IT

최대 절전 모드에서 데이터베이스에서 시퀀스 정보를 가져올 수 없습니다.

itgroup 2023. 7. 2. 19:13
반응형

최대 절전 모드에서 데이터베이스에서 시퀀스 정보를 가져올 수 없습니다.

최근에 응용 프로그램에서 최대 절전 모드를 5.4.4로 업데이트했습니다.Final. 이제 배포 중에 다음과 같은 예외가 발생했습니다.

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을 사용합니다.

<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>

추가 조사 후, 저는 근본 원인이 다음과 같다는 것을 발견했습니다: 최대 절전 모드는 시퀀스 메타데이터 조작을 위해 시퀀스 정보 인터페이스를 사용합니다.

public interface SequenceInformation {
  Long getMinValue();
  Long getMaxValue();
  Long getIncrementValue();
  ...
}

그러나 내 앱은 다음과 같은 시퀀스를 사용합니다.

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는 922372036854775807과 같으므로 숫자 오버플로 예외가 발생했습니다.

자, 제 질문은 다음과 같습니다.

  • 겨울잠을 자는 벌레인가요?
  • 그것을 해결하는 가장 좋은 방법은 무엇일까요?

이제 다음과 같은 방법을 알게 되었습니다.

  1. 시퀀스 선언을 수정합니다.제 경우에는 상당히 문제가 될 수 있습니다.그런데, 휴면기가 제 애플리케이션에서 사용되는 것뿐만 아니라 모든 시퀀스에 대한 메타데이터를 읽으려고 하는 것이 이상하게 보입니다.
  2. Oracle12cDialect를 확장하고 getQuerySequencesString() 및/또는 getSequenceInformation을 재정의하는 사용자 지정 사투리 만들기추출기().
public class Oracle8iDialect extends Dialect {
  ...
  public String getQuerySequencesString() {
    return "select * from all_sequences";
  }

  public SequenceInformationExtractor getSequenceInformationExtractor() {
    return SequenceInformationExtractorOracleDatabaseImpl.INSTANCE;
  }
}

시퀀스 정보를 전환할 수 있습니다.추출기에서 시퀀스 정보로ExtractorNoOpImppl.인스턴스 및 최대 절전 모드에서는 시퀀스 메타데이터를 읽지 않습니다.이 결정이 어떤 영향을 미칠까요?최대 절전 모드는 INCREMENT_BY를 통해 @SequenceGenerator()의 allocationSize를 검증하려고 합니다.다른 이유가 있습니까?

어떤 제안이든 감사하겠습니다.

업데이트: HHH-13694입니다.

저는 다음과 같이 문제를 해결했습니다.Oracle12cDialect에 대한 확장을 만들었습니다.열의 최대값/ 최소값을 SQL로 제한

package ru.mvawork.hibernate;

import org.hibernate.dialect.Oracle12cDialect;

@SuppressWarnings("unused")
public class CustomOracleDialect extends Oracle12cDialect {

    @Override
    public String getQuerySequencesString() {
        return "select SEQUENCE_OWNER, SEQUENCE_NAME, greatest(MIN_VALUE,         -9223372036854775807) MIN_VALUE,\n"+
                "Least(MAX_VALUE, 9223372036854775808) MAX_VALUE, INCREMENT_BY,     CYCLE_FLAG, ORDER_FLAG, CACHE_SIZE,\n"+
                "Least(greatest(LAST_NUMBER, -9223372036854775807), 9223372036854775808) LAST_NUMBER,\n"+
                "PARTITION_COUNT, SESSION_FLAG, KEEP_VALUE\n"+
                "from all_sequences";
    }

}

application.properties 파일에서 사투리 구현을 참조했습니다.

spring.jpa.properties.hibernate.dialect=ru.mvawork.hibernate.CustomOracleDialect

최소값과 최대값을 제한하여 시퀀스를 재생성할 수 있습니다.저 같은 경우에는 할 수 없습니다.제가 사용하는 기본 키의 치수 번호(12)는 -922372036854775807에서 922372036854775808까지의 범위 내에 있으며 마진이 큽니다.

결국, 저는 다음과 같은 해결책을 생각해냈습니다.

  1. 확할시퀀정추만들기출기보를 확장하는 시퀀스 .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(super.sequenceMinValueColumn()).longValue();
   }
}

네, 저는 우리가 이것의 전체적인 규모와 정확성에 대한 정보를 잃을 수 있다는 것을 이해합니다.BigDecimal값을 지정하고 반대 기호를 사용하여 결과를 반환합니다.하지만 이것은 스티브 에버솔의 언급 때문에 중요하지 않습니다.Long getMinValue()그리고.Long getMaxValue()의드소의 SequenceInformation인터페이스:

저는 실제로 이 두 가지 방법을 에서 삭제하고 싶습니다. 우리는 결코 그것들을 의미 있는 방식으로 사용하지 않습니다.또는 이 두 가지 방법에 대한 반환 유형을 다음에서 변경합니다.LongBigInteger그럴 수도 있습니다BigDecimal대신 값은 암묵적으로 정수(정수 의미)입니다.

이 시점에서 이것들 중 하나를 수행하기에는 너무 늦은 것 같습니다. 따라서 당신의 변화와 같은 것은 괜찮습니다. 제가 말했듯이, 우리는 어쨌든 이러한 가치를 사용하지 않습니다.우리는 분명히 이 두 가지 방법 IMO를 폐지해야 합니다.

그래서 이 속임수는 최소한의 어색한 추가 코딩으로 예외를 피할 수 있게 해줍니다.

  1. 확장된 최대 절전 모드 방언 만들기Oracle12cDialect:
public class AppOracleDialect extends Oracle12cDialect
{
   @Override
   public SequenceInformationExtractor getSequenceInformationExtractor() {
      return AppSequenceInformationExtractor.INSTANCE;
   }
   
