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