何时使用Spring JPA(Hibernate)实体管理器将连接返回到连接池?

时间:2022-09-11 12:54:46

In my java process I'm connecting to MySql using the following spring configuration:

在我的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();
    }

}

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.

我的公共业务逻辑方法使用@Transactional注释进行注释。

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?

据我所知,容器负责确保实体管理器在完成事务后返回到池的连接(在我的案例中为HikariCP)但我没有找到描述如何管理连接的任何官方文档。任何人都可以向我解释或提供一个很好的参考,可以解释在使用这样的配置时何时将确切的连接返回到池中?

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:

实现EntityManager的持久性上下文代理不是使声明式事务管理工作所需的唯一组件。实际上需要三个独立的组件:

The EntityManager Proxy itself The Transactional Aspect The Transaction Manager Let's go over each one and see how they interact.

EntityManager代理本身事务方面事务管理器让我们回顾每一个,看看它们是如何交互的。

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.

事务方面是一个“周围”方面,可以在带注释的业务方法之前和之后调用。实现方面的具体类是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 not the 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:

一个事务已经在进行或事务方法的传播属性的事实(例如REQUIRES_NEW总是启动一个新事务)如果事务管理器决定创建一个新事务,那么它将:

create a new entity manager bind the entity manager to the current thread grab a connection from the DB connection pool bind the connection to the current thread The entity manager and the connection are both bound to the current thread using ThreadLocal variables.

创建新的实体管理器将实体管理器绑定到当前线程从数据库连接池中获取连接绑定到当前线程的连接实体管理器和连接都使用ThreadLocal变量绑定到当前线程。

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.

程序的任何需要当前实体管理器或连接的部分都可以从线程中检索它们。一个程序组件就是EntityManager代理。

1 个解决方案

#1


19  

It's not complicated at all.

它根本不复杂。

  1. 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.

    首先,您需要了解Spring事务管理器只是一个事务管理抽象。在您的情况下,实际事务发生在JDBC连接级别。

  2. All @Transactional service method calls are intercepted by the TransactionInterceptor Aspect.

    TransactionInterceptor方面拦截所有@Transactional服务方法调用。

  3. The TransactionIntreceptor delegates transaction management to the current configured AbstractPlatformTransactionManager implementation (JpaTransactionManager in your case).

    TransactionIntreceptor将事务管理委托给当前配置的AbstractPlatformTransactionManager实现(在您的情况下为JpaTransactionManager)。

  4. 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将当前运行的Spring事务绑定到EntityManager,因此参与当前事务的所有DAO共享相同的Persistence Context。

  5. JpaTransactionManager simply uses EntityManager Transaction API for controlling transactions:

    JpaTransactionManager只使用EntityManager Transaction API来控制事务:

    EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
    tx.commit();
    

    The JPA Transaction API simply delegates the call to the underlying JDBC Connection commit/rollback methods.

    JPA Transaction API只是将调用委托给底层JDBC Connection提交/回滚方法。

  6. When the transaction is done (commit/rollback), the org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction calls:

    当事务完成(提交/回滚)时,org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction调用:

    transactionCoordinator().getTransactionContext().managedClose();
    

    which triggers a Hibernate Session (Entity Manager) close.

    它触发Hibernate会话(实体管理器)关闭。

  7. The underlying JDBC connection is therefore triggered to be closed as well:

    因此,触发基础JDBC连接也将被关闭:

    jdbcCoordinator.close();
    
  8. Hibernate has a logical JDBC connection handle:

    Hibernate有一个逻辑JDBC连接句柄:

    @Override
    public Connection close() {
        LOG.tracev( "Closing JDBC container [{0}]", this );
        if ( currentBatch != null ) {
        LOG.closingUnreleasedBatch();
            currentBatch.release();
        }
        cleanup();
        return logicalConnection.close();
    }
    
  9. 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:

    逻辑连接句柄将close调用委托给当前配置的连接提供程序(在您的情况下为DataSourceConnectionProvider),它只是在JDBC连接上调用close方法:

    @Override
    public void closeConnection(Connection connection) throws SQLException {
         connection.close();
    }
    
  10. 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.

    与任何其他连接池DataSource一样,JDBC连接close只返回到池的连接,并且不关闭物理数据库连接。这是因为连接池DataSource返回一个JDBC连接代理,它拦截所有调用并将关闭委托给连接池处理逻辑。

#1


19  

It's not complicated at all.

它根本不复杂。

  1. 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.

    首先,您需要了解Spring事务管理器只是一个事务管理抽象。在您的情况下,实际事务发生在JDBC连接级别。

  2. All @Transactional service method calls are intercepted by the TransactionInterceptor Aspect.

    TransactionInterceptor方面拦截所有@Transactional服务方法调用。

  3. The TransactionIntreceptor delegates transaction management to the current configured AbstractPlatformTransactionManager implementation (JpaTransactionManager in your case).

    TransactionIntreceptor将事务管理委托给当前配置的AbstractPlatformTransactionManager实现(在您的情况下为JpaTransactionManager)。

  4. 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将当前运行的Spring事务绑定到EntityManager,因此参与当前事务的所有DAO共享相同的Persistence Context。

  5. JpaTransactionManager simply uses EntityManager Transaction API for controlling transactions:

    JpaTransactionManager只使用EntityManager Transaction API来控制事务:

    EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
    tx.commit();
    

    The JPA Transaction API simply delegates the call to the underlying JDBC Connection commit/rollback methods.

    JPA Transaction API只是将调用委托给底层JDBC Connection提交/回滚方法。

  6. When the transaction is done (commit/rollback), the org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction calls:

    当事务完成(提交/回滚)时,org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction调用:

    transactionCoordinator().getTransactionContext().managedClose();
    

    which triggers a Hibernate Session (Entity Manager) close.

    它触发Hibernate会话(实体管理器)关闭。

  7. The underlying JDBC connection is therefore triggered to be closed as well:

    因此,触发基础JDBC连接也将被关闭:

    jdbcCoordinator.close();
    
  8. Hibernate has a logical JDBC connection handle:

    Hibernate有一个逻辑JDBC连接句柄:

    @Override
    public Connection close() {
        LOG.tracev( "Closing JDBC container [{0}]", this );
        if ( currentBatch != null ) {
        LOG.closingUnreleasedBatch();
            currentBatch.release();
        }
        cleanup();
        return logicalConnection.close();
    }
    
  9. 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:

    逻辑连接句柄将close调用委托给当前配置的连接提供程序(在您的情况下为DataSourceConnectionProvider),它只是在JDBC连接上调用close方法:

    @Override
    public void closeConnection(Connection connection) throws SQLException {
         connection.close();
    }
    
  10. 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.

    与任何其他连接池DataSource一样,JDBC连接close只返回到池的连接,并且不关闭物理数据库连接。这是因为连接池DataSource返回一个JDBC连接代理,它拦截所有调用并将关闭委托给连接池处理逻辑。