KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > jdbc > support > incrementer > MySQLMaxValueIncrementer


1 /*
2  * Copyright 2002-2005 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
17 package org.springframework.jdbc.support.incrementer;
18
19 import java.sql.Connection JavaDoc;
20 import java.sql.ResultSet JavaDoc;
21 import java.sql.SQLException JavaDoc;
22 import java.sql.Statement JavaDoc;
23
24 import javax.sql.DataSource JavaDoc;
25
26 import org.springframework.dao.DataAccessException;
27 import org.springframework.dao.DataAccessResourceFailureException;
28 import org.springframework.jdbc.datasource.DataSourceUtils;
29 import org.springframework.jdbc.support.JdbcUtils;
30
31 /**
32  * Class to increment maximum value of a given MySQL table with the equivalent
33  * of an auto-increment column. Note: If you use this class, your MySQL key
34  * column should <i>NOT</i> be auto-increment, as the sequence table does the job.
35  *
36  * <p>The sequence is kept in a table; there should be one sequence table per
37  * table that needs an auto-generated key. The table type of the sequence table
38  * should be MyISAM so the sequences are allocated without regard to any
39  * transactions that might be in progress.
40  *
41  * <p>Example:
42  *
43  * <pre class="code">create table tab (id int unsigned not null primary key, text varchar(100));
44  * create table tab_sequence (value int not null) type=MYISAM;
45  * insert into tab_sequence values(0);</pre>
46  *
47  * If cacheSize is set, the intermediate values are served without querying the
48  * database. If the server or your application is stopped or crashes or a transaction
49  * is rolled back, the unused values will never be served. The maximum hole size in
50  * numbering is consequently the value of cacheSize.
51  *
52  * @author Isabelle Muszynski
53  * @author Jean-Pierre Pawlak
54  * @author Thomas Risberg
55  */

56
57 public class MySQLMaxValueIncrementer extends AbstractDataFieldMaxValueIncrementer {
58
59     /** The Sql string for retrieving the new sequence value */
60     private static final String JavaDoc VALUE_SQL = "select last_insert_id()";
61
62     /** The name of the column for this sequence */
63     private String JavaDoc columnName;
64
65     /** The number of keys buffered in a cache */
66     private int cacheSize = 1;
67
68     /** The next id to serve */
69     private long nextId = 0;
70
71     /** The max id to serve */
72     private long maxId = 0;
73
74
75     /**
76      * Default constructor.
77      **/

78     public MySQLMaxValueIncrementer() {
79     }
80
81     /**
82      * Convenience constructor.
83      * @param ds the DataSource to use
84      * @param incrementerName the name of the sequence/table to use
85      * @param columnName the name of the column in the sequence table to use
86      **/

87     public MySQLMaxValueIncrementer(DataSource JavaDoc ds, String JavaDoc incrementerName, String JavaDoc columnName) {
88         setDataSource(ds);
89         setIncrementerName(incrementerName);
90         this.columnName = columnName;
91         afterPropertiesSet();
92     }
93
94     /**
95      * Set the name of the column in the sequence table.
96      */

97     public void setColumnName(String JavaDoc columnName) {
98         this.columnName = columnName;
99     }
100
101     /**
102      * Return the name of the column in the sequence table.
103      */

104     public String JavaDoc getColumnName() {
105         return this.columnName;
106     }
107
108     /**
109      * Set the number of buffered keys.
110      */

111     public void setCacheSize(int cacheSize) {
112         this.cacheSize = cacheSize;
113     }
114
115     /**
116      * Return the number of buffered keys.
117      */

118     public int getCacheSize() {
119         return this.cacheSize;
120     }
121
122     public void afterPropertiesSet() {
123         super.afterPropertiesSet();
124         if (this.columnName == null) {
125             throw new IllegalArgumentException JavaDoc("columnName is required");
126         }
127     }
128
129
130     protected synchronized long getNextKey() throws DataAccessException {
131         if (this.maxId == this.nextId) {
132             /*
133             * Need to use straight JDBC code because we need to make sure that the insert and select
134             * are performed on the same connection (otherwise we can't be sure that last_insert_id()
135             * returned the correct value)
136             */

137             Connection JavaDoc con = DataSourceUtils.getConnection(getDataSource());
138             Statement JavaDoc stmt = null;
139             try {
140                 stmt = con.createStatement();
141                 DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
142                 // increment the sequence column
143
stmt.executeUpdate("update "+ getIncrementerName() + " set " + this.columnName +
144                                                      " = last_insert_id(" + this.columnName + " + " + getCacheSize() + ")");
145                 // retrieve the new max of the sequence column
146
ResultSet JavaDoc rs = stmt.executeQuery(VALUE_SQL);
147                 try {
148                     if (!rs.next()) {
149                         throw new DataAccessResourceFailureException("last_insert_id() failed after executing an update");
150                     }
151                     this.maxId = rs.getLong(1);
152                 }
153                 finally {
154                     JdbcUtils.closeResultSet(rs);
155                 }
156                 this.nextId = this.maxId - getCacheSize() + 1;
157             }
158             catch (SQLException JavaDoc ex) {
159                 throw new DataAccessResourceFailureException("Could not obtain last_insert_id()", ex);
160             }
161             finally {
162                 JdbcUtils.closeStatement(stmt);
163                 DataSourceUtils.releaseConnection(con, getDataSource());
164             }
165         }
166         else {
167             this.nextId++;
168         }
169         return this.nextId;
170     }
171
172 }
173
Popular Tags