#java #hibernate #jpa
#java #mysql #spring #спящий режим #jpa
Вопрос:
В моем 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();
}
}
Диспетчер объектов вводится на уровень доступа к данным контейнером:
@PersistenceContext(type = PersistenceContextType.TRANSACTION, unitName = "my-pu")
private EntityManager myEntityManager;
И мои общедоступные методы бизнес-логики снабжены @Transactional
аннотацией.
Насколько я понимаю, контейнер отвечает за то, чтобы entity-manager возвращал соединения в пул (в моем случае HikariCP) после завершения транзакции, но я не нашел никакой официальной документации, описывающей, как управляются соединения. Кто-нибудь может мне это объяснить или предоставить хорошую ссылку, которая может объяснить, когда именно соединения возвращаются в пул при использовании такой конфигурации?
Обновить:
Лучшая связанная информация, которую я мог придумать до сих пор (взято отсюда):
Прокси-сервер контекста сохранения, который реализует EntityManager, не единственный компонент, необходимый для выполнения декларативного управления транзакциями. На самом деле необходимы три отдельных компонента:
Сам прокси EntityManager Транзакционный аспект Диспетчер транзакций Давайте рассмотрим каждый из них и посмотрим, как они взаимодействуют.
Транзакционный аспект
Транзакционный аспект — это аспект «вокруг», который вызывается как до, так и после аннотированного бизнес-метода. Конкретным классом для реализации аспекта является TransactionInterceptor .
Транзакционный аспект имеет две основные обязанности:
В момент «до» аспект предоставляет точку подключения для определения, должен ли вызываемый бизнес-метод выполняться в рамках текущей транзакции базы данных или должна быть запущена новая отдельная транзакция.
В момент «после» аспекту необходимо решить, следует ли зафиксировать транзакцию, откатить или оставить выполнение.
В момент «до» сам транзакционный аспект не содержит никакой логики принятия решений, решение о запуске новой транзакции, если это необходимо, делегируется менеджеру транзакций.
Диспетчер транзакций
Диспетчер транзакций должен предоставить ответ на два вопроса:
следует ли создавать новый диспетчер сущностей? следует ли запускать новую транзакцию базы данных? Это необходимо решить в тот момент, когда вызывается логика транзакционного аспекта «до». Диспетчер транзакций примет решение на основе:
тот факт, что одна транзакция уже выполняется или нет, является атрибутом распространения транзакционного метода (например, REQUIRES_NEW всегда запускает новую транзакцию) Если диспетчер транзакций решит создать новую транзакцию, то она будет:
создайте новый диспетчер сущностей, привяжите диспетчер сущностей к текущему потоку, захватите соединение из пула подключений БД, привяжите соединение к текущему потоку Диспетчер сущностей и соединение привязаны к текущему потоку с помощью локальных переменных потока.
Они сохраняются в потоке во время выполнения транзакции, и диспетчер транзакций должен очистить их, когда они больше не нужны.
Любые части программы, которым требуется текущий диспетчер сущностей или соединение, могут извлекать их из потока. Одним из программных компонентов, который делает именно это, является прокси EntityManager .
Комментарии:
1. Я сомневаюсь, что контейнер отвечает за возврат соединений. Ответственность несет Spring, поскольку он управляет транзакциями через JpaTransactionManager. Хорошим способом подтверждения было бы включить spring и журналы HikariCP и проверить это.
2. Когда я говорю «контейнер», я использую spring container, или, если быть более точным, Entity-Manager, которым управляет spring container. docs.spring.io/spring/docs/current/spring-framework-reference /…
3. Веб-сайт, которым вы поделились, не работает. Можете ли вы обновить ссылку.
Ответ №1:
Это совсем не сложно.
-
Во-первых, вам нужно понять, что Spring transaction manager — это всего лишь абстракция управления транзакциями. В вашем случае фактические транзакции происходят на уровне соединения JDBC.
-
Все
@Transactional
вызовы методов обслуживания перехватываютсяTransactionInterceptor
Аспектом. -
Управление
TransactionIntreceptor
транзакциями делегируется текущей настроеннойAbstractPlatformTransactionManager
реализации (JpaTransactionManager
в вашем случае). -
JpaTransactionManager
свяжет текущую запущенную транзакцию Spring с EntityManager, поэтому все DAO, участвующие в текущей транзакции, используют один и тот же контекст сохранения. -
JpaTransactionManager
просто использует APIEntityManager
транзакций для управления транзакциями:EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction(); tx.commit();
API транзакций JPA просто делегирует вызов базовым методам фиксации / отката соединения JDBC.
-
Когда транзакция завершена (фиксация / откат),
org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction
вызовы:transactionCoordinator().getTransactionContext().managedClose();
который запускает закрытие сеанса гибернации (Entity Manager).
-
Поэтому базовое соединение 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
в вашем случае), который просто вызывает метод close для соединения JDBC:@Override public void closeConnection(Connection connection) throws SQLException { connection.close(); }
-
Как и любой другой источник данных пула соединений, закрытие соединения JDBC просто возвращает соединение с пулом и не закрывает физическое соединение с базой данных. Это потому, что источник данных пула соединений возвращает прокси-сервер подключения JDBC, который перехватывает все вызовы и делегирует закрытие логике обработки пула соединений.
Обратите внимание, что для транзакций RESOURCE_LOCAL вам также следует установить hibernate.connection.provider_disables_autocommit
свойство, если autocommit
проверка была отключена пулом соединений. Таким образом, соединения с базой данных будут получены лениво до выполнения SQL-запроса или очистки контекста сохранения.
Комментарии:
1. Хорошо, но откуда вы получили эту информацию? Как я могу получить официальную документацию, объясняющую этот процесс, или, по крайней мере, объявляющую обязанности каждого компонента в цепочке?
2. Я только что просмотрел исходный код. Я думаю, что это самая обновленная документация.
3. Я напишу сообщение в блоге, а затем свяжу его с этим вопросом . Это очень интересный вопрос.
4. Конечно, я обновил ответ ссылкой на сообщение в блоге, в котором содержится более подробная информация.
5. » Это совсем не сложно. «, Затем переходит к объяснению 10 уровней абстракции … 😉