Я использую JPA EntityListener, чтобы выполнить дополнительную работу по аудиту, и внедряю Springit AuditService в мой AuditEntryListener, используя @Configurable. AuditService создает коллекцию объектов AuditEntry. AuditService сам по себе является компонентом синглтонной области, и я хотел бы собрать все объекты AuditEntry под общим ключом, к которому затем может обращаться самый внешний сервисный уровень (тот, который вызвал постоянный вызов, который, в свою очередь, вызвал EntityListener).
Я смотрю на использование Spring TransactionSynchronizationManager для установки конкретного имени транзакции (с помощью 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);
}
}