web-dev-qa-db-ja.com

Spring JPA(Hibernate)Entity Managerで接続が接続プールに返されるのはいつですか?

私のJavaプロセスでは、次のスプリング設定を使用して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:

これまでに考え出した最高の関連情報( ここから取得 ):

EntityManagerを実装する永続コンテキストプロキシは、宣言的なトランザクション管理を機能させるために必要な唯一のコンポーネントではありません。実際には、3つの個別のコンポーネントが必要です。

EntityManagerプロキシ自体トランザクションの側面トランザクションマネージャーそれぞれを見て、それらがどのように相互作用するかを見てみましょう。

トランザクションの側面

トランザクションアスペクトは、注釈付きビジネスメソッドの前後に呼び出される「アラウンド」アスペクトです。アスペクトを実装するための具体的なクラスはTransactionInterceptorです。

トランザクションアスペクトには2つの主な責任があります。

「前」の時点で、アスペクトは、呼び出されようとしているビジネスメソッドを進行中のデータベーストランザクションのスコープで実行するか、新しい別のトランザクションを開始するかを決定するフックポイントを提供します。

「後」の瞬間に、アスペクトはトランザクションをコミットするか、ロールバックするか、実行したままにするかを決定する必要があります。

「前」の時点で、トランザクションアスペクト自体には決定ロジックが含まれておらず、必要に応じて新しいトランザクションを開始する決定がトランザクションマネージャに委任されます。

トランザクションマネージャー

トランザクションマネージャーは、次の2つの質問に答える必要があります。

新しいEntity Managerを作成する必要がありますか?新しいデータベーストランザクションを開始する必要がありますか?これは、Transactional Aspect 'before'ロジックが呼び出された時点で決定する必要があります。トランザクションマネージャは、以下に基づいて決定します。

1つのトランザクションがすでに進行中であるか、トランザクションメソッドの伝播属性ではないという事実(たとえば、REQUIRES_NEWは常に新しいトランザクションを開始します)トランザクションマネージャーが新しいトランザクションの作成を決定すると、次のようになります。

新しいエンティティマネージャを作成し、エンティティマネージャを現在のスレッドにバインドします。DB接続プールから接続を取得します。接続を現在のスレッドにバインドします。エンティティマネージャと接続は、ThreadLocal変数を使用して現在のスレッドにバインドされます。

これらはトランザクションの実行中にスレッドに保存され、不要になったときにクリーンアップするのはトランザクションマネージャー次第です。

現在のエンティティマネージャまたは接続を必要とするプログラムの部分は、スレッドからそれらを取得できます。まさにそれを行う1つのプログラムコンポーネントは、EntityManagerプロキシです。

23
forhas

それはまったく複雑ではありません。

  1. まず、Springトランザクションマネージャーは トランザクション管理の抽象化 にすぎないことを理解する必要があります。あなたの場合、実際のトランザクションはJDBC接続レベルで発生します。

  2. すべての@Transactionalサービスメソッド呼び出しは、TransactionInterceptorアスペクトによってインターセプトされます。

  3. TransactionIntreceptorは、トランザクション管理を現在構成されている AbstractPlatformTransactionManager 実装(あなたの場合はJpaTransactionManager)に委任します。

  4. JpaTransactionManagerは現在実行中のSpringトランザクションをEntityManagerにバインドするため、現在のトランザクションに参加しているすべてのDAOは同じ永続コンテキストを共有します。

  5. JpaTransactionManagerは、単にEntityManager Transaction APIを使用してトランザクションを制御します。

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

    JPAトランザクションAPIは、呼び出しを基礎となるJDBC接続のコミット/ロールバックメソッドに委任するだけです。

  6. トランザクションが完了すると(コミット/ロールバック)、org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionは以下を呼び出します。

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

    hibernate Session(Entity Manager)のクローズをトリガーします。

  7. したがって、基礎となるJDBC接続もトリガーされて閉じられます。

    jdbcCoordinator.close();
    
  8. 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. 論理接続は、JDBC接続でcloseメソッドを単に呼び出す、現在構成されている接続プロバイダー(この場合はDataSourceConnectionProvider)にclose呼び出しを委任します。

    @Override
    public void closeConnection(Connection connection) throws SQLException {
         connection.close();
    }
    
  10. 他の 接続プーリングDataSource と同様に、JDBC接続を閉じると、単純に接続がプールに返され、物理的なデータベース接続は閉じられません。これは、接続プールDataSourceがすべての呼び出しをインターセプトし、接続プール処理ロジックに終了を委任するJDBC接続プロキシを返すためです。

このトピックの詳細と、Hibernateでhibernate.connection.provider_disables_autocommitプロパティを設定する必要がある理由については、 この記事 を参照してください。

30
Vlad Mihalcea