HsqlMaxValueIncrementer.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 5k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 the original author or authors.
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */ 
  16. package org.springframework.jdbc.support.incrementer;
  17. import java.sql.Connection;
  18. import java.sql.ResultSet;
  19. import java.sql.SQLException;
  20. import java.sql.Statement;
  21. import javax.sql.DataSource;
  22. import org.springframework.dao.DataAccessException;
  23. import org.springframework.dao.DataAccessResourceFailureException;
  24. import org.springframework.jdbc.datasource.DataSourceUtils;
  25. import org.springframework.jdbc.support.JdbcUtils;
  26. /**
  27.  * Class to increment maximum value of a given HSQL table with the equivalent
  28.  * of an auto-increment column. Note: If you use this class, your HSQL key
  29.  * column should <i>NOT</i> be auto-increment, as the sequence table does the job.
  30.  *
  31.  * <p>The sequence is kept in a table. There should be one sequence table per
  32.  * table that needs an auto-generated key.
  33.  *
  34.  * <p>Example:
  35.  *
  36.  * <pre>
  37.  * create table tab (id int not null primary key, text varchar(100));
  38.  * create table tab_sequence (value identity);
  39.  * insert into tab_sequence values(0);</pre>
  40.  *
  41.  * If cacheSize is set, the intermediate values are served without querying the
  42.  * database. If the server or your application is stopped or crashes or a transaction
  43.  * is rolled back, the unused values will never be served. The maximum hole size in
  44.  * numbering is consequently the value of cacheSize.
  45.  *
  46.  * @author Isabelle Muszynski
  47.  * @author Jean-Pierre Pawlak
  48.  * @author Thomas Risberg
  49.  */
  50. public class HsqlMaxValueIncrementer extends AbstractDataFieldMaxValueIncrementer {
  51. /** The name of the column for this sequence */
  52. private String columnName;
  53. /** The number of keys buffered in a cache */
  54. private int cacheSize = 1;
  55. private long[] valueCache = null;
  56. /** The next id to serve from the value cache */
  57. private int nextValueIndex = -1;
  58. /**
  59.  * Default constructor.
  60.  **/
  61. public HsqlMaxValueIncrementer() {
  62. }
  63. /**
  64.  * Convenience constructor.
  65.  * @param ds the DataSource to use
  66.  * @param incrementerName the name of the sequence/table to use
  67.  * @param columnName the name of the column in the sequence table to use
  68.  **/
  69. public HsqlMaxValueIncrementer(DataSource ds, String incrementerName, String columnName) {
  70. setDataSource(ds);
  71. setIncrementerName(incrementerName);
  72. this.columnName = columnName;
  73. afterPropertiesSet();
  74. }
  75. /**
  76.  * Set the name of the column in the sequence table.
  77.  */
  78. public void setColumnName(String columnName) {
  79. this.columnName = columnName;
  80. }
  81. /**
  82.  * Return the name of the column in the sequence table.
  83.  */
  84. public String getColumnName() {
  85. return this.columnName;
  86. }
  87. /**
  88.  * Set the number of buffered keys.
  89.  */
  90. public void setCacheSize(int cacheSize) {
  91. this.cacheSize = cacheSize;
  92. }
  93. /**
  94.  * Return the number of buffered keys.
  95.  */
  96. public int getCacheSize() {
  97. return this.cacheSize;
  98. }
  99. public void afterPropertiesSet() {
  100. super.afterPropertiesSet();
  101. if (this.columnName == null) {
  102. throw new IllegalArgumentException("columnName is required");
  103. }
  104. }
  105. protected synchronized long getNextKey() throws DataAccessException {
  106. if (this.nextValueIndex < 0 || this.nextValueIndex >= getCacheSize()) {
  107. /*
  108. * Need to use straight JDBC code because we need to make sure that the insert and select
  109. * are performed on the same connection (otherwise we can't be sure that last_insert_id()
  110. * returned the correct value)
  111. */
  112. Connection con = DataSourceUtils.getConnection(getDataSource());
  113. Statement stmt = null;
  114. try {
  115. stmt = con.createStatement();
  116. DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
  117. this.valueCache = new long[getCacheSize()];
  118. this.nextValueIndex = 0;
  119. for (int i = 0; i < getCacheSize(); i++) {
  120. stmt.executeUpdate("insert into " + getIncrementerName() + " values(null)");
  121. ResultSet rs = stmt.executeQuery("select max(identity()) from " + getIncrementerName());
  122. try {
  123. if (!rs.next()) {
  124. throw new DataAccessResourceFailureException("identity() failed after executing an update");
  125. }
  126. this.valueCache[i] = rs.getLong(1);
  127. }
  128. finally {
  129. JdbcUtils.closeResultSet(rs);
  130. }
  131. }
  132. long maxValue = this.valueCache[(this.valueCache.length - 1)];
  133. stmt.executeUpdate("delete from " + getIncrementerName() + " where " + this.columnName + " < " + maxValue);
  134. }
  135. catch (SQLException ex) {
  136. throw new DataAccessResourceFailureException("Could not obtain identity()", ex);
  137. }
  138. finally {
  139. JdbcUtils.closeStatement(stmt);
  140. DataSourceUtils.closeConnectionIfNecessary(con, getDataSource());
  141. }
  142. }
  143. return this.valueCache[this.nextValueIndex++];
  144. }
  145. }