Skip to content

Commit cc53d59

Browse files
committed
MySQLMaxValueIncrementer supports "useNewConnection" mode for INNODB tables
Issue: SPR-15107
1 parent 5982f73 commit cc53d59

File tree

1 file changed

+76
-14
lines changed

1 file changed

+76
-14
lines changed

spring-jdbc/src/main/java/org/springframework/jdbc/support/incrementer/MySQLMaxValueIncrementer.java

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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.
@@ -33,21 +33,25 @@
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.
5050
*
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.
54+
*
5155
* @author Jean-Pierre Pawlak
5256
* @author Thomas Risberg
5357
* @author Juergen Hoeller
@@ -63,6 +67,9 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
6367
/** The max id to serve */
6468
private long maxId = 0;
6569

70+
/** Whether or not to use a new connection for the incrementer */
71+
private boolean useNewConnection = false;
72+
6673

6774
/**
6875
* Default constructor for bean property style usage.
@@ -76,31 +83,69 @@ public MySQLMaxValueIncrementer() {
7683
/**
7784
* Convenience constructor.
7885
* @param dataSource the DataSource to use
79-
* @param incrementerName the name of the sequence/table to use
86+
* @param incrementerName the name of the sequence table to use
8087
* @param columnName the name of the column in the sequence table to use
8188
*/
8289
public MySQLMaxValueIncrementer(DataSource dataSource, String incrementerName, String columnName) {
8390
super(dataSource, incrementerName, columnName);
8491
}
8592

8693

94+
/**
95+
* Set whether to use a new connection for the incrementer.
96+
* <p>{@code true} is necessary to support transactional storage engines,
97+
* using an isolated separate transaction for the increment operation.
98+
* {@code false} is sufficient if the storage engine of the sequence table
99+
* is non-transactional (like MYISAM), avoiding the effort of acquiring an
100+
* extra {@code Connection} for the increment operation.
101+
* <p>Default is {@code false} in the Spring Framework 4.3.x line.
102+
* @since 4.3.6
103+
* @see DataSource#getConnection()
104+
*/
105+
public void setUseNewConnection(boolean useNewConnection) {
106+
this.useNewConnection = useNewConnection;
107+
}
108+
109+
87110
@Override
88111
protected synchronized long getNextKey() throws DataAccessException {
89112
if (this.maxId == this.nextId) {
90113
/*
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)
114+
* If useNewConnection is true, then we obtain a non-managed connection so our modifications
115+
* are handled in a separate transaction. If it is false, then we use the current transaction's
116+
* connection relying on the use of a non-transactional storage engine like MYISAM for the
117+
* incrementer table. We also use straight JDBC code because we need to make sure that the insert
118+
* and select are performed on the same connection (otherwise we can't be sure that last_insert_id()
119+
* returned the correct value).
94120
*/
95-
Connection con = DataSourceUtils.getConnection(getDataSource());
121+
Connection con = null;
96122
Statement stmt = null;
123+
boolean mustRestoreAutoCommit = false;
97124
try {
125+
if (this.useNewConnection) {
126+
con = getDataSource().getConnection();
127+
if (con.getAutoCommit()) {
128+
mustRestoreAutoCommit = true;
129+
con.setAutoCommit(false);
130+
}
131+
}
132+
else {
133+
con = DataSourceUtils.getConnection(getDataSource());
134+
}
98135
stmt = con.createStatement();
99-
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
136+
if (!this.useNewConnection) {
137+
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
138+
}
100139
// Increment the sequence column...
101140
String columnName = getColumnName();
102-
stmt.executeUpdate("update "+ getIncrementerName() + " set " + columnName +
103-
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
141+
try {
142+
stmt.executeUpdate("update " + getIncrementerName() + " set " + columnName +
143+
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
144+
}
145+
catch (SQLException ex) {
146+
throw new DataAccessResourceFailureException("Could not increment " + columnName + " for " +
147+
getIncrementerName() + " sequence table", ex);
148+
}
104149
// Retrieve the new max of the sequence column...
105150
ResultSet rs = stmt.executeQuery(VALUE_SQL);
106151
try {
@@ -119,7 +164,24 @@ protected synchronized long getNextKey() throws DataAccessException {
119164
}
120165
finally {
121166
JdbcUtils.closeStatement(stmt);
122-
DataSourceUtils.releaseConnection(con, getDataSource());
167+
if (con != null) {
168+
if (this.useNewConnection) {
169+
try {
170+
con.commit();
171+
if (mustRestoreAutoCommit) {
172+
con.setAutoCommit(true);
173+
}
174+
}
175+
catch (SQLException ignore) {
176+
throw new DataAccessResourceFailureException(
177+
"Unable to commit new sequence value changes for " + getIncrementerName());
178+
}
179+
JdbcUtils.closeConnection(con);
180+
}
181+
else {
182+
DataSourceUtils.releaseConnection(con, getDataSource());
183+
}
184+
}
123185
}
124186
}
125187
else {

0 commit comments

Comments
 (0)