Spring Data JPA - saveAndFlush

0

Cześć, mam pewien kłopot, a mianowicie.. Mam pewien trigger na bazie, który przy zapisie klasy C sprawdza czy istnieje obiekt w bazie B o danym businessId. Tak więc postanowiłem zrobić to tak: zapisuję klasę A oraz B (Cascade.PERSIST) poprzez saveAndFlush() ze Spring Data JPA, a następnie zapisuję klasę C wcześniej wyciągając dane o klasie B - biorąc jej ID. Natomiast po dokładnym debugowaniu po linii saveAndFlush() nie mam jeszcze w bazie rekordów klasy A oraz B, przez co przy zapisie C i tak wywala się trigger, że brak jest takiego rekordu B o takim businessId - jak to rozwiązać? saveAndFlush() nie robi automatycznie commit'a do bazy? Rozegrać to jakoś @Transactional? Jeśli tak to jak? Niżej jak to mniej więcej wygląda u mnie i co chcę osiągnąć. Przecież saveAndFlush() w implementacji swojej ma domyślnie @Transactional nad sobą, a więc powinien iść commit...

class A {
    @OneToMany(CascadeType.PERSIST)
    List<B> classesB;

    addB(B b) {
        classesB.add(b);
        b.setA(this);
    }
}
class B {
    @ManyToOne
    A classA;

    @OneToMany(CascadeType.PERSIST)
    List<C> classesC;
}
class C {
    @ManyToOne //businessId - nie jest to PK
    Long classBId;
}
class Service {
    save() {
        A a = Amapper.map();
        aRepository.saveAndFlush(a);//Tutaj zapisuje A i B
        B b = bRepository.findById();
        C c = Cmapper.map(b);
        cRepository.save(c);
    }
}
class Amapper {
    map() {
        //settery
        A a = new A();
        Bmapper bMapper = new Bmapper();
        B b = Bmapper.map();
        a.addB(b)
    }
}
class Cmapper {
    map(B b) {
        C c = new C();
        c.setClassBId(b.getId());
    }
}
2

Według mnie to przyczyna problemu została przez Ciebie zgubiona przy przepisywaniu na ten uproszczony kod. Tu nie widzę błedu, poza tym, że na 100% to się nie ma prawa skompilować nawet.
Zobacz jakie jest id tego b i wydrukuj.
W ogóle nie wiadomo po co robisz to saveAndFlush.

  1. albo wywal save and flush i walnij @Transactional na save
  2. albo nie używaj JPA - szkoda życia na takie problemy.

(Tak czy siak - sądze, że problem jest w tej części kodu, której nie podałeś).

0

Bardzo możliwe, że pisząc to z pamięci o czymś zapomniałem, sprawdzę to jeszcze w pracy rano, choć tam też to wyglądało jakby miało działać. Chętnie bym nie używał JPA, tak jak triggerow w bazie, ale niestety.

Dzięki za pomoc na obecną chwilę, przynajmniej wiem, że te fragmenty kodu działają.

0

Na upartego można zrobić to wszystko jawnie i zrobić taki myk:

EntityManager entityManager = ... //pobierz go/wstrzyknij jako zależność, wyczaruj
entityManager.getTransaction().begin();
entityManager.merge(a); // wstaw a+b
entityManager.merge(c);
entityManager.getTransaction().commit();

i cały ten pierdzielnik wrzucić w jakieś CustomRepo aby to ukryć. Powyższe rozwiązanie to jechanie po bebechach więc jest takie se.

2

Pamiętaj jeszcze o tym, że jak debugujesz transakcje to czytanie z bazy danych w ty momencie nie ma sensu - w normalnych warunkach twoje dane będą widoczne dopiero po commit. Niby oczywiste, a już ileś razy widziałem jak ktoś tracił przez to czas i krzyczął, że cuda się dzieją...

0

Commit zachodzi na koniec transakcji, dobrze pamiętam? Czy Transactional tego nie zapewnia?

0

Zapewnia, o ile zadziała. Jest troche powodow, dzięki ktorym może nie zadziałać, albo zadziała w innym miejscu niż się spodziewasz.

0

Słyszałem o tych przypadkach na prezentacjach, więc raczej to nie to. Jutro jeszcze z chłodną głową sprawdzę czy to nie jest jakiś głupi błąd.

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