A ma jednostkę JPA, która ma znacznik czasu i wyróżnia się złożonym polem identyfikatora. Potrzebuję zaktualizować znacznik czasu w jednostce, która już została zapisana, w przeciwnym razie utworzyć i zapisać nowy obiekt z bieżącym znacznikiem czasu.Utwórz nowy lub zaktualizuj istniejący podmiot za jednym razem, korzystając z wersji JPA
Jak się okazuje, zadanie nie jest tak proste, jak się wydaje od pierwszego wejrzenia. Problem polega na tym, że w środowisku współbieżnym pojawia się nieprzyjemny wyjątek "Unikatowy indeks lub naruszenie klucza głównego". Oto mój kod:
// Load existing entity, if any.
Entity e = entityManager.find(Entity.class, id);
if (e == null) {
// Could not find entity with the specified id in the database, so create new one.
e = entityManager.merge(new Entity(id));
}
// Set current time...
e.setTimestamp(new Date());
// ...and finally save entity.
entityManager.flush();
Należy zauważyć, że w tym przykładzie identyfikator jednostki nie jest generowany na insert, jest znany z góry.
Gdy dwa lub więcej wątków uruchomić ten blok kodu równolegle, mogą jednocześnie uzyskać null
z entityManager.find(Entity.class, id)
wywołanie metody, więc starają się zaoszczędzić dwa lub więcej podmiotów, w tym samym czasie, z tym samym identyfikatorem wyniku błędu .
Myślę, że istnieje niewiele rozwiązań tego problemu.
- Oczywiście mogę zsynchronizować ten blok kodu z globalną blokadą, aby zapobiec równoczesnemu dostępowi do bazy danych, ale czy byłby to najbardziej wydajny sposób?
- Niektóre bazy danych obsługują bardzo przydatne instrukcje
MERGE
, które aktualizują istniejący lub tworzą nowy wiersz, jeśli żaden nie istnieje. Ale wątpię, że OpenJPA (moja implementacja JPA) obsługuje to. - Zdarzenie, jeśli JPA nie obsługuje SQL MERGE, zawsze mogę wrócić do zwykłego starego JDBC i zrobić, co chcę z bazą danych. Ale nie chcę zostawiać wygodnego API i bałaganu z włochatą kombinacją JDBC + SQL.
- Istnieje magiczna sztuczka, aby naprawić go za pomocą standardowego interfejsu API JPA, ale jeszcze go nie znam.
Proszę o pomoc.
Oba odnośniki są zepsute – Sergey