package message.datasource.key.generic;
import message.datasource.key.AbstractMaxValueIncrementer;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
/**
* MySQL主键自增长策略
*
* @author sunhao(sunhao.java@gmail.com)
* @version V1.0, 2012-4-11 上午09:15:08
* @see org.springframework.jdbc.support.incrementer.MySQLMaxValueIncrementer
*/
public class MySQLMaxValueIncrementer extends AbstractMaxValueIncrementer {
/** The SQL string for retrieving the new sequence value */
private static final String VALUE_SQL = "select last_insert_id()";
/** The next id to serve */
private long nextId = 0;
/** The max id to serve */
private long maxId = 0;
/** The number of keys buffered in a cache */
private int cacheSize = 1;
/**cache for each sequence's next id**/
private Map<String, Long> NEXT_ID_CACHE = new HashMap<String, Long>();
/**cache for each sequence's max id**/
private Map<String, Long> MAX_ID_CACHE = new HashMap<String, Long>();
protected synchronized long getNextKey(String name) throws DataAccessException {
if(NEXT_ID_CACHE.get(name) != null)
this.nextId = NEXT_ID_CACHE.get(name);
if(MAX_ID_CACHE.get(name) != null)
this.maxId = MAX_ID_CACHE.get(name);
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)
*/
Connection con = DataSourceUtils.getConnection(getDataSource());
Statement stmt = null;
try {
stmt = con.createStatement();
DataSourceUtils.applyTransactionTimeout(stmt, getDataSource());
String updateSql = "update "+ name + " set " + name + " = last_insert_id(" + name + " + " + getCacheSize() + ")";
try {
// Increment the sequence column...
stmt.executeUpdate(updateSql);
} catch (SQLException e) {
//发生异常,即不存在这张表
//1.执行新建这张表的语句
StringBuffer createSql = new StringBuffer();
createSql.append("create table ").append(name).append(" ( ");
createSql.append(name).append(" bigint(12) default null ");
createSql.append(" ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ");
stmt.execute(createSql.toString());
//2.初始化这张表的数据(将name的值置为0)
String initDataSql = "insert into " + name + " values(0)";
stmt.executeUpdate(initDataSql);
//3.Increment the sequence column...
stmt.executeUpdate(updateSql);
}
// Retrieve the new max of the sequence column...
ResultSet rs = stmt.executeQuery(VALUE_SQL);
try {
if (!rs.next()) {
throw new DataAccessResourceFailureException("last_insert_id() failed after executing an update");
}
this.maxId = rs.getLong(1);
//更新缓存
MAX_ID_CACHE.put(name, this.maxId);
}
finally {
JdbcUtils.closeResultSet(rs);
}
this.nextId = this.maxId - getCacheSize() + 1;
//更新缓存
NEXT_ID_CACHE.put(name, this.nextId);
} catch (SQLException ex) {
throw new DataAccessResourceFailureException("Could not obtain last_insert_id()", ex);
} finally {
JdbcUtils.closeStatement(stmt);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
else {
this.nextId++;
//更新缓存
NEXT_ID_CACHE.put(name, this.nextId);
}
long result = this.nextId;
//初始化nextId和maxId
this.nextId = 0;
this.maxId = 0;
return result;
}
/**
* Return the number of buffered keys.
*/
public int getCacheSize() {
return cacheSize;
}
/**
* Set the number of buffered keys.
*/
public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
}
}