2014-04-04 10 views
5

Sto utilizzando Hibernate Envers per mantenere la cronologia degli oggetti. In alcuni punti vogliamo catturare un'istantanea dello stato del grafico dell'oggetto - e possiamo farlo conoscendo la corrispondente revisione di Envers che poi memorizziamo su un record di controllo.Come forzare Hibernate Envers a eseguire il commit di una revisione all'interno di un metodo Spring @Transactional

Tuttavia abbiamo un problema. L'oggetto genitore viene aggiornato all'interno della stessa transazione in cui creiamo e memorizziamo il suo record di controllo figlio, completo con la revisione di Envers. Siamo in grado di ottenere l'ultima revisione:

Number revision = reader.getRevisionNumberForDate(new Date(Long.MAX_VALUE)); 

o creare una nuova revisione:

Number revision = reader.getCurrentRevision(DefaultRevisionEntity.class, true).getId(); 

e utilizzare, ma il commettere del genitore accade sempre dopo. Ed è quando Envers incrementa la revisione. Quindi la revisione che dobbiamo effettivamente fare riferimento nel record di controllo è sempre superiore al valore memorizzato. Nel caso più semplice, si ottiene e conservare revisione N ma la versione genitore abbiamo bisogno è memorizzato come N + 1.

Il riferimento lettore AuditReader può essere ottenuto con:

JpaTransactionManager transactionManager; // injected 
EntityManagerFactory emf = transactionManager.getEntityManagerFactory(); 
EntityManager entityManager = emf.createEntityManager(); 
AuditReader reader = AuditReaderFactory.get(entityManager); 

Stiamo utilizzando Spring 3 @ Annotazioni transazionali e Hibernate 4.2.

Minimal grafico classe:

Parent.class 
    int version   // for hibernate optimistic locking 
    String revisionName 
    List<AuditChild> audits 

AuditChild.class 
    int enversRevision // use to retrieve previous graphs of parent 

Ho provato numerosi approcci per forzare il commit di un genitore che si verifichi prima, tra i quali:

  • Splitting il codice su più metodi con @Transactional (propagation = Propagation.REQUIRES_NEW)
  • Invocazione esplicita di entityManager.flush();

Tutto ciò che ho provato non ha avuto alcun effetto o causato altri problemi. Sarei felice di sapere delle soluzioni che hanno funzionato per gli altri. Grazie.

+0

Il numero di revisione deve essere univoco per una transazione, sei sicuro che 'reader.getCurrentRevision (DefaultRevisionEntity.class, true) .getId()' non funziona?Dovresti avere bisogno di fare riferimento alla revisione "corrente" per la transazione corrente. – adamw

+0

@adamw Sì, ciò crea una nuova revisione. Tuttavia, quando la mia transazione di inclusione è stata confermata, la revisione effettiva delle righe di aggiornamento pertinenti nella tabella * _AUD è> n (n + 1 quando nessun altro aggiornamento al database si sovrappone a questo tx). Quindi l'Id sull'oggetto restituito da 'getCurrentRevision()' non era quello applicato quando è stato eseguito il commit del Tx corrente. La mia soluzione consiste nel creare una revisione fittizia (-1) e in una seconda transazione per rileggere e aggiornare il riferimento di revisione. (Ambiente: Hibernate Envers 4.2, Spring 3.2 @ Transazioni annotate transazionali, DB2 9.1). – user598656

+0

Hmm, beh se chiami getCurrentRevision() in un TX dovresti ottenere la revisione per quella transazione. Avete qualche flush() in mezzo? Anche se non dovrebbe importare davvero. – adamw

risposta

0

Un'alternativa più semplice sarebbe quella di utilizzare @Version.

Innanzitutto, Envers deve essere configurato per verificare effettivamente il valore del campo di blocco ottimistico, che non esegue per impostazione predefinita. A tale scopo, impostando la seguente configurazione:

org.hibernate.envers.do_not_audit_optimistic_locking_field=false 

A questo punto, Envers includerà il campo nella tabella della cronologia di revisione e replicherà il valore della versione ORM assegnato alla tabella di cronologia di controllo. A questo punto, abbiamo un modo univoco di partecipare alla riga dell'entità ORM con la cronologia della cronologia di controllo utilizzando @Formula. Lo SQL formula sarebbe:

SELECT e.REV 
    FROM YourEntity_AUD e 
WHERE e.originalId.id = id 
    AND e.version = version 

Ora il suo altrettanto semplice come l'aggiunta di un campo al vostro soggetto:

@Formula(/* the SQL from above */) 
@NotAudited 
private Integer revisionNumber; 

HTH.

Problemi correlati