MySQLMaxValueIncrementer supports "useNewConnection" mode for INNODB tables
Issue: SPR-15107
This commit is contained in:
parent
5982f732b4
commit
cc53d597c0
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -33,14 +33,14 @@ import org.springframework.jdbc.support.JdbcUtils;
|
|||
* key column should <i>NOT</i> be auto-increment, as the sequence table does the job.
|
||||
*
|
||||
* <p>The sequence is kept in a table; there should be one sequence table per
|
||||
* table that needs an auto-generated key. The table type of the sequence table
|
||||
* should be MyISAM so the sequences are allocated without regard to any
|
||||
* transactions that might be in progress.
|
||||
* table that needs an auto-generated key. The storage engine used by the sequence table
|
||||
* can be MYISAM or INNODB since the sequences are allocated using a separate connection
|
||||
* without being affected by any other transactions that might be in progress.
|
||||
*
|
||||
* <p>Example:
|
||||
*
|
||||
* <pre class="code">create table tab (id int unsigned not null primary key, text varchar(100));
|
||||
* create table tab_sequence (value int not null) type=MYISAM;
|
||||
* create table tab_sequence (value int not null);
|
||||
* insert into tab_sequence values(0);</pre>
|
||||
*
|
||||
* If "cacheSize" is set, the intermediate values are served without querying the
|
||||
|
|
@ -48,6 +48,10 @@ import org.springframework.jdbc.support.JdbcUtils;
|
|||
* is rolled back, the unused values will never be served. The maximum hole size in
|
||||
* numbering is consequently the value of cacheSize.
|
||||
*
|
||||
* <p>It is possible to avoid acquiring a new connection for the incrementer by setting the
|
||||
* "useNewConnection" property to false. In this case you <i>MUST</i> use a non-transactional
|
||||
* storage engine like MYISAM when defining the incrementer table.
|
||||
*
|
||||
* @author Jean-Pierre Pawlak
|
||||
* @author Thomas Risberg
|
||||
* @author Juergen Hoeller
|
||||
|
|
@ -63,6 +67,9 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
|
|||
/** The max id to serve */
|
||||
private long maxId = 0;
|
||||
|
||||
/** Whether or not to use a new connection for the incrementer */
|
||||
private boolean useNewConnection = false;
|
||||
|
||||
|
||||
/**
|
||||
* Default constructor for bean property style usage.
|
||||
|
|
@ -76,7 +83,7 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
|
|||
/**
|
||||
* Convenience constructor.
|
||||
* @param dataSource the DataSource to use
|
||||
* @param incrementerName the name of the sequence/table to use
|
||||
* @param incrementerName the name of the sequence table to use
|
||||
* @param columnName the name of the column in the sequence table to use
|
||||
*/
|
||||
public MySQLMaxValueIncrementer(DataSource dataSource, String incrementerName, String columnName) {
|
||||
|
|
@ -84,23 +91,61 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to use a new connection for the incrementer.
|
||||
* <p>{@code true} is necessary to support transactional storage engines,
|
||||
* using an isolated separate transaction for the increment operation.
|
||||
* {@code false} is sufficient if the storage engine of the sequence table
|
||||
* is non-transactional (like MYISAM), avoiding the effort of acquiring an
|
||||
* extra {@code Connection} for the increment operation.
|
||||
* <p>Default is {@code false} in the Spring Framework 4.3.x line.
|
||||
* @since 4.3.6
|
||||
* @see DataSource#getConnection()
|
||||
*/
|
||||
public void setUseNewConnection(boolean useNewConnection) {
|
||||
this.useNewConnection = useNewConnection;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected synchronized long getNextKey() throws DataAccessException {
|
||||
if (this.maxId == this.nextId) {
|
||||
/*
|
||||
* Need to use straight JDBC code because we need to make sure that the insert and select
|
||||
* are performed on the same connection (otherwise we can't be sure that last_insert_id()
|
||||
* returned the correct value)
|
||||
* If useNewConnection is true, then we obtain a non-managed connection so our modifications
|
||||
* are handled in a separate transaction. If it is false, then we use the current transaction's
|
||||
* connection relying on the use of a non-transactional storage engine like MYISAM for the
|
||||
* incrementer table. We also use straight JDBC code because we need to make sure that the insert
|
||||
* and select are performed on the same connection (otherwise we can't be sure that last_insert_id()
|
||||
* returned the correct value).
|
||||
*/
|
||||
Connection con = DataSourceUtils.getConnection(getDataSource());
|
||||
Connection con = null;
|
||||
Statement stmt = null;
|
||||
boolean mustRestoreAutoCommit = false;
|
||||
try {
|
||||
if (this.useNewConnection) {
|
||||
con = getDataSource().getConnection();
|
||||
if (con.getAutoCommit()) {
|
||||
mustRestoreAutoCommit = true;
|
||||
con.setAutoCommit(false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
con = DataSourceUtils.getConnection(getDataSource());
|
||||
}
|
||||
stmt = con.createStatement();
|
||||
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
|
||||
if (!this.useNewConnection) {
|
||||
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
|
||||
}
|
||||
// Increment the sequence column...
|
||||
String columnName = getColumnName();
|
||||
stmt.executeUpdate("update "+ getIncrementerName() + " set " + columnName +
|
||||
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
|
||||
try {
|
||||
stmt.executeUpdate("update " + getIncrementerName() + " set " + columnName +
|
||||
" = last_insert_id(" + columnName + " + " + getCacheSize() + ")");
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
throw new DataAccessResourceFailureException("Could not increment " + columnName + " for " +
|
||||
getIncrementerName() + " sequence table", ex);
|
||||
}
|
||||
// Retrieve the new max of the sequence column...
|
||||
ResultSet rs = stmt.executeQuery(VALUE_SQL);
|
||||
try {
|
||||
|
|
@ -119,7 +164,24 @@ public class MySQLMaxValueIncrementer extends AbstractColumnMaxValueIncrementer
|
|||
}
|
||||
finally {
|
||||
JdbcUtils.closeStatement(stmt);
|
||||
DataSourceUtils.releaseConnection(con, getDataSource());
|
||||
if (con != null) {
|
||||
if (this.useNewConnection) {
|
||||
try {
|
||||
con.commit();
|
||||
if (mustRestoreAutoCommit) {
|
||||
con.setAutoCommit(true);
|
||||
}
|
||||
}
|
||||
catch (SQLException ignore) {
|
||||
throw new DataAccessResourceFailureException(
|
||||
"Unable to commit new sequence value changes for " + getIncrementerName());
|
||||
}
|
||||
JdbcUtils.closeConnection(con);
|
||||
}
|
||||
else {
|
||||
DataSourceUtils.releaseConnection(con, getDataSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
Loading…
Reference in New Issue