Hibernate 无法从数据库中获取 SequenceInformation [英] Hibernate could not fetch the SequenceInformation from the database
问题描述
我最近将我的应用程序中的 hibernate 更新到了 5.4.4.Final.现在,我在部署过程中遇到了以下异常.
I have recently updated hibernate in my application to 5.4.4.Final. And now, I have faced with the following exception during deployment.
ERROR [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl|[STANDBY] ExecuteThread: '5' for queue: 'weblogic.kernel.Default (self-tuning)']
Could not fetch the SequenceInformation from the database
java.sql.SQLException: Numeric Overflow
at oracle.jdbc.driver.NumberCommonAccessor.throwOverflow(NumberCommonAccessor.java:4136)
at oracle.jdbc.driver.NumberCommonAccessor.getLong(NumberCommonAccessor.java:634)
at oracle.jdbc.driver.GeneratedStatement.getLong(GeneratedStatement.java:206)
at oracle.jdbc.driver.GeneratedScrollableResultSet.getLong(GeneratedScrollableResultSet.java:259)
at oracle.jdbc.driver.GeneratedResultSet.getLong(GeneratedResultSet.java:558)
at weblogic.jdbc.wrapper.ResultSet_oracle_jdbc_driver_ForwardOnlyResultSet.getLong(Unknown Source)
at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.resultSetMaxValue(SequenceInformationExtractorLegacyImpl.java:139)
at org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl.extractMetadata(SequenceInformationExtractorLegacyImpl.java:61)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl.sequenceInformationList(JdbcEnvironmentImpl.java:403)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentImpl.<init>(JdbcEnvironmentImpl.java:268)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:114)
at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:101)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:152)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:175)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:118)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:900)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:931)
at org.hibernate.jpa.HibernatePersistenceProvider.createContainerEntityManagerFactory(HibernatePersistenceProvider.java:141)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1633)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:956)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:747)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
at com.sternkn.app.services.web.AppContextLoaderListener.<clinit>(AppContextLoaderListener.java:30)
我使用以下persistence.xml.
I use the following persistence.xml.
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">
<persistence-unit name="appPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
<property name="hibernate.id.new_generator_mappings" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value = "true"/>
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.cache.region.factory_class" value="ehcache"/>
<property name="hibernate.cache.ehcache.missing_cache_strategy" value="create" />
<property name="hibernate.cache.region_prefix" value="app_cache" />
<property name="net.sf.ehcache.configurationResourceName" value="/META-INF/app-ehcache.xml" />
<property name="hibernate.bytecode.provider" value="bytebuddy" />
</properties>
</persistence-unit>
</persistence>
经过进一步调查,我发现根本原因如下:hibernate使用SequenceInformation接口进行序列元数据操作
After further investigation, I found out that the root cause is the following: hibernate uses the SequenceInformation interface for the sequences metadata manipulations
public interface SequenceInformation {
Long getMinValue();
Long getMaxValue();
Long getIncrementValue();
...
}
但是,我的应用程序使用如下序列:
However, my app uses the sequences like the following:
SQL> CREATE SEQUENCE SEQ_TEST START WITH 1 INCREMENT BY 1 NOCYCLE;
SQL> select MIN_VALUE, MAX_VALUE, INCREMENT_BY
from USER_SEQUENCES
where SEQUENCE_NAME = 'SEQ_TEST';
MIN_VALUE MAX_VALUE INCREMENT_BY
--------- ---------------------------- ------------
1 9999999999999999999999999999 1
Long.MAX_VALUE 等于 9223372036854775807,因此我得到了数字溢出异常.
The Long.MAX_VALUE is equal to 9223372036854775807, therefore I got the numeric overflow exception.
那么,我的问题:
- 这是休眠中的错误吗?
- 解决问题的最佳方法是什么?
现在我看到以下几种方式:
Now I see the following ways:
- 修正序列声明.就我而言,这可能很有问题.而且,顺便说一下,hibernate 尝试读取有关所有序列的元数据,而不仅仅是我的应用程序中使用的元数据,这看起来很奇怪.
- 创建将扩展 Oracle12cDialect 并覆盖 getQuerySequencesString() 和/或 getSequenceInformationExtractor() 的自定义方言.
public class Oracle8iDialect extends Dialect {
...
public String getQuerySequencesString() {
return "select * from all_sequences";
}
public SequenceInformationExtractor getSequenceInformationExtractor() {
return SequenceInformationExtractorOracleDatabaseImpl.INSTANCE;
}
}
我可以将 SequenceInformationExtractor 切换到 SequenceInformationExtractorNoOpImpl.INSTANCE 并且休眠不会读取序列元数据.这个决定会产生什么影响?Hibernate 尝试通过 INCREMENT_BY 验证 @SequenceGenerator() 的 allocationSize.还有其他原因吗?
I can switch SequenceInformationExtractor to SequenceInformationExtractorNoOpImpl.INSTANCE and hibernate will not read sequences metadata. What impact will this decision have? Hibernate tries to validate allocationSize of @SequenceGenerator() by INCREMENT_BY. Are there other reasons?
任何建议将不胜感激.
更新:这是HHH-13694>
推荐答案
最后,我想到了以下解决方案:
In the end, I came up to the following solution:
- 创建一个扩展
SequenceInformationExtractorOracleDatabaseImpl
的序列信息提取器:
- Create a sequence information extractor that extends
SequenceInformationExtractorOracleDatabaseImpl
:
public class AppSequenceInformationExtractor extends SequenceInformationExtractorOracleDatabaseImpl
{
/**
* Singleton access
*/
public static final AppSequenceInformationExtractor INSTANCE = new AppSequenceInformationExtractor();
@Override
protected Long resultSetMinValue(ResultSet resultSet) throws SQLException {
return resultSet.getBigDecimal("min_value").longValue();
}
}
是的,我知道我们可能会丢失有关此 BigDecimal
值的整体大小和精度的信息,并返回具有相反符号的结果.但这并不重要,因为 this Steve Ebersole 对来自 SequenceInformation
接口的 Long getMinValue()
和 Long getMaxValue()
方法:
Yes, I understand that we can lose information about the overall magnitude and precision of this BigDecimal
value as well as return a result with the opposite sign. But this is not important due to this Steve Ebersole's comment about the Long getMinValue()
and Long getMaxValue()
methods from the SequenceInformation
interface:
我实际上很想从 SequenceInformation
中删除这 2 个方法.我们从不以任何有意义的方式使用它们. 或者将这 2 个方法的返回类型从 Long
更改为 BigInteger
- 它可能是 BigDecimal
代替,但该值隐式是一个整数(在整数意义上).
I'm actually tempted to just drop these 2 methods from
SequenceInformation
. We never use them in any meaningful way. Or change the return type for these 2 methods fromLong
toBigInteger
- it could beBigDecimal
instead, but the value is implicitly an integer (in the whole number sense).
我想在游戏中现在做这些都太晚了,所以像你的改变这样的事情很好 - 就像我说的,我们永远不会使用这些值.我们绝对应该弃用这两种 IMO 方法.
I guess at this point it is too late in the game to do either of these, so something like your change is fine - like I said, we never use these values anyway. We should definitely deprecate these 2 methods IMO.
所以,这个技巧只允许通过最少的额外编码来避免异常.
So, this trick just allows to avoid the exception with minimal awkward extra coding.
- 创建一个扩展
Oracle12cDialect
的休眠方言:
- Create a hibernate dialect that extends
Oracle12cDialect
:
public class AppOracleDialect extends Oracle12cDialect
{
@Override
public SequenceInformationExtractor getSequenceInformationExtractor() {
return AppSequenceInformationExtractor.INSTANCE;
}
@Override
public String getQuerySequencesString() {
return "select * from user_sequences";
}
}
- 然后在
persistence.xml
中使用这个方言:
- And then use this dialect in the
persistence.xml
:
<property name="hibernate.dialect" value="com.my.app.AppOracleDialect" />
至于方法 getQuerySequencesString()
覆盖和使用 USER_SEQUENCES
而不是 ALL_SEQUENCES
这是值得商榷的(见 HHH-13322 和 HHH-14022).但是,就我而言,USER_SEQUENCES
的用法更可取.
As for the method getQuerySequencesString()
overriding and usage USER_SEQUENCES
instead of ALL_SEQUENCES
it's debatable (See HHH-13322 and HHH-14022). But, in my case, the USER_SEQUENCES
usage is preferable.
这篇关于Hibernate 无法从数据库中获取 SequenceInformation的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!