![](/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.