Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
357 views
in Technique[技术] by (71.8m points)

java - Hibernate not recognizing sequence increment correctly

We are using Spring Boot 2.2.1 with Hibernate 5.4.8.Final, dialect org.hibernate.dialect.Oracle12cDialect, against an Oracle 19.3.0.0 database with Open JDK 11.

Randomly and only for some developers, Hibernate fails to initialize with this exception:

org.hibernate.MappingException: The increment size of the [SEQ_THE_SEQUENCE] sequence is set to [10] in the entity mapping while the associated database sequence increment size is [1].
    at org.hibernate.id.enhanced.SequenceStyleGenerator.configure(SequenceStyleGenerator.java:257)
    at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.createIdentifierGenerator(DefaultIdentifierGeneratorFactory.java:118)
    ... 42 common frames omitted
Wrapped by: org.hibernate.MappingException: Could not instantiate id generator [entity-name=com.[...].TheEntity]
    at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.createIdentifierGenerator(DefaultIdentifierGeneratorFactory.java:124)
    at org.hibernate.mapping.SimpleValue.createIdentifierGenerator(SimpleValue.java:344)
    at org.hibernate.internal.SessionFactoryImpl.lambda$new$1(SessionFactoryImpl.java:292)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
    at java.base/java.util.HashMap$ValueSpliterator.forEachRemaining(HashMap.java:1675)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:497)
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:291)
    at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462)
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1249)
    at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:391)
    ... 25 common frames omitted
Wrapped by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not instantiate id generator [entity-name=com.[...].TheEntity]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:403)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:378)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1862)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1799)
    ... 21 common frames omitted
Wrapped by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not instantiate id generator [entity-name=com.[....]TheEntity]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1803)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:595)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1108)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:868)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
    at ....main(....java:28)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

The sequence is created by the following statement (we are using flyway, so it is the same in all environments):

create sequence SEQ_THE_SEQUENCE
  start with 1
  increment by 10;

The Entity with abstract parent:

@MappedSuperclass
public abstract class AbstractParentEntity implements JpaBaseEntity {

    public static final String SEQ_GENERATOR_NAME = "SEQ_GENERAL";

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = SEQ_GENERATOR_NAME)
    @Column(name = "[ID]", unique = true, nullable = false, insertable = false, updatable = false)
    protected Long id;
...
}
@Entity
@Table(name = "[THE_ENTITY]")
@SequenceGenerator(name = AbstractParentEntity.SEQ_GENERATOR_NAME, sequenceName = "SEQ_THE_SEQUENCE", allocationSize = 10)
public class TheEntity extends AbstractParentEntity {
...
}

Our application uses the Pooled Sequence generator, not hilo, which is configured in the spring configuration:

spring.jpa.properties.hibernate.id.new_generator_mappings: true

I am aware of https://hibernate.atlassian.net/browse/HHH-13783, but I don't see it fitting our case, as it explicitly only affects hilo.

I have double and triple checked the sequence in the database, but it states "increment by 10", and is the same as in all the other environments in which this works. I have cleaned and rebuilt the application. I have dropped and recreated the database several times and restarted the Oracle database because I ran out of things to try. What else can I do to address this issue?

Edit 1:

After debugging into the SequenceStyleGenerator, I can explain this behavior.

The method getSequenceIncrementValue in this Hibernate class tries to read the increment value from the database:

private Long getSequenceIncrementValue(JdbcEnvironment jdbcEnvironment, String sequenceName) {
        return jdbcEnvironment.getExtractedDatabaseMetaData().getSequenceInformationList()
                .stream()
                .filter(
                    sequenceInformation -> {
                        Identifier catalog = sequenceInformation.getSequenceName().getCatalogName();
                        Identifier schema = sequenceInformation.getSequenceName().getSchemaName();
                        return sequenceName.equalsIgnoreCase( sequenceInformation.getSequenceName().getSequenceName().getText() ) &&
                                ( catalog == null || catalog.equals( jdbcEnvironment.getCurrentCatalog() ) ) &&
                                ( schema == null || schema.equals( jdbcEnvironment.getCurrentSchema() ) );
                    }
                )
                .map( SequenceInformation::getIncrementValue )
                .filter( Objects::nonNull )
                .findFirst()
                .orElse( null );
    }

The result of ...getSequenceInformationList() contains two entries with the name SEQ_THE_SEQUENCE. One is from the applications schema, and one by another schema in the database. The sequence exists there, too, and has an increment of 1. As the variables catalog and schema, derived from the sequenceInformation object, are null for both of those entries, Hibernate processes the first one - the one, that is in the wrong schema.

The question now is, why this Metadata is missing.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If the DB user that is used with Hibernate has access to multiple schemas in the database, it will retrieve information about all the sequences, because the query select * from all_sequences is used. If sequences with the same name are present in different schemas, the first one will be compared with the Hibernate definition by chance.

Unfortunately it does not seem to be possible on Oracle to get the schema belonging to a sequence, as the implementation in SequenceInformationExtractorOracleDatabaseImplshows:

    @Override
    protected String sequenceSchemaColumn() {
        return null;
    }

This only happened to us on the local environment (a.k.a "My Machine"), as we have a user there which can access several schemas. It only happened to some developers, as the Integration Test DB Schema was sometimes in the state of a different application version, and then the mismatch happened.

We will need to restrict the user more.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...