Когда соединения возвращаются в пул соединений с помощью Spring JPA (Hibernate) Entity Manager?

В моем Java-процессе я подключаюсь к MySql, используя следующую конфигурацию Spring:

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

Транзакционный аспект имеет две основные обязанности:

В момент "до" аспект обеспечивает точку подключения для определения того, должен ли бизнес-метод, который должен быть вызван, выполняться в области текущей транзакции базы данных или должна быть запущена новая отдельная транзакция.

В момент "после" аспект должен решить, должна ли транзакция быть зафиксирована, откатана или запущена.

В момент "до" сам Транзакционный аспект не содержит никакой логики принятия решения, решение начать новую транзакцию, если необходимо, делегируется Менеджеру транзакций.

Менеджер транзакций

Менеджер транзакций должен предоставить ответ на два вопроса:

должен быть создан новый Entity Manager? должна быть запущена новая транзакция базы данных? Это должно быть решено в тот момент, когда вызывается транзакционный аспект "до". Менеджер транзакций примет решение на основании:

тот факт, что одна транзакция уже выполняется или нет атрибута распространения метода транзакции (например, REQUIRES_NEW всегда запускает новую транзакцию). Если менеджер транзакций решает создать новую транзакцию, он будет:

создать нового менеджера сущностей, связать менеджера сущностей с текущим потоком, получить соединение из пула соединений БД, связать соединение с текущим потоком. Менеджер сущностей и соединение связаны с текущим потоком с помощью переменных ThreadLocal.

Они хранятся в потоке во время выполнения транзакции, и менеджер транзакций может очистить их, когда они больше не нужны.

Любые части программы, которым нужен текущий менеджер сущностей или соединение, могут получить их из потока. Одним из компонентов программы, который делает именно это, является прокси EntityManager.

1 ответ

Решение

Это совсем не сложно.

  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();
    

    API транзакций JPA просто делегирует вызов базовым методам фиксации / отката соединения JDBC Connection.

  6. Когда транзакция завершена (commit / roll back), 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. Дескриптор логического соединения делегирует вызов close для текущего настроенного поставщика соединений (в вашем случае DataSourceConnectionProvider), который просто вызывает метод close для соединения JDBC:

    @Override
    public void closeConnection(Connection connection) throws SQLException {
         connection.close();
    }
    
  10. Как и любой другой источник данных пула соединений, закрытие соединения JDBC просто возвращает соединение с пулом и не закрывает физическое соединение с базой данных. Это связано с тем, что DataSource пула соединений возвращает прокси JDBC Connection, который перехватывает все вызовы и делегирует закрытие логике обработки пула соединений.

Другие вопросы по тегам