11/*
2- * Copyright 2002-2008 the original author or authors.
2+ * Copyright 2002-2017 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
3333 * key column should <i>NOT</i> be auto-increment, as the sequence table does the job.
3434 *
3535 * <p>The sequence is kept in a table; there should be one sequence table per
36- * table that needs an auto-generated key. The table type of the sequence table
37- * should be MyISAM so the sequences are allocated without regard to any
38- * transactions that might be in progress.
36+ * table that needs an auto-generated key. The storage engine used by the sequence table
37+ * can be MYISAM or INNODB since the sequences are allocated using a separate connection
38+ * without being affected by any other transactions that might be in progress.
3939 *
4040 * <p>Example:
4141 *
4242 * <pre class="code">create table tab (id int unsigned not null primary key, text varchar(100));
43- * create table tab_sequence (value int not null) type=MYISAM ;
43+ * create table tab_sequence (value int not null);
4444 * insert into tab_sequence values(0);</pre>
4545 *
4646 * If "cacheSize" is set, the intermediate values are served without querying the
4747 * database. If the server or your application is stopped or crashes or a transaction
4848 * is rolled back, the unused values will never be served. The maximum hole size in
4949 * numbering is consequently the value of cacheSize.
50+ *
51+ * <p>It is possible to avoid acquiring a new connection for the incrementer by setting the
52+ * "useNewConnection" property to false. In this case you <i>MUST</i> use a non-transactional
53+ * storage engine like MYISAM when defining the incrementer table.
5054 *
5155 * @author Jean-Pierre Pawlak
5256 * @author Thomas Risberg
@@ -63,6 +67,14 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
6367 /** The max id to serve */
6468 private long maxId = 0 ;
6569
70+ /**
71+ * Whether or not to use a new connection for the incrementer. Defaults to true
72+ * in order to support transactional storage engines. Set this to false if the storage engine
73+ * for the incrementer table is non-transactional like MYISAM and you prefer to not acquire
74+ * an additional database connection
75+ */
76+ private boolean useNewConnection = true ;
77+
6678
6779 /**
6880 * Default constructor for bean property style usage.
@@ -83,24 +95,73 @@ public MySQLMaxValueIncrementer(DataSource dataSource, String incrementerName, S
8395 super (dataSource , incrementerName , columnName );
8496 }
8597
98+ /**
99+ * Convenience constructor for setting whether to use a new connection for the incrementer.
100+ * @param dataSource the DataSource to use
101+ * @param incrementerName the name of the sequence/table to use
102+ * @param columnName the name of the column in the sequence table to use
103+ * @param useNewConnection whether to use a new connection for the incrementer
104+ */
105+ public MySQLMaxValueIncrementer (DataSource dataSource , String incrementerName , String columnName ,
106+ boolean useNewConnection ) {
107+ super (dataSource , incrementerName , columnName );
108+ this .useNewConnection = useNewConnection ;
109+ }
110+
111+
112+ /**
113+ * Return whether to use a new connection for the incrementer.
114+ */
115+ public boolean isUseNewConnection () {
116+ return useNewConnection ;
117+ }
118+
119+ /**
120+ * Set whether to use a new connection for the incrementer.
121+ */
122+ public void setUseNewConnection (boolean useNewConnection ) {
123+ this .useNewConnection = useNewConnection ;
124+ }
86125
87126 @ Override
88127 protected synchronized long getNextKey () throws DataAccessException {
89128 if (this .maxId == this .nextId ) {
90129 /*
91- * Need to use straight JDBC code because we need to make sure that the insert and select
92- * are performed on the same connection (otherwise we can't be sure that last_insert_id()
93- * returned the correct value)
130+ * If useNewConnection is true, then we obtain a non-managed connection so our modifications
131+ * are handled in a separate transaction. If it is false, then we use the current transaction's
132+ * connection relying on the use of a non-transactional storage engine like MYISAM for the
133+ * incrementer table. We also use straight JDBC code because we need to make sure that the insert
134+ * and select are performed on the same connection (otherwise we can't be sure that last_insert_id()
135+ * returned the correct value).
94136 */
95- Connection con = DataSourceUtils . getConnection ( getDataSource ()) ;
137+ Connection con = null ;
96138 Statement stmt = null ;
139+ boolean mustRestoreAutoCommit = false ;
97140 try {
141+ if (useNewConnection ) {
142+ con = getDataSource ().getConnection ();
143+ if (con .getAutoCommit ()) {
144+ mustRestoreAutoCommit = true ;
145+ con .setAutoCommit (false );
146+ }
147+ }
148+ else {
149+ con = DataSourceUtils .getConnection (getDataSource ());
150+ }
98151 stmt = con .createStatement ();
99- DataSourceUtils .applyTransactionTimeout (stmt , getDataSource ());
152+ if (!useNewConnection ) {
153+ DataSourceUtils .applyTransactionTimeout (stmt , getDataSource ());
154+ }
100155 // Increment the sequence column...
101156 String columnName = getColumnName ();
102- stmt .executeUpdate ("update " + getIncrementerName () + " set " + columnName +
103- " = last_insert_id(" + columnName + " + " + getCacheSize () + ")" );
157+ try {
158+ stmt .executeUpdate ("update " + getIncrementerName () + " set " + columnName +
159+ " = last_insert_id(" + columnName + " + " + getCacheSize () + ")" );
160+ }
161+ catch (SQLException ex ) {
162+ throw new DataAccessResourceFailureException ("Could not increment " + columnName + " for " +
163+ getIncrementerName () + " sequence table" , ex );
164+ }
104165 // Retrieve the new max of the sequence column...
105166 ResultSet rs = stmt .executeQuery (VALUE_SQL );
106167 try {
@@ -119,7 +180,24 @@ protected synchronized long getNextKey() throws DataAccessException {
119180 }
120181 finally {
121182 JdbcUtils .closeStatement (stmt );
122- DataSourceUtils .releaseConnection (con , getDataSource ());
183+ if (useNewConnection ) {
184+ try {
185+ con .commit ();
186+ if (mustRestoreAutoCommit ) {
187+ con .setAutoCommit (true );
188+ }
189+ }
190+ catch (SQLException ignore ) {
191+ throw new DataAccessResourceFailureException (
192+ "Unable to commit new sequence value changes for " + getIncrementerName ());
193+ }
194+ try {
195+ con .close ();
196+ } catch (SQLException ignore ) {}
197+ }
198+ else {
199+ DataSourceUtils .releaseConnection (con , getDataSource ());
200+ }
123201 }
124202 }
125203 else {
0 commit comments