![](/img/trans.png)
[英]When is a connection returned to the connection pool in a JPA application?
[英]When are connections returned to the connection pool with Spring JPA (Hibernate) Entity Manager?
在我的 java 進程中,我使用以下 spring 配置連接到 MySql:
@Configuration
@EnableTransactionManagement
@PropertySources({ @PropertySource("classpath:/myProperties1.properties"), @PropertySource("classpath:/myProperties2.properties") })
public class MyConfiguration {
@Autowired
protected Environment env;
/**
* @return EntityManagerFactory for use with Hibernate JPA provider
*/
@Bean(destroyMethod = "destroy")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setPersistenceUnitManager(persistenceUnitManager());
return em;
}
/**
*
* @return jpaVendorAdapter that works in conjunction with the
* persistence.xml
*/
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.valueOf(env.getProperty("jpa.database")));
vendorAdapter.setDatabasePlatform(env.getProperty("jpa.dialect"));
vendorAdapter.setGenerateDdl(env.getProperty("jpa.generateDdl", Boolean.class, false));
vendorAdapter.setShowSql(env.getProperty("jpa.showSql", Boolean.class, false));
return vendorAdapter;
}
@Bean
public PersistenceUnitManager persistenceUnitManager() {
DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
pum.setPackagesToScan("com.app.dal");
pum.setDefaultPersistenceUnitName("my-pu");
pum.setPersistenceXmlLocations("classpath:/META-INF/persistence.xml");
pum.setDefaultDataSource(dataSource());
return pum;
}
@Bean(destroyMethod = "close")
public DataSource dataSource() {
Properties dsProps = new Properties();
dsProps.put("driverClassName", env.getProperty("hikari.driverClassName"));
dsProps.put("username", env.getProperty("hikari.username"));
dsProps.put("password", env.getProperty("hikari.password"));
dsProps.put("jdbcUrl", env.getProperty("hikari.source.data.jdbcUrl"));
dsProps.put("connectionTimeout", env.getProperty("hikari.connectionTimeout", Integer.class));
dsProps.put("idleTimeout", env.getProperty("hikari.idleTimeout", Integer.class));
dsProps.put("maxLifetime", env.getProperty("hikari.maxLifetime", Integer.class));
dsProps.put("maximumPoolSize", env.getProperty("hikari.maximumPoolSize.rtb.source", Integer.class));
dsProps.put("leakDetectionThreshold", env.getProperty("hikari.leakDetectionThreshold", Integer.class));
dsProps.put("jdbc4ConnectionTest", env.getProperty("hikari.jdbc4ConnectionTest", Boolean.class));
HikariConfig config = new HikariConfig(dsProps);
HikariDataSource ds = new HikariDataSource(config);
return ds;
}
@Bean(name = "sourceTxMgr")
public PlatformTransactionManager sourceDatatransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName("my-pu");
transactionManager.setDataSource(dataSource());
return transactionManager;
}
@Bean
public PersistencyManager persistencyManager() {
return new JpaPersistencyManager();
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
}
Entity-Manager 由容器注入數據訪問層:
@PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;
我的公共業務邏輯方法使用@Transactional
注釋進行注釋。
據我了解,一旦事務完成,容器負責確保實體管理器將連接返回到池(在我的情況下為HikariCP ),但我沒有找到任何描述連接管理方式的官方文檔。 任何人都可以向我解釋它或提供一個很好的參考,可以解釋使用這種配置時連接何時返回到池中?
更新:
到目前為止我能想到的最好的相關信息( 取自這里):
實現 EntityManager 的持久化上下文代理並不是使聲明式事務管理工作所需的唯一組件。 實際上需要三個獨立的組件:
EntityManager 代理本身 事務方面 事務管理器 讓我們仔細看看它們是如何交互的。
交易方面
事務方面是一個“周圍”方面,在注釋的業務方法之前和之后都會被調用。 實現方面的具體類是 TransactionInterceptor。
事務方面有兩個主要職責:
在“之前”時刻,方面提供了一個掛鈎點,用於確定將要調用的業務方法是否應該在正在進行的數據庫事務的范圍內運行,或者是否應該啟動一個新的單獨事務。
在“之后”時刻,方面需要決定事務是應該提交、回滾還是繼續運行。
在“之前”時刻,事務方面本身不包含任何決策邏輯,如果需要,啟動新事務的決定委托給事務管理器。
交易經理
事務管理器需要回答兩個問題:
應該創建一個新的實體管理器嗎? 應該啟動一個新的數據庫事務嗎? 這需要在調用事務方面“之前”邏輯時決定。 事務管理器將根據以下因素做出決定:
一個事務是否已經在進行中的事實 事務方法的傳播屬性(例如 REQUIRES_NEW 總是啟動一個新事務) 如果事務管理器決定創建一個新事務,那么它將:
創建一個新的實體管理器將實體管理器綁定到當前線程從數據庫連接池中獲取一個連接將連接綁定到當前線程實體管理器和連接都使用 ThreadLocal 變量綁定到當前線程。
它們在事務運行時存儲在線程中,當不再需要它們時,由事務管理器來清理它們。
需要當前實體管理器或連接的程序的任何部分都可以從線程中檢索它們。 執行此操作的一個程序組件是 EntityManager 代理。
這一點都不復雜。
首先,您需要了解 Spring 事務管理器只是一個事務管理抽象。 在您的情況下,實際事務發生在 JDBC 連接級別。
所有@Transactional
服務方法調用都被TransactionInterceptor
Aspect 攔截。
TransactionIntreceptor
將事務管理委托給當前配置的AbstractPlatformTransactionManager
實現(在您的情況下為JpaTransactionManager
)。
JpaTransactionManager
將當前運行的 Spring 事務綁定到一個 EntityManager,因此所有參與當前事務的 DAO 共享相同的持久化上下文。
JpaTransactionManager
簡單地使用EntityManager
Transaction API 來控制事務:
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction(); tx.commit();
JPA 事務 API 只是將調用委托給底層 JDBC 連接提交/回滾方法。
當事務完成(提交/回滾)時, org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction
調用:
transactionCoordinator().getTransactionContext().managedClose();
這會觸發休眠會話(實體管理器)關閉。
因此,底層 JDBC 連接也被觸發關閉:
jdbcCoordinator.close();
Hibernate 有一個邏輯 JDBC 連接句柄:
@Override public Connection close() { LOG.tracev( "Closing JDBC container [{0}]", this ); if ( currentBatch != null ) { LOG.closingUnreleasedBatch(); currentBatch.release(); } cleanup(); return logicalConnection.close(); }
邏輯連接將關閉調用委托給當前配置的連接提供程序(在您的情況下為DataSourceConnectionProvider
),它只調用 JDBC 連接上的 close 方法:
@Override public void closeConnection(Connection connection) throws SQLException { connection.close(); }
與任何其他連接池數據源一樣,JDBC 連接關閉只是將連接返回到池中,並不關閉物理數據庫連接。 這是因為連接池 DataSource 返回一個 JDBC Connection 代理,該代理攔截所有調用並將關閉委托給連接池處理邏輯。
請注意,對於 RESOURCE_LOCAL 事務,如果連接池禁用了autocommit
檢查,您還應該設置hibernate.connection.provider_disables_autocommit
屬性。 這樣,在執行 SQL 查詢或刷新持久性上下文之前,將延遲獲取數據庫連接。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.