Я использую JPA EntityListener для выполнения некоторой дополнительной работы по аудиту и вставляю AuditService, управляемый Spring, в свой AuditEntryListener с помощью @Configurable. AuditService создает коллекцию объектов AuditEntry. AuditService сам по себе является bean-компонентом с одноэлементной областью видимости, и я хотел бы собрать все объекты AuditEntry под общим ключом, к которому затем может получить доступ самый внешний уровень службы (тот, который вызвал вызов persist, который, в свою очередь, запустил EntityListener).
Я собираюсь использовать TransactionSynchronizationManager Spring для установки определенного имени транзакции (с использованием UID () или какой-либо другой уникальной стратегии) в начале транзакции,а затем используя это имя в качестве ключа в AuditService, что позволит мне сгруппировать все объекты AuditEntry, созданные в этой транзакции.
Может ли сочетание декларативного и программного управления транзакциями вызвать проблемы? (Хотя я не делаю ничего, кроме установки имени транзакции). Есть ли лучший способ связать сгенерированные объекты AuditEntry с текущей транзакцией? Это решение действительно работает для меня, но, учитывая, что TransactionSynchronizationManager не предназначен для использования в приложениях, я хотел бы убедиться, что мое его использование не вызовет некоторых непредвиденных проблем.
Наконец, связанный, но не сразу уместный вопрос: я знаю, что документация для JPA EntityListeners предостерегает от использования текущего EntityManager, но если бы я действительно хотел использовать его для сравнения объекта с его постоянным самим собой, можно ли было бы безопасно использовать @Transactional ( распространение = REQUIRES_NEW) вокруг моего метода preUpdate ()?
Класс обслуживания
@Transactional
public void create(MyEntity e) {
TransactionSynchronizationManager.setCurrentTransactionName(new UID().toString());
this.em.persist(e);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
Set<AuditEntry> entries = auditService.getAuditEntries(TransactionSynchronizationManager.getCurrentTransactionName());
if(entries != null) {
for(AuditEntry entry : entries) {
//do some stuff....
LOG.info(entry.toString());
}
}
}
});
}
JPA EntityListener
@Configurable
public class AuditEntryListener {
@Autowired
private AuditService service;
@PreUpdate
public void preUpdate(Object entity) {
service.auditUpdate(TransactionSynchronizationManager.getCurrentTransactionName(), entity);
}
public void setService(AuditService service) {
this.service = service;
}
public AuditService getService() {
return service;
}
}
AuditService
@Service
public class AuditService {
private Map<String, Set<AuditEntry>> auditEntryMap = new HashMap<String, Set<AuditEntry>>();
public void auditUpdate(String key, Object entity) {
// do some audit work
// add audit entries to map
this.auditEntryMap.get(key).add(ae);
}
}