当前位置: 首页 > 面试题库 >

什么时候使用Spring JPA(休眠)实体管理器将连接返回到连接池?

胡飞鹏
2023-03-14
问题内容

在我的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代理。


问题答案:

一点也不复杂。

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

  2. Aspect @Transactional将拦截所有服务方法调用TransactionInterceptor

  3. TransactionIntreceptor委托事务管理当前的配置 AbstractPlatformTransactionManager实现(JpaTransactionManager你的情况)。

  4. JpaTransactionManager 会将当前正在运行的Spring事务绑定到EntityManager,因此参与当前事务的所有DAO共享相同的持久性上下文。

  5. JpaTransactionManager只需使用EntityManagerTransaction API来控制交易即可:

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

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

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

这会触发hibernate会话(实体管理器)关闭。

  1. 因此,也会触发基础JDBC连接的关闭:

    jdbcCoordinator.close();
    
  2. Hibernate具有逻辑JDBC连接句柄:

        @Override
    public Connection close() {
        LOG.tracev( "Closing JDBC container [{0}]", this );
        if ( currentBatch != null ) {
        LOG.closingUnreleasedBatch();
            currentBatch.release();
        }
        cleanup();
        return logicalConnection.close();
    }
  1. 逻辑连接将close调用委派给当前配置的连接提供程序(DataSourceConnectionProvider在您的情况下),该提供程序仅在JDBC连接上调用close方法:
        @Override
    public void closeConnection(Connection connection) throws SQLException {
         connection.close();
    }
  1. 像任何其他连接池DataSource一样,JDBC连接关闭只会将连接返回到池,而不会关闭物理数据库连接。这是因为连接池数据源返回一个JDBC连接代理,该代理拦截所有调用并将结束事务委托给连接池处理逻辑。

您还可以找到关于此主题的更多细节,以及为什么需要设置hibernate.connection.provider_disables_autocommit与Hibernate属性在这篇文章。



 类似资料:
  • 在我的java进程中,我使用以下spring配置连接到MySql: Entity-Manager由容器注入到数据访问层: 并且我的公共业务逻辑方法使用注释进行了注释。 据我所知,容器负责确保实体管理器在事务完成后将连接返回到池(在我的例子中是HikariCP),但我没有找到任何描述如何管理连接的官方文档。有没有人可以给我解释一下,或者提供一个很好的参考,可以说明使用这样的配置时,连接到底是什么时候

  • 问题内容: 与 那两个工作正常。 使用连接池背后的想法是什么?什么时候使用? 问题答案: 从redis-py docs: 在后台,redis- py使用连接池来管理与Redis服务器的连接。默认情况下,您创建的每个Redis实例将依次创建自己的连接池。通过将已创建的连接池实例传递给Redis类的connection_pool参数,可以覆盖此行为并使用现有的连接池。您可以选择执行此操作,以实现客户端

  • 问题内容: 我有一个Hibernate的服务方法,例如:。securityId2由用户传递。每个SecurityContact与一个Contact有多对一的关系,因此Hibernate在运行此查询时会自动调用联接。但是,Hibernate始终运行的联接是内部联接,因此无法正常运行。有没有办法强迫Hibernate在内部生成左外部联接?这是SecurityContact类的代码: 问题答案: 尝试多

  • 问题内容: 如何在Hibernate中编写此SQL查询?我想使用Hibernate创建查询,而不是创建数据库。 我在SQLServer2008中创建了实体类, 我试图以多种方式构建有效的选择查询,但仍然无法正常工作。 实体有问题吗? 问题答案: 仅当实体之间存在关联时才能使用联接。您的Employee实体不应将名称为,类型为的字段映射到列。它应该与Team实体具有ManyToOne关联,并映射为J

  • 问题内容: 能否请您告诉我,我如何才能很好地为Hibernate实体启用Spring自动装配? 假设我有一个实体,并希望在那里有邮件发送者: 有没有比做的更好的方法 在我的DAO中? 谢谢! 问题答案: 有可能的!(这是Spring Roo中的默认样式!) 您所需要做的就是将@Configurable批注添加到您的实体。在配置中并使用AspectJ编织激活注释。 Spring参考中有一章:7.8.