2013-03-28 15 views
5

Cerco di registrare eventuali modifiche delle entità JPA. Per questa ragione ciascuna entità eredita da una classe di entità astratta che ha una lista di oggetti LogEntry.Modifiche al rilevamento JPA di EclipseLink

classe AbstractEntity: classe

@Entity 
@Inheritance(strategy = InheritanceType.JOINED) 
@EntityListeners(ChangeListener.class) 
public abstract class AbstractEntity implements Serializable { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private Long id; 
    @Version 
    private Long version; 
    @Temporal(TemporalType.DATE) 
    private Date validFrom; 
    @Temporal(TemporalType.DATE) 
    private Date validTo; 
    private String name; 
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "abstractEntity") 
    private List<LogEntry> logEntry = new ArrayList<LogEntry>(); 
    //getter and setter 
} 

LogEntry:

@Entity 
public class LogEntry extends AbstractEntity { 

    @ManyToOne 
    @JoinColumn 
    protected AbstractEntity abstractEntity; 
    @ManyToOne 
    @JoinColumn 
    protected Person person; // creator or updater 
    @Column(updatable=false, insertable=false, columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP") 
    @Temporal(TemporalType.TIMESTAMP) 
    protected Date changeDate; 
    protected String comment; 
    //getter and setter 
} 

Il mio approccio a questo è stato quello di creare un nuovo oggetto LogEntry e aggiungerlo alla lista LogEntry dell'entità prima che un'entità ottenuto aggiornato o persisteva .

Ho provato le seguenti soluzioni:

  • Uso delle annotazioni callback (@PreUpdate, @PrePersist ecc) direttamente in classe entità o separato in un ascoltatore entità collegata con AbstractEntity
  • Uso DescriptorEvent e di EclipsLink la corrispondenti metodi di callback in un listener di entità. Questo è stato il processo più promettente. In preUpdate potrei aggiungere un nuovo LogEntry all'oggetto interessato. Il LogEntry aggiunto è stato persino mantenuto correttamente, ma preUpdate verrà richiamato da qualsiasi operazione di database (selezionare porta anche al richiamo di preUpdate), quindi non posso differire tra un oggetto modificato e un oggetto senza modifiche. I changeset forniti dall'evento descrittore, dalla query correlata o da unitOfWork sono in ogni caso nulli. Un confronto tra l'oggetto corrente e il vecchio oggetto (fornito dall'evento descrittore) è IMHO troppo complesso, non è vero? D'altra parte, in preUpdateWithChanges potrei facilmente rilevare un'entità modificata, ma a questo punto è apparentemente troppo tardi per aggiungere un log-in. La logentry non verrà mantenuta.

Quasi ognuna di queste prove mi consente di modificare gli attributi (come nome o validTo) dell'entità interessata. Ma nessuna soluzione offre l'opportunità di creare una nuova istanza di LogEntry, o piuttosto di mantenere questa istanza di LogEntry. Ho anche cercato di ottenere un'istanza di un bean di sessione tramite jndi lookup per mantenere manualmente LogEntry. La ricerca jndi funziona, ma chiamare i metodi di creazione o aggiornamento del bean di sessione non ha alcun effetto.

Il mio attuale ascoltatore entità simile a questa:

public class ChangeListener extends DescriptorEventAdapter { 

    @Override 
    public void preUpdate(DescriptorEvent event) { 
     AbstractEntity entity = (AbstractEntity) event.getObject(); 
     if (!(entity instanceof LogEntry)) { 
      LogEntry logEntry = new LogEntry(); 
      logEntry.setPerson(getSessionController().getCurrentUser()); 
      logEntry.setAbstractEntity(entity); 
      entity.getLogEntries().add(logEntry); 
     } 
    } 
} 

Hibernate Envers è per vari motivi alcuna opzione.

La versione di EclipseLink è 2.3.2.

risposta

7

Il nuovo oggetto persistente durante gli eventi dal processo di commit può essere difficile.

È possibile ottenere le modifiche prima del commit e mantenere i registri (ottenere il set di modifiche da UnitOfWork).

See, http://wiki.eclipse.org/EclipseLink/FAQ/JPA#How_to_access_what_changed_in_an_object_or_transaction.3F

Altrimenti, è possibile inserire e oggetto direttamente dall'interno di un evento.

cioè

event.getSession().insertObject(logEntry); 
+2

Considerare anche il supporto della cronologia di EclipseLink, http://wiki.eclipse.org/EclipseLink/Examples/JPA/History – James

3

Temporaneamente ho risolto questo problema confrontando l'entità corrente e il vecchio entità PreUpdate. Il confronto viene effettuato con EqualsBuilder da Apache Commons Lang.

public class ChangeAbstractEntityListener extends DescriptorEventAdapter { 

    @Override 
    public void preUpdate(DescriptorEvent event) { 
     AbstractEntity originalEntity = (AbstractEntity) event.getOriginalObject(); 
     AbstractEntity entity = (AbstractEntity) event.getObject(); 

     if (!EqualsBuilder.reflectionEquals(originalEntity, entity, true)) { 
      if (!(entity instanceof LogEntry)) { 
       LogEntry logEntry = new LogEntry(); 
       logEntry.setPerson(getSessionController().getCurrentUser()); 
       logEntry.setAbstractEntity(entity); 
       entity.getLogEntries().add(logEntry); 
      } 
     } 
    } 
    //jndi lookup to get the user object 
} 
-1

Se volete semplice revisione entità, quindi non guardare oltre il modulo Envers di Hibernate.

Funziona con JPA (e Spring-Data), e può memorizzare ogni versione modificata insieme a chi ha cambiato se si esegue dire Spring Security.

Envers fa parte di Hibernate dal 3,6

0

Si può provare Tracking Changes Using History Policy

EclipseLink fornisce supporto esteso per il monitoraggio di tutte le modifiche apportate al database. EclipseLink HistoryPolicy può essere configurato su ClassDescriptor per memorizzare una tabella mirror dell'originale che memorizzerà lo stato dell'oggetto in qualsiasi momento. Questo può essere usato per scopi di controllo, o per consentire l'interrogazione come di punti passati nel tempo, o per consentire il ripristino di vecchi dati.

Problemi correlati