HHH-11131 - Fixed OrderSequenceGenerator to use a custom structure for ordered oracle sequences.
This commit is contained in:
parent
0dcb9b6487
commit
f66031fa53
|
@ -6,25 +6,37 @@
|
|||
*/
|
||||
package org.hibernate.envers.enhanced;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.boot.model.relational.QualifiedName;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.id.enhanced.DatabaseStructure;
|
||||
import org.hibernate.id.enhanced.SequenceStyleGenerator;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Revision number generator has to produce values in ascending order (gaps may occur).
|
||||
* <p/>
|
||||
* This generator is only applicable when {@code USE_REVISION_ENTITY_WITH_NATIVE_ID} is {@code false} in the
|
||||
* bootstrapping configuration properties.
|
||||
*
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class OrderedSequenceGenerator extends SequenceStyleGenerator {
|
||||
@Override
|
||||
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
|
||||
String[] create = super.sqlCreateStrings( dialect );
|
||||
if ( dialect instanceof Oracle8iDialect ) {
|
||||
// Make sure that sequence produces increasing values in Oracle RAC environment.
|
||||
create = StringHelper.suffix( create, " order" );
|
||||
protected DatabaseStructure buildDatabaseStructure(
|
||||
Type type,
|
||||
Properties params,
|
||||
JdbcEnvironment jdbcEnvironment,
|
||||
boolean forceTableUse,
|
||||
QualifiedName sequenceName,
|
||||
int initialValue,
|
||||
int incrementSize) {
|
||||
final boolean useSequence = jdbcEnvironment.getDialect().supportsSequences() && !forceTableUse;
|
||||
if ( useSequence ) {
|
||||
return new OrderedSequenceStructure( jdbcEnvironment, sequenceName, initialValue, incrementSize, type.getReturnedClass() );
|
||||
}
|
||||
return create;
|
||||
return super.buildDatabaseStructure( type, params, jdbcEnvironment, forceTableUse, sequenceName, initialValue, incrementSize );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.envers.enhanced;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
|
||||
import org.hibernate.boot.model.relational.Database;
|
||||
import org.hibernate.boot.model.relational.Namespace;
|
||||
import org.hibernate.boot.model.relational.QualifiedName;
|
||||
import org.hibernate.boot.model.relational.QualifiedSequenceName;
|
||||
import org.hibernate.boot.model.relational.Sequence;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.Oracle8iDialect;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.id.IdentifierGeneratorHelper;
|
||||
import org.hibernate.id.IntegralDataTypeHolder;
|
||||
import org.hibernate.id.enhanced.AccessCallback;
|
||||
import org.hibernate.id.enhanced.DatabaseStructure;
|
||||
import org.hibernate.id.enhanced.Optimizer;
|
||||
import org.hibernate.id.enhanced.SequenceStructure;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class OrderedSequenceStructure implements DatabaseStructure {
|
||||
|
||||
// @todo: a fair bit of duplication from SequenceStructure - needs refactor
|
||||
|
||||
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
|
||||
CoreMessageLogger.class,
|
||||
SequenceStructure.class.getName()
|
||||
);
|
||||
|
||||
private final QualifiedName logicalQualifiedSequenceName;
|
||||
private final int initialValue;
|
||||
private final int incrementSize;
|
||||
private final Class numberType;
|
||||
|
||||
private String sequenceName;
|
||||
private String sql;
|
||||
private boolean applyIncrementSizeToSourceValues;
|
||||
private int accessCounter;
|
||||
private AuxiliaryDatabaseObject sequenceObject;
|
||||
|
||||
public OrderedSequenceStructure(
|
||||
JdbcEnvironment jdbcEnvironment,
|
||||
QualifiedName qualifiedSequenceName,
|
||||
int initialValue,
|
||||
int incrementSize,
|
||||
Class numberType) {
|
||||
this.logicalQualifiedSequenceName = qualifiedSequenceName;
|
||||
|
||||
this.initialValue = initialValue;
|
||||
this.incrementSize = incrementSize;
|
||||
this.numberType = numberType;
|
||||
this.sequenceObject = new OrderedSequence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return sequenceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIncrementSize() {
|
||||
return incrementSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTimesAccessed() {
|
||||
return accessCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInitialValue() {
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessCallback buildCallback(final SharedSessionContractImplementor session) {
|
||||
if ( sql == null ) {
|
||||
throw new AssertionFailure( "SequenceStyleGenerator's SequenceStructure was not properly initialized" );
|
||||
}
|
||||
|
||||
return new AccessCallback() {
|
||||
@Override
|
||||
public IntegralDataTypeHolder getNextValue() {
|
||||
accessCounter++;
|
||||
try {
|
||||
final PreparedStatement st = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql );
|
||||
try {
|
||||
final ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st );
|
||||
try {
|
||||
rs.next();
|
||||
final IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
|
||||
value.initialize( rs, 1 );
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf( "Sequence value obtained: %s", value.makeValue() );
|
||||
}
|
||||
return value;
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( rs, st );
|
||||
}
|
||||
catch( Throwable ignore ) {
|
||||
// intentionally empty
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st );
|
||||
session.getJdbcCoordinator().afterStatementExecution();
|
||||
}
|
||||
|
||||
}
|
||||
catch ( SQLException sqle) {
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not get next sequence value",
|
||||
sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTenantIdentifier() {
|
||||
return session.getTenantIdentifier();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare(Optimizer optimizer) {
|
||||
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerExportables(Database database) {
|
||||
final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment();
|
||||
final Dialect dialect = jdbcEnvironment.getDialect();
|
||||
|
||||
// construct the next value sql
|
||||
this.sequenceName = jdbcEnvironment.getQualifiedObjectNameFormatter().format(
|
||||
logicalQualifiedSequenceName,
|
||||
dialect
|
||||
);
|
||||
this.sql = jdbcEnvironment.getDialect().getSequenceNextValString( sequenceName );
|
||||
|
||||
// add auxiliary object
|
||||
database.addAuxiliaryDatabaseObject( sequenceObject );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
|
||||
// delegate to auxiliary object
|
||||
return sequenceObject.sqlCreateStrings( dialect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
|
||||
// delegate to auxiliary object
|
||||
return sequenceObject.sqlDropStrings( dialect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPhysicalSequence() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private class OrderedSequence implements AuxiliaryDatabaseObject {
|
||||
@Override
|
||||
public String getExportIdentifier() {
|
||||
return logicalQualifiedSequenceName.getObjectName().getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appliesToDialect(Dialect dialect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beforeTablesOnCreation() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] sqlCreateStrings(Dialect dialect) {
|
||||
final int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
|
||||
|
||||
final String[] createStrings = dialect.getCreateSequenceStrings(
|
||||
sequenceName,
|
||||
initialValue,
|
||||
sourceIncrementSize
|
||||
);
|
||||
|
||||
if ( dialect instanceof Oracle8iDialect ) {
|
||||
for ( int i = 0; i < createStrings.length; ++i ) {
|
||||
createStrings[ i ] = createStrings[ i ] + " ORDER";
|
||||
}
|
||||
}
|
||||
|
||||
return createStrings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] sqlDropStrings(Dialect dialect) {
|
||||
return dialect.getDropSequenceStrings( sequenceName );
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue