【问题标题】:Connection not releasing for StoredProcedureQuery without @Transactional没有 @Transactional 的 StoredProcedureQuery 连接不释放
【发布时间】:2017-02-07 18:49:42
【问题描述】:

我有以下服务,它只是将错误消息记录到数据库。当我在方法上没有@Transactional 时,连接将保持活动状态,直到所有连接都用完。所有返回结果的StoredProcedureQuery 调用都没有这个问题。

为什么我需要将方法标记为@Transactional 才能释放连接?

@Service
public class SSOErrorLogServiceImpl implements SSOErrorLogService {

    @PersistenceContext
    private EntityManager em;

    @Override
    @Transactional
    public void logError(String errorMessage, String request){
        StoredProcedureQuery proc =   em.createNamedStoredProcedureQuery("dbo.spSSOLogError");
        proc.setParameter("errorMessage", errorMessage);
        proc.setParameter("request", request);
        proc.execute();
    }
}

数据配置

public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean entityManagerFactory
            = new LocalContainerEntityManagerFactoryBean();

    entityManagerFactory.setDataSource(reflexDataSource());
    entityManagerFactory.setPackagesToScan("com.company.platform.jpa");
    entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter());

    Properties additionalProperties = new Properties();
    additionalProperties.put("eclipselink.weaving", "false");

    entityManagerFactory.setJpaProperties(additionalProperties);

    return entityManagerFactory;
}

@Bean
public HikariDataSource reflexDataSource() {
    HikariDataSource dataSource = new HikariDataSource();

    dataSource.setLeakDetectionThreshold(20000);
    dataSource.setMaximumPoolSize(20);
    dataSource.setConnectionTimeout(5000);
    dataSource.setRegisterMbeans(false);
    dataSource.setInitializationFailFast(false);
    dataSource.setDriverClassName(driver);
    dataSource.setJdbcUrl(url);
    dataSource.setUsername(user);
    dataSource.setPassword(password);
    return dataSource;
}

没有事务的日志(摘录)

[EL Finer]: connection: 2016-09-30 14:33:16.955--ServerSession(317574415)--Thread(Thread[Test worker,5,main])--client acquired: 1151058631
[EL Finer]: transaction: 2016-09-30 14:33:16.962--ClientSession(1151058631)--Thread(Thread[Test worker,5,main])--acquire unit of work: 544160013
[EL Finest]: query: 2016-09-30 14:33:16.962--UnitOfWork(544160013)--Thread(Thread[Test worker,5,main])--Execute query ResultSetMappingQuery(name="dbo.spSSOLogError" )
[EL Finest]: connection: 2016-09-30 14:33:16.963--ServerSession(317574415)--Connection(1107704332)--Thread(Thread[Test worker,5,main])--Connection acquired from connection pool [read].
[EL Finest]: connection: 2016-09-30 14:33:16.963--ServerSession(317574415)--Thread(Thread[Test worker,5,main])--reconnecting to external connection pool
[EL Fine]: sql: 2016-09-30 14:33:16.963--ServerSession(317574415)--Connection(539538496)--Thread(Thread[Test worker,5,main])--EXECUTE dbo.spSSOLogError @errorMessage = ?, @request = ?
    bind => [Message, Request]
