ORA-02396: 使用多个 DS 和 Hikari 时超出最大空闲
ORA-02396: exceeded maximum idle when several DS and Hikari used
我们有几个系统,使用相同的核心库和相同的 Oracle 数据库。但是每天只有一个系统出错,下面是堆栈跟踪。
错误是:ORA-04042: procedure, function, package, or package body does not exist
。该系统与其他系统的区别在于,该系统使用多个数据源,在下面您可以看到 Hikari 配置、build.gradle
的一部分和堆栈跟踪。所有其他系统使用一个数据源。
这是 Oracle 版本信息:
BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Release 12.1.0.1.0 - 64bit Production 0
PL/SQL Release 12.1.0.1.0 - Production 0
CORE 12.1.0.1.0 Production 0
TNS for 64-bit Windows: Version 12.1.0.1.0 - Production 0
NLSRTL Version 12.1.0.1.0 - Production 0
这是用于配置其中一个数据源的代码。第二个以同样的方式生产:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "sourceEntityManagerFactory",
transactionManagerRef = "sourceTransactionManager",
basePackages = { "com.maxi.jpa.source" }
)
public class SourceConfig
{
@Bean(name = "sourceDS")
@Primary
@ConfigurationProperties("spring.datasource.ds-src")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "sourceEntityManagerFactory")
@DependsOn("sourceDS")
@Primary
public LocalContainerEntityManagerFactoryBean sourceEMF(
EntityManagerFactoryBuilder builder,
@Qualifier("sourceDS")
DataSource ds
) {
return builder
.dataSource(ds)
.packages("com.maxi.jpa.model")
.persistenceUnit("source")
.build();
}
@Bean(name = "sourceEntityManager")
@Primary
public EntityManager sourceEM(
@Qualifier("sourceEntityManagerFactory")
EntityManagerFactory factory
) {
return factory.createEntityManager();
}
@Bean(name = "sourceTransactionManager")
@Primary
public PlatformTransactionManager transactionManager(
@Qualifier("sourceEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
这是异常的代码:
private List<String> descTable(String owner, String tableName) {
String query =
"select column_name " +
"from all_tab_columns " +
"where upper(owner) = '%s' " +
"and upper(table_name) = '%s'";
query = String.format(query, owner, tableName);
@SuppressWarnings("unchecked")
List<String> result = (List<String>)sourceEntityManager
.createNativeQuery(query)
.getResultList();
return result;
}
这样,我注入 sourceEntityManager
:
@Service
public class DMLService
{
private EntityManager sourceEntityManager;
private EntityManager destEntityManager;
public DMLService(
@Qualifier("sourceEntityManager")
EntityManager sourceEntityManager,
@Qualifier("destEntityManager")
EntityManager destEntityManager
) {
this.sourceEntityManager = sourceEntityManager;
this.destEntityManager = destEntityManager;
}
....
}
application.yaml
spring:
datasource:
ds-src:
driverClassName: oracle.jdbc.OracleDriver
jdbcUrl: jdbc:oracle:thin:@${DB4VAL_SRC_DB}
username: ${DB4VAL_SRC_USERNAME}
password: ${DB4VAL_SRC_PASSWORD}
poolName: Db4ValidateSource
connectionTestQuery: SELECT 1 FROM DUAL
ds-dest:
driverClassName: oracle.jdbc.OracleDriver
jdbcUrl: jdbc:oracle:thin:@${DB4VAL_DEST_DB}
username: ${DB4VAL_DEST_USERNAME}
password: ${DB4VAL_DEST_PASSWORD}
poolName: Db4ValidateDestination
connectionTestQuery: SELECT 1 FROM DUAL
jpa:
database-platform: org.hibernate.dialect.Oracle12cDialect
application-default.yaml
spring:
datasource:
ds-src:
minimumIdle: 1
maximumPoolSize: 5
idleTimeout: 20000
maxLifetime: 60000
keepaliveTime: 10000
connectionTimeout: 30000
ds-dest:
minimumIdle: 1
maximumPoolSize: 5
idleTimeout: 20000
maxLifetime: 60000
keepaliveTime: 10000
connectionTimeout: 30000
build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3'
implementation 'commons-net:commons-net:3.6'
implementation 'commons-io:commons-io:2.6'
implementation 'com.zaxxer:HikariCP:4.0.2'
implementation 'org.springframework:spring-context-support'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
...
}
stacktrace
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:628)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:557)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:730)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:291)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:492)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:148)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:928)
at oracle.jdbc.driver.OracleStatement.prepareDefineBufferAndExecute(OracleStatement.java:1158)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1093)
at oracle.jdbc.driver.OracleStatement.executeSQLSelect(OracleStatement.java:1402)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1285)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3735)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3847)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1098)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
at org.hibernate.loader.Loader.getResultSet(Loader.java:2297)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2050)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2012)
at org.hibernate.loader.Loader.doQuery(Loader.java:948)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
at org.hibernate.loader.Loader.doList(Loader.java:2843)
at org.hibernate.loader.Loader.doList(Loader.java:2825)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2657)
at org.hibernate.loader.Loader.list(Loader.java:2652)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2141)
at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1169)
at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:176)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1604)
at org.hibernate.query.Query.getResultList(Query.java:165)
at com.maxi.services.DMLService.descTable(DMLService.java:77)
at com.maxi.services.DMLService.getTableData(DMLService.java:191)
at com.maxi.services.DMLService.processRecord(DMLService.java:203)
at com.maxi.services.ValidateService.processRecord(ValidateService.java:263)
at com.maxi.services.ValidateService.runETL(ValidateService.java:362)
at com.maxi.services.Handler.runProcess(Handler.java:54)
at com.maxi.services.Handler.lambda$handleRequest[=17=](Handler.java:35)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
如您所见,系统捕获了所有配置
所以,问题是 EntityManager 从池中检索连接,但 returns 不检索它,直到它关闭。但是我们无法手动关闭它,因为它是一个 bean,我们可能无法重新打开它。
所以解决方案不是手动创建 EM,而是使用 @PersistenceContext(unitName = "unit_name")
.
从框架中获取它
有必要删除此代码(对于两个 EM):
@Bean(name = "sourceEntityManager")
@Primary
public EntityManager sourceEM(
@Qualifier("sourceEntityManagerFactory")
EntityManagerFactory factory
) {
return factory.createEntityManager();
}
@Bean(name = "sourceTransactionManager")
@Primary
public PlatformTransactionManager transactionManager(
@Qualifier("sourceEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
并按以下方式 Autowire
beans:
@PersistenceContext(unitName = "source")
private EntityManager sourceEntityManager;
@PersistenceContext(unitName = "destination")
private EntityManager destEntityManager;
不要忘记重写代码以使用 @Transactions
(jpa
或 spring
)。将注释放在 public 方法或整个 class.
上
我们有几个系统,使用相同的核心库和相同的 Oracle 数据库。但是每天只有一个系统出错,下面是堆栈跟踪。
错误是:ORA-04042: procedure, function, package, or package body does not exist
。该系统与其他系统的区别在于,该系统使用多个数据源,在下面您可以看到 Hikari 配置、build.gradle
的一部分和堆栈跟踪。所有其他系统使用一个数据源。
这是 Oracle 版本信息:
BANNER CON_ID
-------------------------------------------------------------------------------- ----------
Oracle Database 12c Release 12.1.0.1.0 - 64bit Production 0
PL/SQL Release 12.1.0.1.0 - Production 0
CORE 12.1.0.1.0 Production 0
TNS for 64-bit Windows: Version 12.1.0.1.0 - Production 0
NLSRTL Version 12.1.0.1.0 - Production 0
这是用于配置其中一个数据源的代码。第二个以同样的方式生产:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "sourceEntityManagerFactory",
transactionManagerRef = "sourceTransactionManager",
basePackages = { "com.maxi.jpa.source" }
)
public class SourceConfig
{
@Bean(name = "sourceDS")
@Primary
@ConfigurationProperties("spring.datasource.ds-src")
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
@Bean(name = "sourceEntityManagerFactory")
@DependsOn("sourceDS")
@Primary
public LocalContainerEntityManagerFactoryBean sourceEMF(
EntityManagerFactoryBuilder builder,
@Qualifier("sourceDS")
DataSource ds
) {
return builder
.dataSource(ds)
.packages("com.maxi.jpa.model")
.persistenceUnit("source")
.build();
}
@Bean(name = "sourceEntityManager")
@Primary
public EntityManager sourceEM(
@Qualifier("sourceEntityManagerFactory")
EntityManagerFactory factory
) {
return factory.createEntityManager();
}
@Bean(name = "sourceTransactionManager")
@Primary
public PlatformTransactionManager transactionManager(
@Qualifier("sourceEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
这是异常的代码:
private List<String> descTable(String owner, String tableName) {
String query =
"select column_name " +
"from all_tab_columns " +
"where upper(owner) = '%s' " +
"and upper(table_name) = '%s'";
query = String.format(query, owner, tableName);
@SuppressWarnings("unchecked")
List<String> result = (List<String>)sourceEntityManager
.createNativeQuery(query)
.getResultList();
return result;
}
这样,我注入 sourceEntityManager
:
@Service
public class DMLService
{
private EntityManager sourceEntityManager;
private EntityManager destEntityManager;
public DMLService(
@Qualifier("sourceEntityManager")
EntityManager sourceEntityManager,
@Qualifier("destEntityManager")
EntityManager destEntityManager
) {
this.sourceEntityManager = sourceEntityManager;
this.destEntityManager = destEntityManager;
}
....
}
application.yaml
spring:
datasource:
ds-src:
driverClassName: oracle.jdbc.OracleDriver
jdbcUrl: jdbc:oracle:thin:@${DB4VAL_SRC_DB}
username: ${DB4VAL_SRC_USERNAME}
password: ${DB4VAL_SRC_PASSWORD}
poolName: Db4ValidateSource
connectionTestQuery: SELECT 1 FROM DUAL
ds-dest:
driverClassName: oracle.jdbc.OracleDriver
jdbcUrl: jdbc:oracle:thin:@${DB4VAL_DEST_DB}
username: ${DB4VAL_DEST_USERNAME}
password: ${DB4VAL_DEST_PASSWORD}
poolName: Db4ValidateDestination
connectionTestQuery: SELECT 1 FROM DUAL
jpa:
database-platform: org.hibernate.dialect.Oracle12cDialect
application-default.yaml
spring:
datasource:
ds-src:
minimumIdle: 1
maximumPoolSize: 5
idleTimeout: 20000
maxLifetime: 60000
keepaliveTime: 10000
connectionTimeout: 30000
ds-dest:
minimumIdle: 1
maximumPoolSize: 5
idleTimeout: 20000
maxLifetime: 60000
keepaliveTime: 10000
connectionTimeout: 30000
build.gradle
dependencies {
implementation 'org.apache.commons:commons-lang3'
implementation 'commons-net:commons-net:3.6'
implementation 'commons-io:commons-io:2.6'
implementation 'com.zaxxer:HikariCP:4.0.2'
implementation 'org.springframework:spring-context-support'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
...
}
stacktrace
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:628)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:557)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:730)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:291)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:492)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:148)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:928)
at oracle.jdbc.driver.OracleStatement.prepareDefineBufferAndExecute(OracleStatement.java:1158)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1093)
at oracle.jdbc.driver.OracleStatement.executeSQLSelect(OracleStatement.java:1402)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1285)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3735)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3847)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1098)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:57)
at org.hibernate.loader.Loader.getResultSet(Loader.java:2297)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2050)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:2012)
at org.hibernate.loader.Loader.doQuery(Loader.java:948)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:349)
at org.hibernate.loader.Loader.doList(Loader.java:2843)
at org.hibernate.loader.Loader.doList(Loader.java:2825)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2657)
at org.hibernate.loader.Loader.list(Loader.java:2652)
at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:338)
at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:2141)
at org.hibernate.internal.AbstractSharedSessionContract.list(AbstractSharedSessionContract.java:1169)
at org.hibernate.query.internal.NativeQueryImpl.doList(NativeQueryImpl.java:176)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1604)
at org.hibernate.query.Query.getResultList(Query.java:165)
at com.maxi.services.DMLService.descTable(DMLService.java:77)
at com.maxi.services.DMLService.getTableData(DMLService.java:191)
at com.maxi.services.DMLService.processRecord(DMLService.java:203)
at com.maxi.services.ValidateService.processRecord(ValidateService.java:263)
at com.maxi.services.ValidateService.runETL(ValidateService.java:362)
at com.maxi.services.Handler.runProcess(Handler.java:54)
at com.maxi.services.Handler.lambda$handleRequest[=17=](Handler.java:35)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
如您所见,系统捕获了所有配置
所以,问题是 EntityManager 从池中检索连接,但 returns 不检索它,直到它关闭。但是我们无法手动关闭它,因为它是一个 bean,我们可能无法重新打开它。
所以解决方案不是手动创建 EM,而是使用 @PersistenceContext(unitName = "unit_name")
.
有必要删除此代码(对于两个 EM):
@Bean(name = "sourceEntityManager")
@Primary
public EntityManager sourceEM(
@Qualifier("sourceEntityManagerFactory")
EntityManagerFactory factory
) {
return factory.createEntityManager();
}
@Bean(name = "sourceTransactionManager")
@Primary
public PlatformTransactionManager transactionManager(
@Qualifier("sourceEntityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
并按以下方式 Autowire
beans:
@PersistenceContext(unitName = "source")
private EntityManager sourceEntityManager;
@PersistenceContext(unitName = "destination")
private EntityManager destEntityManager;
不要忘记重写代码以使用 @Transactions
(jpa
或 spring
)。将注释放在 public 方法或整个 class.