Reputation:
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.
Upvotes: 2
Views: 1645
Reputation:
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 SequenceInformationExtractorOracleDatabaseImpl
shows:
@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.
Upvotes: 1