问题描述
@Configuration
@ EnableTransactionManagement
@PropertySources({@PropertySource(classpath:/myProperties1.properties),@PropertySource(classpath:/myProperties2.properties)})
public class MyConfiguration {
@Autowired
保护环境env;
/ **
* @return EntityManagerFactory用于Hibernate JPA提供程序
* /
@Bean(destroyMethod =destroy)
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setJpaVendorAdapter(jpaVendorAdapter());
em.setPersistenceUnitManager(persistenceUnitManager());
返回em;
}
/ **
*
* @return jpaVendorAdapter与
* 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));
返回vendorAdapter;
$ b $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;
$ b $Bean(destroyMethod =close)
public DataSource dataSource(){
属性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;
$ b $Bean(name =sourceTxMgr)
Platform PlatformTransactionManager sourceDatatransactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setPersistenceUnitName(my-pu);
transactionManager.setDataSource(dataSource());
返回transactionManager;
}
@Bean
public PersistencyManager persistenceManager(){
return new JpaPersistencyManager();
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
}
实体管理器被注入数据访问层由容器:
@PersistenceContext(type = PersistenceContextType.TRANSACTION,unitName =my-pu)
私人EntityManager myEntityManager;
我的公共业务逻辑方法用 @Transactional
注解。
据我了解,容器负责确保实体管理器返回连接池(在我的情况下)一旦交易完成,但我没有找到任何描述连接管理方式的官方文档。任何人都可以向我解释或提供一个很好的参考,可以解释何时使用这样的配置连接返回到池?
更新:
到目前为止,我可以提出的最好的相关信息():
实现EntityManager的持久性上下文代理不是唯一需要的组件用于进行声明式事务管理工作。实际上,需要三个独立的组件:
EntityManager代理本身
交易方面
交易管理器
让我们来看看每一个,看看它们是如何相互作用的。
交易方面
交易方面是一个被称为周围的方面在注释商业方法之前和之后。实现该方面的具体类是TransactionInterceptor。
交易方面有两个主要职责: 'before'时刻,该方面提供了一个挂钩点,用于确定即将被调用的业务方法是否应该在正在进行的数据库事务的范围内运行,或者是否应该启动新的单独事务。
在'after'时刻,方面需要决定事务是应该提交,回滚还是继续运行。
在'在事务处理方面本身不包含任何决策逻辑之前,如果需要的话,决定启动一个新事务被委托给事务管理器。
事务管理器
p>交易管理者需要提供两个问题的答案:
应该创建一个新的实体经理吗?
应该开始新的数据库事务吗?
这需要在调用逻辑之前的Transactional Aspect'逻辑被调用时决定。事务管理器将根据以下内容来决定:
事实上一个事务已经在进行或者没有
事务方法的传播属性(例如REQUIRES_NEW总是开始一个新的事务)
如果事务管理器决定创建一个新的事务,那么它会:
创建一个新的实体管理器
绑定实体manager到当前线程
从数据库连接池中获取连接
将连接绑定到当前线程
实体管理器和连接都使用ThreadLocal变量绑定到当前线程。 / p>
它们在事务运行时存储在线程中,并且由事务管理器在不再需要时清理它们。
需要当前实体管理器或连接的程序的任何部分都可以从线程中检索它们。一个程序组件,完全是EntityManager代理。
这完全不复杂。 b
-
首先,您需要了解Spring事务管理器只是一个实现(在您的情况下为JpaTransactionManager)。
-
JpaTransactionManager将绑定当前正在运行的Spring事务到一个EntityManager,所以参与当前事务的所有DAO共享相同的持久性上下文。
控制交易: -
当事务完成(提交/回滚)时,org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction调用:
transactionCoordinator()。getTransactionContext()。managedClose();
触发Hibernate Session(实体管理器)关闭。
因此底层JDBC连接也会被触发: ;
-
Hibernate有一个逻辑JDBC连接句柄:
@Override
public Connection close(){
LOG.tracev(关闭JDBC容器[{0}],this);
if(currentBatch!= null){
LOG.closingUnreleasedBatch();
currentBatch.release();
}
cleanup();
返回logicalConnection.close();
$ p
逻辑连接句柄将close调用委托给当前配置的连接提供者(在你的情况下是DataSourceConnectionProvider),它只是在JDBC连接上调用close方法:
@Override
public void closeConnection(Connection connection)引发SQLException {
connection.close();
}
与其他,JDBC连接关闭只是简单地将连接返回到池并且不关闭物理数据库连接。这是因为连接池DataSource会返回一个JDBC Connection代理,该代理截取所有调用,并将关闭委托给连接池处理逻辑。
First you need to understand that Spring transaction manager is only a transaction management abstraction. In your case the actual transactions happen at JDBC Connection level.
All @Transactional service method calls are intercepted by the TransactionInterceptor Aspect.
The TransactionIntreceptor delegates transaction management to the current configuredAbstractPlatformTransactionManager implementation (JpaTransactionManager in your case).
JpaTransactionManager will bind the current running Spring transaction to an EntityManager, so all DAOs participating in the current transaction share the same Persistence Context.
JpaTransactionManager simply uses EntityManager Transaction API for controlling transactions:
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction(); tx.commit();
The JPA Transaction API simply delegates the call to the underlying JDBC Connection commit/rollback methods.
When the transaction is done (commit/rollback), the org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction calls:
transactionCoordinator().getTransactionContext().managedClose();
which triggers a Hibernate Session (Entity Manager) close.
The underlying JDBC connection is therefore triggered to be closed as well:
jdbcCoordinator.close();
Hibernate has a logical JDBC connection handle:
@Override public Connection close() { LOG.tracev( "Closing JDBC container [{0}]", this ); if ( currentBatch != null ) { LOG.closingUnreleasedBatch(); currentBatch.release(); } cleanup(); return logicalConnection.close(); }
The logical connection handle delegates the close call to the currently configured connection provider (DataSourceConnectionProvider in your case), which simply calls the close method on the JDBC connection:
@Override public void closeConnection(Connection connection) throws SQLException { connection.close(); }
Like any other connection pooling DataSource, the JDBC connection close simply returns the connection to the pool and doesn't close the physical database connection. That's because the connection pooling DataSource returns a JDBC Connection proxy that intercepts all calls and delegates the closing to the connection pool handling logic.
EntityTransaction tx = txObject.getEntityManagerHolder()。getEntityManager()。getTransaction();
tx.commit();
JPA Transaction API简单地将调用委托给底层JDBC Connection提交/回滚方法。
jdbcCoordinator.close()$ b
In my java process I'm connecting to MySql using the following spring configuration:
@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();
}
}
The Entity-Manager is injected to the data access layer by the container:
@PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;
And my public business logic methods are annotated with the @Transactional
annotation.
As far as I understand the container is responsible for ensuring that the entity-manager returns connections to the pool (in my case HikariCP) once a transaction is done but I did not find any official documentation that describes how the connections are managed. Can anyone explain it to me or provide a good reference that can explain when exactly connections are returned to the pool when using such a configuration?
UPDATE:
The best related info I could come up with so far (taken from here):
The persistence context proxy that implements EntityManager is not the only component needed for making declarative transaction management work. Actually three separate components are needed:
The EntityManager Proxy itselfThe Transactional AspectThe Transaction ManagerLet's go over each one and see how they interact.
The Transactional Aspect
The Transactional Aspect is an 'around' aspect that gets called both before and after the annotated business method. The concrete class for implementing the aspect is TransactionInterceptor.
The Transactional Aspect has two main responsibilities:
At the 'before' moment, the aspect provides a hook point for determining if the business method about to be called should run in the scope of an ongoing database transaction, or if a new separate transaction should be started.
At the 'after' moment, the aspect needs to decide if the transaction should be committed, rolled back or left running.
At the 'before' moment the Transactional Aspect itself does not contain any decision logic, the decision to start a new transaction if needed is delegated to the Transaction Manager.
The Transaction Manager
The transaction manager needs to provide an answer to two questions:
should a new Entity Manager be created?should a new database transaction be started?This needs to be decided at the moment the Transactional Aspect 'before' logic is called. The transaction manager will decide based on:
the fact that one transaction is already ongoing or notthe propagation attribute of the transactional method (for example REQUIRES_NEW always starts a new transaction)If the transaction manager decides to create a new transaction, then it will:
create a new entity managerbind the entity manager to the current threadgrab a connection from the DB connection poolbind the connection to the current threadThe entity manager and the connection are both bound to the current thread using ThreadLocal variables.
They are stored in the thread while the transaction is running, and it's up to the Transaction Manager to clean them up when no longer needed.
Any parts of the program that need the current entity manager or connection can retrieve them from the thread. One program component that does exactly that is the EntityManager proxy.
解决方案 It's not complicated at all.
这篇关于何时使用Spring JPA(Hibernate)实体管理器将连接返回到连接池?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!