2013-03-28 24 views
5

Próbuję rejestrować wszelkie zmiany moich jednostek JPA. Z tego powodu każda jednostka dziedziczy z abstrakcyjnej klasy encji, która ma listę obiektów LogEntry.Zmiany śledzenia EclipseLink JPA

klasa AbstractEntity: Klasa

@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 
} 

Moje podejście było to, aby utworzyć nowy obiekt logentry i dodać go do listy logentry jednostki przed podmiot został zaktualizowany lub utrzymywały .

I wypróbowane następujące rozwiązania:

  • Stosując opisów zwrotnych (@PreUpdate, @PrePersist) bezpośrednio w klasie jednostki lub oddzielone na detektor jednostki związanej z AbstractEntity
  • Stosując EclipsLink w DescriptorEvent i tym odpowiednie metody wywołania zwrotnego w detektorze encji. To była najbardziej obiecująca próba. W PreUpdate mogłem dodać nowy LogEntry do obiektu, którego dotyczy luka. Dodany LogEntry został nawet poprawnie zachowany, ale preUpdate zostanie wywołany przez dowolną operację bazy danych (wybierz także prowadzi do wywołania funkcji PreUpdate), więc nie mogę różnić się między zmienionym obiektem a obiektem bez żadnych zmian. Dostarczone zestawy zmian ze zdarzenia deskryptora, kwerendy pokrewnej lub jednostki unitOfWork są w każdym przypadku zerowe. Porównanie bieżącego obiektu i starego obiektu (dostarczone przez zdarzenie deskryptora) to zbyt skomplikowane IMHO, nieprawdaż? Z drugiej strony, w preUpdateWithChanges mogłem z łatwością wykryć zmieniony byt, ale w tym momencie najwyraźniej jest już za późno na dodanie logentry. Logentry nie będzie trwało.

Prawie każda z tych prób umożliwia zmianę atrybutów (takich jak nazwa lub validTo) dotkniętej jednostki. Ale żadne rozwiązanie nie daje możliwości utworzenia nowej instancji LogEntry, a raczej utrzymywania tej instancji LogEntry. Próbowałem również uzyskać wystąpienie komponentu bean sesji za pośrednictwem wyszukiwania Jndi, aby ręcznie zachować LogEntry. Sprawdzanie jndi działa, ale wywołanie metod tworzenia lub aktualizacji komponentu bean sesji nie ma żadnego efektu.

Mój obecny podmiot słuchacz wygląda następująco:

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 jest z różnych powodów nie ma opcji.

Wersja EclipseLink to 2.3.2.

Odpowiedz

7

Utrzymywanie nowego obiektu podczas zdarzeń z procesu zatwierdzania może być trudne.

Możesz uzyskać zmiany przed zatwierdzeniem i kontynuować logowanie (pobierz zestaw zmian z UnitOfWork).

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

W przeciwnym razie możliwe jest bezpośrednio wstawić i obiekt od wewnątrz zdarzenia.

tj

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

Weź również pod uwagę wsparcie historii EclipseLink, http://wiki.eclipse.org/EclipseLink/Examples/JPA/History – James

3

Tymczasowo I rozwiązać ten problem poprzez porównanie bieżącej jednostki i starą podmiot preUpdate. Porównanie odbywa się z EqualsBuilder z 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

Jeśli chcesz przeprowadzić prostą inspekcję jednostek, nie szukaj dalej niż moduł Envers Hibernacji.

Działa z JPA (i Spring-Data) i może przechowywać każdą zmienioną wersję wraz z , która zmieniła ją, jeśli uruchomisz polecenie Spring Security.

Envers jest częścią Hibernate od 3,6

0

Można spróbować Tracking Changes Using History Policy

EclipseLink zapewnia rozszerzoną obsługę dla śledzenia wszystkich zmian wprowadzonych do bazy danych. Historia EclipseLink HistoryPolicy może zostać skonfigurowana na ClassDescriptor do przechowywania lustrzanej tabeli oryginału, która będzie przechowywać stan obiektu w dowolnym momencie. Może to być wykorzystywane do celów audytu lub do wysyłania zapytań od przeszłych punktów w czasie lub do przywracania starych danych.