Przechowywanie historii zmian

0

Hej
Mam taką encję i odpowiadającą jej tabelę w bazie danych


@Entity
@Table(name = "Bands")
public class Band {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "band_generator")
    @SequenceGenerator(name = "band_generator", sequenceName = "band_sequence", allocationSize = 1)
    private Integer id;

    @Size(min = 3, message = "The band name must be at least 3 characters long")
    private String name;

    private String country;

    private String description;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "band")
    @OrderBy("released")
    private List<Album> albums = new ArrayList<>();

    @ManyToMany(mappedBy = "bands")
    private Set<Musician> musicians = new HashSet<>();

    // kontruktor + getery, setery

Encje mogą być modyfikowane (aktualizowane PUTem).
Chciałbym jakoś przechowywać historię zmian danego obiektu, tzn. chciałbym mieć kilka wersji ale żeby było wiadomo że to jeden, ten sam, obiekt. Jest jakiś standardowy pattern jak się robi takie rzeczy?

6

Generalnie najlepiej do tego to nie używać bazy SQL.
A jak już SQL to najlepiej w ogóle nie przechowywać obiektów (stan) tylko eventy.

Ale jak już musisz SQL i stan (zamiast eventów) i jak już musisz JPA / Hibernate to zobacz na https://hibernate.org/orm/envers/

albo użyj envers, albo przypatrz się jak jest zrobione (w sensie składowania danych)

4

@Adam Wasiur:

Zaraz polegniesz na etapie JSON-a z takimi encjami (to tak na marginesie).
Jedna ta sama encja bazodanowa JPA przepychana od bazy po frontend, to jest bardzo zły projekt

0

Chyba źle zadałem pytanie, boe ta encja JPA to tylko przykład

Jestem na etapie nauki Springa i tych wszystkich kompenentów Javowych do robienia aplikacji backendowej, to zrobiłem encję JPA

Jakbym nie używał JPA albo pisał w C# czy innym języku to jak się robi takie przechowywanie historii zmian?

1

@Adam Wasiur:

Generalnie SQL-e jako standard nie za bardzo mają coś n/t historii, ale np Postgress coś miał (nic nie pamiętam, wieki temu)

Jak MUSI być przenośny SQL, to trzeba zbudować jakieś siostrzane tabele itd... da się, ale nie ma gotowego samego z siebie

Oddzielne pytanie, jak to przełożyć na design obiektowy.
Na dziesiątki sposobów można wyrazić związek miedzy aktualną wartością obiektu, jego historią, tożsamością czy to nadal ten sam obiekt, wglądem w chwilę 't' w przeszłości

1

Ogolnie to co teraz opisze jest duzo bardziej skomplikowane i raczej bedziesz musial sie postarac zeby to zaimplementowac. (Chociaz moze nawet nie. Po prostu to o czym mysle mialo troche wiecej funkcji niz audyt/historia)

  • w bazie oprocz samych kolumn encji trzymasz jeszcze cos w rodzaju effectiveFrom, effectiveTo
  • w momencie aktualizacji robisz "kopie wiersza". Stary wiersz ustawiasz na effectiveTo = now a w nowym wierszu effectiveFrom = now, effectiveTo = null
  • zapytanie zamiast wygladac tak: get(id), bedzie wygladac tak: get(id, at) (potencjalnie get(id, at=now)). Czyli zamiast SELECT ... where id = $id masz SELECT ... where id = $id and effectiveFrom <= $at and (effectiveTo is null or effectiveTo > $at)

Albo jakis event sourcing

49

Odnośnie samego sql'a i historyzacji danych to możesz zapoznać się z https://sqlplayer.net/2018/01/slowly-changing-dimension-scd/

0

Wszystko zależy od dostępności - tj. czy wszystkie dane muszą faktycznie leżeć w bazie danych, a nie czy w innym miejscu.

Ogólnie to w życiu widziałem kilka rozwiązań, tj.:

  • ręcznie pisane triggery, które wrzucały stare dane do tabel historycznych
  • Debezium
  • trzymanie zmian na Kafce, tj. przy próbie zmian leciała wiadomość na kolejkę z informacją o PUT, a taką kolejkę można było przeszukiwać przy pomocy Presto

Rozwiązanie z Kafką było najprzyjemniejsze przy pracy. Natomiast muszę przyznać, że akurat wszystkie trzy mechanizmy działały.

1 użytkowników online, w tym zalogowanych: 0, gości: 1