首页 文章

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

提问于
浏览
13

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

}

实体管理器由容器注入数据访问层:

@PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;

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

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

UPDATE:

到目前为止我能想出的最佳相关信息(taken from here):

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

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

交易方面

事务方面是一个“周围”方面,可以在带注释的业务方法之前和之后调用 . 实现方面的具体类是TransactionInterceptor .

交易方面有两个主要职责:

在“之前”时刻,方面提供了一个挂钩点,用于确定要调用的业务方法是否应在正在进行的数据库事务的范围内运行,或者是否应启动新的单独事务 .

在'之后'时刻,方面需要决定是否应该提交,回滚或继续运行事务 .

在“之前”时刻,事务方面本身不包含任何决策逻辑,如果需要,则启动新事务的决定被委托给事务管理器 .

交易经理

交易经理需要回答两个问题:

应该创建一个新的实体管理器吗?应该启动新的数据库事务吗?这需要在调用逻辑之前的事务方面时确定 . 交易经理将根据以下内容做出决定:

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

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

它们在事务运行时存储在线程中,并且由事务管理器在不再需要时进行清理 .

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

1 回答

  • 20

    它根本不复杂 .

    • 首先,您需要了解Spring事务管理器只是transaction management abstraction . 在您的情况下,实际事务发生在JDBC连接级别 .

    • 所有@Transactional服务方法调用都被TransactionInterceptor方面拦截 .

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

    • JpaTransactionManager将当前运行的Spring事务绑定到EntityManager,因此参与当前事务的所有DAO共享相同的Persistence Context .

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

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

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

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

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

    • 因此触发基础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();
    }
    
    • 逻辑连接句柄将close调用委托给当前配置的连接提供程序(在您的情况下为DataSourceConnectionProvider),它只是在JDBC连接上调用close方法:
    @Override
    public void closeConnection(Connection connection) throws SQLException {
         connection.close();
    }
    
    • 与任何其他connection pooling DataSource一样,JDBC连接关闭只返回到池的连接并且不会因为连接池DataSource返回一个JDBC连接代理,该代理拦截所有调用并将关闭委托给连接池处理逻辑 .

相关问题