   @Override
   public String getQuerySequencesString() {
      return "select * from user_sequences";
   }
}
  1. 그리고 이 방언을 사용합니다.persistence.xml:
<property name="hibernate.dialect" value="com.my.app.AppOracleDialect" />

는 법은방.getQuerySequencesString()의 및 선우 및법용USER_SEQUENCESALL_SEQUENCES논쟁의 여지가 있습니다(HHH-13322HHH-14022 참조).하지만, 제 경우에는,USER_SEQUENCES사용하는 것이 좋습니다.

Java LONG 데이터 유형에 비해 너무 높은 시퀀스의 dafault MAX_VALUE를 사용했습니다.

다행스럽게도 ALTER SEQUENCE를 사용하여 문제가 발생하지 않는 낮은 수치로 언제든지 재설정할 수 있습니다.

CREATE SEQUENCE SEQ_TEST START WITH 1 INCREMENT BY 1 NOCYCLE;


select MAX_VALUE from ALL_SEQUENCES where SEQUENCE_NAME = 'SEQ_TEST';

 MAX_VALUE
----------
9999999999999999999999999999


ALTER SEQUENCE SEQ_TEST
  MAXVALUE 9223372036854775807;

select MAX_VALUE from ALL_SEQUENCES where SEQUENCE_NAME = 'SEQ_TEST';

 MAX_VALUE
----------
9223372036854775807

및 BTW

최대 절전 모드가 내 응용 프로그램에서 사용되는 것뿐만 아니라 모든 시퀀스에 대한 메타데이터를 읽으려고 하는 것이 이상하게 보입니다.

는 최대절모사용을 사용합니다.select * from all_sequences시퀀스 정보를 가져오는 Oracle 방언입니다.:ALL_SEQUENCES모든 기존 시퀀스가 아니라 Hibernate 데이터베이스 사용자(연결 풀의 DBUSER)에게 사용 권한이 부여된 모든 시퀀스를 의미합니다. 물론 이는 절대적으로 정확합니다.

아래와 같은 방언 속성을 "application.properties"에 추가(또는 변경)합니다.

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect

저는 @sternk가 제안한 것과 이미 최대 절전 모드 저장소에 풀 요청으로 존재하지만 아직 병합되지 않은 것의 하이브리드 솔루션을 제안하고자 합니다.

여기서 허용된 답은 빅 십진수가 long으로 변환되면 값의 부호를 변경합니다.

승인된 답변이 작동하지 않는지 확인하기 위해 다음과 같은 간단한 단위 검정을 실행할 수 있습니다.

  @Test
  public void testSignChange() {

    final BigDecimal minValue = BigDecimal.valueOf(Long.MIN_VALUE);
    final BigDecimal minValueMinusTen = minValue.subtract(BigDecimal.TEN);
    final long minValueMinusTenLong = minValueMinusTen.longValue();
    System.out.println("Min value as big decimal: " + minValueMinusTen);
    System.out.println("Min value as long: " + minValueMinusTenLong);

    final BigDecimal maxValue = BigDecimal.valueOf(Long.MAX_VALUE);
    final BigDecimal maxValuePlusTen = maxValue.add(BigDecimal.TEN);
    final long maxValuePlusTenLong = maxValuePlusTen.longValue();
    System.out.println("Max value as big decimal: " + maxValuePlusTen);
    System.out.println("Max value as long: " + maxValuePlusTenLong);
  }

그러면 출력이 다음과 같이 표시됩니다.

Min value as big decimal: -9223372036854775818
Min value as long: 9223372036854775798
Max value as big decimal: 9223372036854775817
Max value as long: -9223372036854775799

따라서 다음과 같이 최소값과 최대값을 모두 처리할 수 있는 솔루션을 제안하고자 합니다.

public class SequenceInformationExtractor extends SequenceInformationExtractorOracleDatabaseImpl {

  public static final SequenceInformationExtractor INSTANCE = new SequenceInformationExtractor();

  private static final BigDecimal LONG_MIN_VALUE_AS_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE);

  private static final BigDecimal LONG_MAX_VALUE_AS_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE);

  @Override
  protected String sequenceMaxValueColumn() {
    return "max_value";
  }

  @Override
  public Long resultSetMinValue(final ResultSet resultSet) throws SQLException {
    final BigDecimal asDecimal = resultSet.getBigDecimal(this.sequenceMinValueColumn());
    if (asDecimal.compareTo(SequenceInformationExtractor.LONG_MIN_VALUE_AS_DECIMAL) < 0) {
      return Long.MIN_VALUE;
    }
    return asDecimal.longValue();
  }

  @Override
  public Long resultSetMaxValue(final ResultSet resultSet) throws SQLException {
    final BigDecimal asDecimal = resultSet.getBigDecimal(this.sequenceMaxValueColumn());
    if (asDecimal.compareTo(SequenceInformationExtractor.LONG_MAX_VALUE_AS_DECIMAL) > 0) {
      return Long.MAX_VALUE;
    }
    return asDecimal.longValue();
  }
}

Postgre용SQL, 나는 Hibernate가 이 문제를 해결할 때까지 이 클래스를 방언으로 사용했습니다.

package com.societe.dialect;

import org.hibernate.dialect.PostgreSQL95Dialect;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;

public class PostgresqlCustomDialect extends PostgreSQL95Dialect {

    public String getQuerySequencesString() {
        return "select * from all_sequences";
    }

    public SequenceInformationExtractor getSequenceInformationExtractor() {
        return SequenceInformationExtractorNoOpImpl.INSTANCE;
    }
}

언급URL : https://stackoverflow.com/questions/58570032/hibernate-could-not-fetch-the-sequenceinformation-from-the-database

반응형