Подтвердить что ты не робот

Когда соединения возвращаются в пул соединений с помощью 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) после выполнения транзакции, но я не нашел официальной документации, описывающей управление подключениями. Может ли кто-нибудь объяснить это мне или предоставить хорошую ссылку, которая может объяснить, когда именно соединения возвращаются в пул при использовании такой конфигурации?

UPDATE:

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

Прокси-сервер контекста persistence, который реализует EntityManager, не является единственным компонентом, необходимым для выполнения декларативного управления транзакциями. На самом деле необходимы три отдельных компонента:

Прокси-сервер EntityManager Транзакционный аспект Менеджер транзакций Перейдем к каждому из них и посмотрим, как они взаимодействуют.

Трансакционный аспект

Transactional Aspect является аспектом "вокруг", который вызывается как до, так и после аннотированного бизнес-метода. Конкретным классом для реализации аспекта является TransactionInterceptor.

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

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

В момент "after" аспект должен решить, следует ли совершить транзакцию, откат или оставление на ходу.

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

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

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

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

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

создать новый менеджер объектов привязать менеджер объектов к текущему потоку захватить соединение из пула соединений DB привязать соединение к текущему потоку Диспетчер объектов и соединение привязаны к текущему потоку с использованием переменных ThreadLocal.

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

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

4b9b3361

Ответ 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();
    

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

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

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

Вы также можете найти более подробную информацию об этой теме и о том, почему вам нужно установить свойство hibernate.connection.provider_disables_autocommit с помощью Hibernate в этой статье.