Слушатель при запуске транзакции
Я ищу чистое решение, чтобы иметь слушателя для начала транзакции. Это означает, что я хотел бы, чтобы слушатель был компонентом (компонентом) в контексте Spring, который получал бы событие при запуске транзакции от TransactionPlatformManager или Hibernate Session или чего-то подобного в точке, где начинается новая транзакция.
Что-то вместе:
@Component
class TransactionListener implements ?? {
@Autowired
private Something x;
public void onTransactionBegin(...) {
x.doSomething()
}
}
Чтобы быть конкретным, я смягчаю общесистемную проблему, и мне нужно установить локальный поток при запуске транзакции, чтобы я мог получить доступ к этому локальному потоку в дальнейшем при обработке объектов гибернации для получения информации.
Я изучил источники и не нашел никаких следов для такого слушателя, чтобы быть достижимым. Единственное решение, которое я нашел, состояло в том, чтобы создать подкласс HibernateTransactionManager и его метод doBegin(), что я не нахожу особенно приятным.
2 ответа
У меня была похожая проблема, когда я хотел записать идентификатор сеанса Oracle, как только транзакция началась, чтобы исследовать некоторые проблемы, которые у нас есть.
В конце концов я понял, что, поскольку Spring использует PlatformTransactionManager
Вы можете получить доступ ко всей информации, настроив ее.
Первое, что нужно сделать, это определить, какую реализацию вы используете. В нашем случае это было просто JpaTransactionManager
объявлен в @Configuration
класс, так что это было довольно легко.
Сделав это, обратите внимание, что вы можете включить отладку или трассировку для этого класса, который уже предоставляет много информации о состоянии транзакции, если ваша цель - отладить проблему.
Если этого недостаточно, его легко создать подкласс и заменить предыдущим. Затем просто переопределите методы, которые вы хотите перехватить, как doBegin()
или же prepareSynchronization()
,
Посмотрите, например, мою реализацию:
@Slf4j
public class LoggingJpaTransactionManager extends JpaTransactionManager {
@Autowired
private EntityManager entityManager;
LoggingJpaTransactionManager(EntityManagerFactory emf) {
super(emf);
}
@Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction() && log.isInfoEnabled()) {
Query query = entityManager.createNativeQuery("select sys_context('USERENV','SID') from dual");
Object sessionId = query.getSingleResult();
log.info("Started a new transaction on session id {}", sessionId);
TransactionSynchronizationManager.registerSynchronization(…);
}
}
}
Примечание: я решил переопределить prepareSynchronization()
вместо doBegin()
потому что это позволяет использовать TransactionSynchronizationManager
, который, я думаю, остается чище, чтобы получать уведомления о событиях коммит / откат. Этот метод вызывается сразу после doBegin()
тем не мение.
Spring имеет некоторые обратные вызовы транзакций в своей TransactionSynchronization, однако, как вы правильно заметили, обратного вызова для запуска транзакции нет, моя ошибка.
Насколько я знаю, Spring не сообщит, когда начнутся транзакции, хотя это может отличаться в разных реализациях. PlatformTransactionManager
, Если вы хотите подключиться к транзакции Spring, я думаю, что вы остались с
- Подкласса менеджера транзакций и вызова некоторого обратного вызова
- Создать совет для
@Transactional
с Spring-AOP (это будет работать только, если вы используете аннотации, очевидно)
Если вы используете Hibernate, вам может повезти с afterTransactionBegin
в https://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Interceptor.html
Это работает для меня до сих пор.
@Aspect
@Component
public class StartTransactionInterceptor {
@Pointcut("target(org.springframework.transaction.PlatformTransactionManager)")
public void isPlatformTransactionManager() {
}
@Pointcut("execution(org.springframework.transaction.TransactionStatus getTransaction("
+ "org.springframework.transaction.TransactionDefinition)))")
public void getsTransaction() {
}
@Around("isPlatformTransactionManager() && getsTransaction()")
public Object registerSynchronization(ProceedingJoinPoint joinPoint) throws Throwable {
TransactionStatus value = (TransactionStatus)joinPoint.proceed();
if (value.isNewTransaction()) {
// send some application event to others who are interested
}
return value;
}
}
Кроме того, вы можете использовать Spring's SimpleTransactionScope
и область действия bean для области действия транзакции. Боб будет лениво создаваться, как другие зовут DealWithStuffPerTx.addMoreStuff(Object)
ниже в рамках транзакции.
@Configuration
public class TransactionScopeConfig implements BeanFactoryPostProcessor {
public static final String NAME = "tx";
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerScope(NAME, new SimpleTransactionScope());
}
}
@Component
@Scope(value = TransactionScopeConfig.NAME, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class DealWithStuffPerTx extends TransactionSynchronizationAdapter {
public void addMoreStuff(Object stuff) {
}
@Override
public void afterCommit() {
// deal with stuff
}
@PostConstruct
public void init() {
TransactionSynchronizationManager.registerSynchronization(this);
}
}