2016-09-30 14:33:16,984 | TRACE | org.springframework.test.context.TestContextManager:394 - afterTestMethod(): instance [com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest@314c32e8], method [public void com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest.test_logError()], exception [null]
2016-09-30 14:33:16,985 | DEBUG | org.springframework.test.context.support.DirtiesContextTestExecutionListener:94 - After test method: context [DefaultTestContext@6cce3af3 testClass = SSOErrorLogServiceTest, testInstance = com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest@314c32e8, testMethod = test_logError@SSOErrorLogServiceTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@37770c96 testClass = SSOErrorLogServiceTest, locations = '{}', classes = '{class com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest$Config}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class dirties context [false], class mode [null], method dirties context [false].
2016-09-30 14:33:16,985 | TRACE | org.springframework.test.context.TestContextManager:437 - afterTestClass(): class [class com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest]
2016-09-30 14:33:16,986 | DEBUG | org.springframework.test.context.support.DirtiesContextTestExecutionListener:126 - After test class: context [DefaultTestContext@6cce3af3 testClass = SSOErrorLogServiceTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@37770c96 testClass = SSOErrorLogServiceTest, locations = '{}', classes = '{class com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest$Config}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], dirtiesContext [false].

@Transaction 的日志

[EL Finer]: transaction: 2016-09-30 14:30:50.747--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--begin unit of work flush
[EL Finer]: transaction: 2016-09-30 14:30:50.747--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--end unit of work flush
[EL Finest]: query: 2016-09-30 14:30:50.747--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--Execute query ResultSetMappingQuery(name="dbo.spSSOLogError" )
[EL Finest]: connection: 2016-09-30 14:30:50.748--ServerSession(1432568628)--Connection(708660831)--Thread(Thread[Test worker,5,main])--Connection acquired from connection pool [default].
[EL Finer]: transaction: 2016-09-30 14:30:50.748--ClientSession(287210054)--Connection(708660831)--Thread(Thread[Test worker,5,main])--begin transaction
[EL Finest]: connection: 2016-09-30 14:30:50.748--ClientSession(287210054)--Thread(Thread[Test worker,5,main])--reconnecting to external connection pool
[EL Fine]: sql: 2016-09-30 14:30:50.75--ClientSession(287210054)--Connection(29007067)--Thread(Thread[Test worker,5,main])--EXECUTE dbo.spSSOLogError @errorMessage = ?, @request = ?
    bind => [Message, Request]
2016-09-30 14:30:50,772 | TRACE | org.springframework.transaction.interceptor.TransactionAspectSupport:519 - Completing transaction for [com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceImpl.logError]
2016-09-30 14:30:50,772 | TRACE | org.springframework.transaction.support.AbstractPlatformTransactionManager:926 - Triggering beforeCommit synchronization
2016-09-30 14:30:50,772 | TRACE | org.springframework.transaction.support.AbstractPlatformTransactionManager:939 - Triggering beforeCompletion synchronization
2016-09-30 14:30:50,772 | DEBUG | org.springframework.transaction.support.AbstractPlatformTransactionManager:755 - Initiating transaction commit
2016-09-30 14:30:50,773 | DEBUG | org.springframework.orm.jpa.JpaTransactionManager:512 - Committing JPA transaction on EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@8f90fd5]
[EL Finer]: transaction: 2016-09-30 14:30:50.773--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--begin unit of work commit
[EL Finer]: transaction: 2016-09-30 14:30:50.773--ClientSession(287210054)--Connection(29007067)--Thread(Thread[Test worker,5,main])--commit transaction
[EL Finest]: connection: 2016-09-30 14:30:50.775--ServerSession(1432568628)--Connection(708660831)--Thread(Thread[Test worker,5,main])--Connection released to connection pool [default].
[EL Finer]: transaction: 2016-09-30 14:30:50.775--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--end unit of work commit
[EL Finer]: transaction: 2016-09-30 14:30:50.775--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--resume unit of work
2016-09-30 14:30:50,775 | TRACE | org.springframework.transaction.support.AbstractPlatformTransactionManager:952 - Triggering afterCommit synchronization
2016-09-30 14:30:50,775 | TRACE | org.springframework.transaction.support.AbstractPlatformTransactionManager:968 - Triggering afterCompletion synchronization
2016-09-30 14:30:50,775 | TRACE | org.springframework.transaction.support.TransactionSynchronizationManager:331 - Clearing transaction synchronization
2016-09-30 14:30:50,776 | TRACE | org.springframework.transaction.support.TransactionSynchronizationManager:243 - Removed value [org.springframework.orm.jpa.EntityManagerHolder@4569d6c9] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@33d8b566] from thread [Test worker]
2016-09-30 14:30:50,776 | TRACE | org.springframework.transaction.support.TransactionSynchronizationManager:243 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@53d1206a] for key [JDBC URL = jdbc:sqlserver://somehost:1433;databaseName=Database, Username = test, partitions = 1, max (per partition) = 10, min (per partition) = 0, idle max age = 60 min, idle test period = 240 min, strategy = DEFAULT] from thread [Test worker]
2016-09-30 14:30:50,776 | DEBUG | org.springframework.orm.jpa.JpaTransactionManager:600 - Closing JPA EntityManager [org.eclipse.persistence.internal.jpa.EntityManagerImpl@8f90fd5] after transaction
2016-09-30 14:30:50,776 | DEBUG | org.springframework.orm.jpa.EntityManagerFactoryUtils:432 - Closing JPA EntityManager
[EL Finer]: transaction: 2016-09-30 14:30:50.776--UnitOfWork(1448667590)--Thread(Thread[Test worker,5,main])--release unit of work
[EL Finer]: connection: 2016-09-30 14:30:50.776--ClientSession(287210054)--Thread(Thread[Test worker,5,main])--client released
2016-09-30 14:30:50,776 | TRACE | org.springframework.test.context.TestContextManager:394 - afterTestMethod(): instance [com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest@314c32e8], method [public void com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest.test_logError()], exception [null]
2016-09-30 14:30:50,777 | DEBUG | org.springframework.test.context.support.DirtiesContextTestExecutionListener:94 - After test method: context [DefaultTestContext@6cce3af3 testClass = SSOErrorLogServiceTest, testInstance = com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest@314c32e8, testMethod = test_logError@SSOErrorLogServiceTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@37770c96 testClass = SSOErrorLogServiceTest, locations = '{}', classes = '{class com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest$Config}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class dirties context [false], class mode [null], method dirties context [false].
2016-09-30 14:30:50,777 | TRACE | org.springframework.test.context.TestContextManager:437 - afterTestClass(): class [class com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest]
2016-09-30 14:30:50,778 | DEBUG | org.springframework.test.context.support.DirtiesContextTestExecutionListener:126 - After test class: context [DefaultTestContext@6cce3af3 testClass = SSOErrorLogServiceTest, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@37770c96 testClass = SSOErrorLogServiceTest, locations = '{}', classes = '{class com.somecompany.platform.jpa.ssoerrorlog.SSOErrorLogServiceTest$Config}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], dirtiesContext [false].

【问题讨论】:

  • 你是如何指定数据源的,它是否有连接重用限制? 366102 提到了类似的东西,但我之前的印象是代码正在关闭 finally 块中的所有连接。您可以启用调试以打印出错误连接从何处获得的堆栈吗?有些服务器能够处理它,但我不知道它是否是您设置中的一个选项。
  • 我不认为这是重用限制,因为当我在测试中运行它时,第一个连接没有被释放。通过在数据源上设置泄漏检测阈值,我可以在日志输出中很快看到哪些连接没有被释放,但无法获得任何可能表明原因的输出。我认为这是因为 SP 正在插入数据并且需要 @Transaction 才能提交。
  • 如果没有外部事务,它应该自动提交并被释放。将日志记录设置为最佳并查看可能记录的内容。
  • 从日志中可以清楚地看出,没有@Transactional,连接不会被释放,但正如你所说,我希望它会自动提交并被释放。

标签: jpa spring-data eclipselink


【解决方案1】:

您是否尝试将@PersistenceContext 更改为@PersistanceUnit?

通过@PersistenceContext 获得的EntityManagers 是容器管理的实体管理器,因为容器将负责管理“Entity Manager”,而通过@PersistenceUnit / entityManagerFactory.createEntityManager() 获得的EntityManagers 是应用程序管理的实体管理器,开发人员必须管理代码中的某些内容(例如释放 EntityManager 获取的资源)。

【讨论】:

    【解决方案2】:

    尝试释放查询。

    query.unwrap(ProcedureOutputs.class).release();
    

    【讨论】:

      【解决方案3】:

      我们在execute() 通话中遇到了同样的问题。连接没有关闭。看代码更清楚了:execute()

      调用executeUpdate() 解决了。

      【讨论】:

        猜你喜欢
        • 2014-02-07
        • 1970-01-01
        • 2016-10-24
        • 2013-01-29
        • 2015-12-04
        • 1970-01-01
        • 1970-01-01
        • 2011-09-07
        • 1970-01-01
        相关资源
        最近更新 更多