title: Mixing declarative and programmatic transactions with Spring and JPA listeners
body: <p>I'm using a JPA EntityListener to do some additional audit work and am injecting a Spring-managed AuditService into my AuditEntryListener using @Configurable. The AuditService generates a collection of AuditEntry objects. The AuditService is itself a Singleton scoped bean, and I'd like to gather all the AuditEntry objects under a common key that can then be accessed by the outermost service layer (the one that invoked the persist call which in turn triggered the EntityListener). </p>
  
  <p>I'm looking at using Spring's TransactionSynchronizationManager to set a specific transaction name (using UID() or some other unique strategy) at the beginning of the transaction, and then using that name as a key within the AuditService that will allow me to group all AuditEntry objects created within that transaction. </p>
  
  <p>Is mixing declarative and programmatic transaction management have the potential for trouble? (Though I'm doing nothing more than setting the transaction name). Is there a better way to associate the generated AuditEntry objects with the current transaction? This solution does work for me, but given that the TransactionSynchronizationManager isn't intended for application use, I'd like to make sure that my use of it won't cause some unforseen problems.</p>
  
  <h2><em>Related Question</em></h2>
  
  <p>Finally, a related, but not immediately pertinent question: I know that the documentation for JPA EntityListeners cautions against using the current EntityManager, but if I did want to use it to diff an object against it's persisted self, would I be safe using an @Transactional(propagation=REQUIRES_NEW) annotation around my preUpdate() method?</p>
  
  <h2>Prototype Code:</h2>
  
  <p><strong>Service Class</strong></p>
  
  <pre><code>@Transactional
  public void create(MyEntity e) {
  
      TransactionSynchronizationManager.setCurrentTransactionName(new UID().toString());
      this.em.persist(e);
      TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
          @Override
          public void afterCommit() {
              Set&lt;AuditEntry&gt; entries = auditService.getAuditEntries(TransactionSynchronizationManager.getCurrentTransactionName());
              if(entries != null) {
                  for(AuditEntry entry : entries) {
                     //do some stuff....
                     LOG.info(entry.toString());
                  }
              }
          }
      });
  }
  </code></pre>
  
  <p><strong>JPA EntityListener</strong></p>
  
  <pre><code>@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;
  }
  </code></pre>
  
  <p>}</p>
  
  <p><strong>AuditService</strong></p>
  
  <pre><code>@Service
  public class AuditService {
  private Map&lt;String, Set&lt;AuditEntry&gt;&gt; auditEntryMap = new HashMap&lt;String, Set&lt;AuditEntry&gt;&gt;();
  
  public void auditUpdate(String key, Object entity) {
      // do some audit work
      // add audit entries to map
      this.auditEntryMap.get(key).add(ae);
  }
  
  }
  </code></pre>