This is class very similar to the IncrementGenerator class. It will fetch the next id ( Max (id) + 1 ) from the DB instead of using an internal counter. I needed this functionality because we're running in a clustered environment, the tables do not have identity columns, and unfortunately i can not change the DB schema.
I suppose this class would not be useful for folks in an environment where there is an extremely large amount of traffic because it would be possible to get Primary Key constraint violations. But, in my case this would be very unlikey.
Anyways, i welcome any and all feedback.
Thanks,
Russ
Code:
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentifierGeneratorFactory;
import org.hibernate.id.IncrementGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;
public class MaxPlusOneIdentifierGenerator implements IdentifierGenerator, Configurable {
private static final Log log = LogFactory.getLog(IncrementGenerator.class);
private String sql;
private Class returnClass;
public synchronized Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return IdentifierGeneratorFactory.createNumber( getNext( session ), returnClass);
}
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
String tableList = params.getProperty("tables");
if (tableList==null) tableList = params.getProperty(PersistentIdentifierGenerator.TABLES);
String[] tables = StringHelper.split(", ", tableList);
String column = params.getProperty("column");
if (column==null) column = params.getProperty(PersistentIdentifierGenerator.PK);
String schema = params.getProperty(PersistentIdentifierGenerator.SCHEMA);
String catalog = params.getProperty(PersistentIdentifierGenerator.CATALOG);
returnClass = type.getReturnedClass();
StringBuffer buf = new StringBuffer();
for ( int i=0; i<tables.length; i++ ) {
if (tables.length>1) {
buf.append("select ").append(column).append(" from ");
}
buf.append( Table.qualify( catalog, schema, tables[i] ) );
if ( i<tables.length-1) buf.append(" union ");
}
if (tables.length>1) {
buf.insert(0, "( ").append(" ) ids_");
column = "ids_." + column;
}
sql = "select max(" + column + ") from " + buf.toString();
}
private long getNext( SessionImplementor session ) {
long next;
log.debug("fetching initial value: " + sql);
try {
PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
try {
ResultSet rs = st.executeQuery();
try {
if ( rs.next() ) {
next = rs.getLong(1) + 1;
if ( rs.wasNull() ) next = 1;
}
else {
next = 1;
}
log.debug("first free id: " + next);
}
finally {
rs.close();
}
}
finally {
session.getBatcher().closeStatement(st);
}
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(
session.getFactory().getSQLExceptionConverter(),
sqle,
"could not fetch initial value for increment generator",
sql
);
}
return next;
}
}