/* * Hibernate OGM, Domain model persistence for NoSQL datastores * * 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.ogm.id.impl; import java.util.Properties; import org.hibernate.MappingException; import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.model.relational.Namespace; import org.hibernate.boot.model.relational.QualifiedName; import org.hibernate.boot.model.relational.QualifiedNameParser; import org.hibernate.boot.model.relational.Sequence; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.ogm.dialect.impl.GridDialects; import org.hibernate.ogm.dialect.spi.GridDialect; import org.hibernate.ogm.model.impl.DefaultIdSourceKeyMetadata; import org.hibernate.ogm.model.key.spi.IdSourceKey; import org.hibernate.ogm.model.key.spi.IdSourceKeyMetadata; import org.hibernate.ogm.util.impl.Log; import org.hibernate.ogm.util.impl.LoggerFactory; import org.hibernate.service.ServiceRegistry; import org.hibernate.type.Type; /** * A JPA sequence-based identifier generator (inspired by {@link SequenceStyleGenerator}. * <p> * This identifier generator is also used for JPA auto identifier generation. {@link OgmTableGenerator} is used as * fall-back, if the current datastore does not support sequences. * <table> * <caption>Configuration parameters</caption> * <tr> * <td><b>NAME</b></td> * <td><b>DESCRIPTION</b></td> * </tr> * <tr> * <td>{@link org.hibernate.id.enhanced.SequenceStyleGenerator#SEQUENCE_PARAM}</td> * <td>The name of the sequence to use store/retrieve sequence values</td> * </tr> * <tr> * <td>{@link org.hibernate.id.enhanced.SequenceStyleGenerator#INITIAL_PARAM}</td> * <td>The initial value of the sequence</td> * </tr> * <tr> * <td>{@link org.hibernate.id.enhanced.SequenceStyleGenerator#INCREMENT_PARAM}</td> * <td>The increment of the sequence</td> * </tr> * </table> * * @author Nabeel Ali Memon <nabeel@nabeelalimemon.com> * @author Steve Ebersole * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) * @author Gunnar Morling */ public class OgmSequenceGenerator extends OgmGeneratorBase implements ExportableProducer { private static final Log log = LoggerFactory.make(); private Type type; private Properties params; private QualifiedName logicalQualifiedSequenceName; private String sequenceName; private IdSourceKeyMetadata generatorKeyMetadata; private IdSourceKeyAndKeyMetadataProvider delegate; public OgmSequenceGenerator() { } @Override public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException { super.configure( type, params, serviceRegistry ); JdbcEnvironment jdbcEnvironment = serviceRegistry.getService( JdbcEnvironment.class ); this.type = type; this.params = params; logicalQualifiedSequenceName = determineSequenceName( params, jdbcEnvironment ); sequenceName = jdbcEnvironment.getQualifiedObjectNameFormatter().format( logicalQualifiedSequenceName, jdbcEnvironment.getDialect() ); generatorKeyMetadata = DefaultIdSourceKeyMetadata.forSequence( sequenceName ); delegate = getDelegate( serviceRegistry ); } @Override public IdSourceKeyMetadata getGeneratorKeyMetadata() { return delegate.getGeneratorKeyMetadata(); } @Override protected IdSourceKey getGeneratorKey(SessionImplementor session) { return delegate.getGeneratorKey( session ); } /** * NOTE: Copied from SequenceStyleGenerator * * Determine the name of the sequence (or table if this resolves to a physical table) * to use. * <p> * Called during {@link #configure configuration}. * * @param params The params supplied in the generator config (plus some standard useful extras). * @param jdbcEnv * @return The sequence name */ protected QualifiedName determineSequenceName(Properties params, JdbcEnvironment jdbcEnv) { final String sequencePerEntitySuffix = ConfigurationHelper.getString( SequenceStyleGenerator.CONFIG_SEQUENCE_PER_ENTITY_SUFFIX, params, SequenceStyleGenerator.DEF_SEQUENCE_SUFFIX ); // JPA_ENTITY_NAME value honors <class ... entity-name="..."> (HBM) and @Entity#name (JPA) overrides. final String defaultSequenceName = ConfigurationHelper.getBoolean( SequenceStyleGenerator.CONFIG_PREFER_SEQUENCE_PER_ENTITY, params, false ) ? params.getProperty( JPA_ENTITY_NAME ) + sequencePerEntitySuffix : SequenceStyleGenerator.DEF_SEQUENCE_NAME; final String sequenceName = ConfigurationHelper.getString( SequenceStyleGenerator.SEQUENCE_PARAM, params, defaultSequenceName ); if ( sequenceName.contains( "." ) ) { return QualifiedNameParser.INSTANCE.parse( sequenceName ); } else { final String schemaName = params.getProperty( PersistentIdentifierGenerator.SCHEMA ); if ( schemaName != null ) { log.schemaOptionNotSupportedForSequenceGenerator( schemaName ); } final String catalogName = params.getProperty( PersistentIdentifierGenerator.CATALOG ); if ( catalogName != null ) { log.catalogOptionNotSupportedForSequenceGenerator( catalogName ); } return new QualifiedNameParser.NameParts( null, null, jdbcEnv.getIdentifierHelper().toIdentifier( sequenceName ) ); } } private IdSourceKeyAndKeyMetadataProvider getDelegate(ServiceRegistry serviceRegistry) { GridDialect gridDialect = super.getGridDialect(); if ( gridDialect.supportsSequences() ) { return new SequenceKeyAndMetadataProvider( generatorKeyMetadata ); } else { log.dialectDoesNotSupportSequences( GridDialects.getWrappedDialect( gridDialect ), (String) params.get( SequenceStyleGenerator.JPA_ENTITY_NAME ) ); OgmTableGenerator tableGenerator = new OgmTableGenerator(); Properties newParams = new Properties(); newParams.putAll( params ); newParams.put( OgmTableGenerator.SEGMENT_VALUE_PARAM, sequenceName ); tableGenerator.configure( type, newParams, serviceRegistry ); return new TableKeyAndMetadataProvider( tableGenerator ); } } /** * Provides a uniform way for handling the actual sequence case and the case of delegation to the table generator. */ private interface IdSourceKeyAndKeyMetadataProvider { IdSourceKeyMetadata getGeneratorKeyMetadata(); IdSourceKey getGeneratorKey(SessionImplementor session); } private static class TableKeyAndMetadataProvider implements IdSourceKeyAndKeyMetadataProvider { private final OgmTableGenerator delegate; public TableKeyAndMetadataProvider(OgmTableGenerator delegate) { this.delegate = delegate; } @Override public IdSourceKeyMetadata getGeneratorKeyMetadata() { return delegate.getGeneratorKeyMetadata(); } @Override public IdSourceKey getGeneratorKey(SessionImplementor session) { return delegate.getGeneratorKey( session ); } } private static class SequenceKeyAndMetadataProvider implements IdSourceKeyAndKeyMetadataProvider { private final IdSourceKey idSourceKey; private SequenceKeyAndMetadataProvider(IdSourceKeyMetadata idSourceKeyMetadata) { idSourceKey = IdSourceKey.forSequence( idSourceKeyMetadata ); } @Override public IdSourceKeyMetadata getGeneratorKeyMetadata() { return idSourceKey.getMetadata(); } @Override public IdSourceKey getGeneratorKey(SessionImplementor session) { return idSourceKey; } } @Override public void registerExportables(Database database) { final Namespace namespace = database.locateNamespace( logicalQualifiedSequenceName.getCatalogName(), logicalQualifiedSequenceName.getSchemaName() ); Sequence sequence = namespace.locateSequence( logicalQualifiedSequenceName.getObjectName() ); if ( sequence != null ) { sequence.validate( getInitialValue(), getIncrementSize() ); } else { sequence = namespace.createSequence( logicalQualifiedSequenceName.getObjectName(), getInitialValue(), getIncrementSize() ); } } }