Hibernacja - aktualizacja wsadowa zwróciła nieoczekiwaną liczbę wierszy od aktualizacji: 0 rzeczywista liczba wierszy: 0 oczekiwano: 1

141

Otrzymuję następujący błąd hibernacji. Potrafię zidentyfikować funkcję, która powoduje problem. Niestety funkcja zawiera kilka wywołań DB. Nie mogę znaleźć linii, która powoduje problem, ponieważ hibernacja opróżnia sesję pod koniec transakcji. Poniższy błąd hibernacji wygląda na błąd ogólny. Nie wspomniano nawet, która fasola powoduje problem. Czy ktoś zna ten błąd hibernacji?

org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
        at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:93)
        at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:195)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:584)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransacti
onManager.java:500)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManag
er.java:473)
        at org.springframework.transaction.interceptor.TransactionAspectSupport.doCommitTransactionAfterReturning(Transaction
AspectSupport.java:267)
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:106)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:170)
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:176)
Sujee
źródło
Dziękuję @Peter Mortensen. Zaktualizowałem swój e-mail.
Sujee
Mam ten sam problem. Nie jest to duży problem, ponieważ zdarza się to bardzo rzadko. Używanie show_sql nie jest praktyczne, ponieważ odtworzenie tego zachowania wymaga milionów transakcji. Jednakże, ponieważ zdarza się to wielokrotnie podczas testu systemu, który przeprowadzam (który ma miliardy transakcji), podejrzewam, że istnieje konkretny powód.
daniel_or_else
Napotkałem ten problem, próbując zaktualizować wiersze, które nie mają żadnych zmian. Nie aktualizuj niczego, jeśli nie ma różnicy.
fifman

Odpowiedzi:

62

Bez kodu i mapowań dla Twoich transakcji zbadanie problemu będzie prawie niemożliwe.

Aby jednak lepiej zrozumieć, co powoduje problem, spróbuj wykonać następujące czynności:

  • W konfiguracji hibernacji ustaw hibernate.show_sql na true. Powinno to pokazać, jaki kod SQL jest wykonywany i powoduje problem.
  • Ustaw poziomy logów dla Spring i Hibernate na DEBUG, to znowu da ci lepsze pojęcie, która linia powoduje problem.
  • Utwórz test jednostkowy, który powiela problem bez konfigurowania menedżera transakcji w Spring. Powinno to dać lepsze wyobrażenie o nieprawidłowym wierszu kodu.

Mam nadzieję, że to pomoże.

beny23
źródło
21
hibernate.show_sql> Radziłbym raczej ustawić kategorię dziennika org.hibernate.SQL poziom na DEBUG. W ten sposób nie musisz modyfikować konfiguracji hibernacji tylko do logowania.
Thierry
Użycie „show_sql” może być bardzo rozwlekłe i niepraktyczne w produkcji. W tym celu zmodyfikowałem klauzulę catch w linii 74 klasy BatchingBatcher, aby wypisała instrukcję z ps.toString (), tak aby miała tylko instrukcję, której dotyczy problem.
Orden
71

Wystąpił ten sam wyjątek podczas usuwania rekordu według identyfikatora, który w ogóle nie istnieje. Więc sprawdź, czy rekord, który aktualizujesz / usuwasz, faktycznie istnieje w DB

shreyas
źródło
12
Miałem ten problem, gdy usunąłem dziecko z relacji rodzic-dziecko, uratowałem rodzica (co usuwa dziecko), a następnie próbowałem również ręcznie usunąć dziecko.
dave thieben
To rozwiązało mój problem. Rekord nie istniał, a moja usługa wywoływała metodę updateAll (), podczas gdy w rzeczywistości musiała wywołać metodę createOrUpdateAll (). Dzięki.
Mital Pritmani
3
więc jak rozwiązać problem? Jeśli otrzymam rekord, a następnie go usunę; ale jeśli system usunie go już przed usunięciem, inna aplikacja zgłosi wyjątek.
Stony
@davethieben, to były dokładnie te informacje, których potrzebowałem. Nie zdawałem sobie sprawy, że relacja rodzic-dziecko trwała przy usuwaniu.
śnieżny
Rozwiązaniem jest użycie funkcji select do aktualizacji podczas pobierania rekordu, aby zablokować wiersz, aby nic innego nie mogło go usunąć przed wykonaniem.
Matthew Read
55

Rozwiązanie: W pliku odwzorowania Hibernate dla właściwości id, jeśli używasz jakiejkolwiek klasy generatora, dla tej właściwości nie należy jawnie ustawiać wartości za pomocą metody ustawiającej.

Jeśli jawnie ustawisz wartość właściwości Id, spowoduje to powyższy błąd. Zaznacz to, aby uniknąć tego błędu. lub Pojawia się błąd, gdy wspomnisz w pliku mapowania generator pola = "natywny" lub "przyrostowy", aw BAZIE DANYCH mapowana tabela nie jest automatycznie_incremented Rozwiązanie: Przejdź do swojej BAZY DANYCH i zaktualizuj tabelę, aby ustawić auto_increment

Rēda Biramanē
źródło
2
Całkowita racja. To musi być właściwa odpowiedź. Dzięki @ Rēda Biramanē
Kuldeep Verma
1
Jednak MOŻESZ ustawić wartość ID na „0”, a wtedy Hibernacja będzie mogła go nadpisać
forresthopkinsa
1
Dzięki! Nie wiem, dlaczego nie jest to najwyżej sklasyfikowana odpowiedź.
Stuart McIntyre,
18

Zdarzyło mi się to raz przez przypadek, kiedy przypisywałem określone identyfikatory do niektórych obiektów (testowanie), a potem próbowałem je zapisać w bazie danych. Problem polegał na tym, że w bazie danych istniała określona polityka dotycząca ustawiania identyfikatorów obiektów. Po prostu nie przypisuj identyfikatora, jeśli masz politykę na poziomie hibernacji.

cSn
źródło
15

W moim przypadku doszedłem do tego wyjątku w dwóch podobnych przypadkach:

  • W metodzie z adnotacją @Transactionalmiałem wywołanie innej usługi (z długim czasem odpowiedzi). Metoda aktualizuje niektóre właściwości jednostki (po metodzie jednostka nadal istnieje w bazie danych). Jeśli użytkownik dwukrotnie zażąda metody (za pierwszym razem uważa, że ​​nie działa) przy wychodzeniu z metody transakcyjnej po raz drugi, Hibernate próbuje zaktualizować podmiot, który zmienił już swój stan od początku transakcji. Gdy Hibernacja wyszukuje encję w stanie i znalazła tę samą jednostkę, ale została już zmieniona przez pierwsze żądanie, zgłasza wyjątek, ponieważ nie może zaktualizować jednostki. To jak konflikt w GIT.
  • Miałem automatyczne żądania (do monitorowania platformy), które aktualizowały jednostkę (i ręczne wycofywanie kilka sekund później). Ale ta platforma jest już używana przez zespół testowy. Kiedy tester wykonuje test w tej samej jednostce co automatyczne żądania (w tej samej setnej części milisekundy), otrzymuję wyjątek. Podobnie jak w poprzednim przypadku, wychodząc z drugiej transakcji, podmiot wcześniej pobrany już się zmienił.

Wniosek: w moim przypadku to nie był problem, który można znaleźć w kodzie. Ten wyjątek jest zgłaszany, gdy Hibernate stwierdzi, że jednostka pobrana z bazy danych jako pierwsza uległa zmianie podczas bieżącej transakcji , więc nie może jej opróżnić do bazy danych, ponieważ Hibernate nie wie, która wersja jednostki jest poprawna: ta bieżąca pobranie transakcji na początku; lub już zapisany w bazie danych.

Rozwiązanie: aby rozwiązać problem, będziesz musiał zagrać z Hibernate LockMode, aby znaleźć ten, który najlepiej odpowiada Twoim wymaganiom.

Sergio Lema
źródło
Cześć, dziękuję za pomocną odpowiedź. Czy mógłbyś poinformować, który LockMode ostatecznie wybrałeś, aby pozbyć się błędu. Mam podobny problem, gdy API zostaje trafione sukcesywnie w różnych milisekundach z powodu nieumyślnego dwukrotnego kliknięcia przez użytkownika. Pesymistyczne blokowanie szkodzi wydajności; więc będziesz zainteresowany, aby wiedzieć, do czego ostatecznie się zdecydowałeś?
Madhur Bhaiya
1
Minęło dużo czasu, odkąd miałem problem i nie pamiętam dobrze dokładnego rozwiązania, które zastosowałem, ale było to LockMode.READlub LockMode.OPTIMISTIC.
Sergio Lema
13

Właśnie napotkałem ten problem i dowiedziałem się, że usuwam rekord i próbuję go później zaktualizować w ramach transakcji Hibernate.

Julius
źródło
9

Może się to zdarzyć, gdy wyzwalacze wykonują dodatkowe zapytania DML (modyfikacja danych), które mają wpływ na liczbę wierszy. Moim rozwiązaniem było dodanie następującego kodu u góry mojego wyzwalacza:

SET NOCOUNT ON;
Panie TA
źródło
1
Ta odpowiedź skierowała mnie we właściwym kierunku. Pracowałem ze starszą bazą danych, która miała wyzwalacz - na szczęście zastępowałem to, co robił wyzwalacz, kodem, więc mogłem go po prostu usunąć.
S. Baggy,
2
+1 To też był mój problem. Używałem postgresql, więc musiałem użyć adnotacji @SQLInsert, aby wyłączyć sprawdzanie liczby wierszy: technology-ebay.de/the-teams/mobile-de/blog/…
s1mm0t
USTAW NOCOUNT OFF *
G. Ciardini
7

Miałem ten sam problem. Kod działał w środowisku testowym. Ale to nie działało w środowisku przejściowym.

org.hibernate.jdbc.BatchedTooManyRowsAffectedException: Batch update returned unexpected row count from update [0]; actual row count: 3; expected: 1

Problem polegał na tym, że tabela miała pojedynczy wpis dla każdego klucza podstawowego w testowej tabeli DB. Ale w pomostowej bazie danych było wiele wpisów dla tego samego klucza podstawowego. (Problem dotyczy przemieszczania bazy danych, tabela nie miała żadnych ograniczeń dotyczących klucza podstawowego, również było wiele wpisów).

Więc za każdym razem podczas operacji aktualizacji kończy się niepowodzeniem. Próbuje zaktualizować pojedynczy rekord i oczekuje, że liczba aktualizacji będzie wynosić 1. Ale ponieważ w tabeli były 3 rekordy dla tego samego klucza podstawowego, liczba aktualizacji wyników znajduje się na 3. Ponieważ oczekiwana liczba aktualizacji i rzeczywista liczba aktualizacji wyników nie zgadzają się , Zgłasza wyjątek i cofa się.

Po usunięciu wszystkich rekordów, które mają zduplikowany klucz podstawowy i dodałem ograniczenia klucza podstawowego. Działa dobrze.

Hibernate - Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1

rzeczywista liczba wierszy: 0 // oznacza brak rekordu do aktualizacji
update: 0 // oznacza brak rekordu, więc nie
oczekiwano żadnej aktualizacji : 1 // oznacza oczekiwany co najmniej 1 rekord z kluczem w tabeli db.

Tutaj problem polega na tym, że kwerenda próbująca zaktualizować rekord dla jakiegoś klucza, ale hibernacja nie znalazła żadnego rekordu z kluczem.

ParagFlume
źródło
Cześć @ParagFlume, mam ten sam błąd „Aktualizacja partii zwróciła nieoczekiwaną liczbę wierszy po aktualizacji: 0 rzeczywista liczba wierszy: 0 oczekiwana: 1” Kiedy próbuję wybrać jeden obiekt z mojej bazy danych, problem dotyczy tylko produkcji, czy masz jakąś pomysł, jak rozwiązać ten problem, jestem w krytycznej sytuacji. Pozdrowienia !
James,
Myślę, że zapytanie nie znalazło żadnego rekordu w bazie danych, oczekuje, że znajdzie jeden rekord w bazie danych, który próbuje zaktualizować. możesz sprawdzić ręcznie / query-browser, czy rekord faktycznie istnieje w bazie danych.
ParagFlume
tak, rekord istnieje, ale mój problem polega na tym, dlaczego hibernacja próbuje zaktualizować, używam tylko usługi wyboru, aby pobrać obiekt z bazy danych!
James,
być może próbujesz zmienić stan obiektów. a sesja jest nadal otwarta.
ParagFlume
jak mogę sprawdzić to behaivor, bo to nie ja rozwijam usługę ...
James,
5

Jak mówi Julius , dzieje się tak, gdy następuje aktualizacja obiektu, którego elementy podrzędne są usuwane. (Prawdopodobnie dlatego, że istniała potrzeba aktualizacji całego Obiektu Ojca i czasami wolimy usunąć dzieci i ponownie wstawić je do Ojca (nowe, stare nie ma znaczenia) wraz z wszelkimi innymi aktualizacjami, które ojciec mógł mieć na dowolnym z jego inne zwykłe pola) Więc ... aby to zadziałało, usuń elementy podrzędne (w ramach transakcji), wywołując childrenList.clear()(nie zapętlaj elementów podrzędnych i usuwaj je z niektórymi childDAO.delete(childrenList.get(i).delete()))i ustawieniami @OneToMany(cascade = CascadeType.XXX ,orphanRemoval=true)po stronie Ojca Obiekt. Następnie zaktualizuj ojca (fatherDAO.update (father)). (Powtórz dla każdego obiektu ojca) Rezultat jest taki, że dzieciom zrywa się więź z ojcem, a następnie są usuwane jako sieroty przez ramy.

George Papatheodorou
źródło
5

Napotkałem ten problem, gdy mieliśmy relację jeden-wiele.

W pliku mapowania hibernacji hbm dla wzorca, dla obiektu z ustawionym ustawieniem typu, dodano cascade="save-update"i działało dobrze.

Bez tego, domyślnie hibernacja próbuje zaktualizować nieistniejący rekord i zamiast tego wstawia.

Shiv
źródło
4

Może się to również zdarzyć, gdy spróbujesz UPDATEużyć KLUCZA PODSTAWOWEGO .

Marcel
źródło
3

Mam ten sam problem i zweryfikowałem, że może to nastąpić z powodu klucza podstawowego automatycznego zwiększania. Aby rozwiązać ten problem, nie wstawiaj wartości automatycznego zwiększania do zestawu danych. Wstaw dane bez klucza podstawowego.

MacDaddy
źródło
3

Innym sposobem na uzyskanie tego błędu jest sytuacja, gdy w kolekcji znajduje się pusty element.

Bryan Pugh
źródło
2

dzieje się tak, gdy próbujesz usunąć ten sam obiekt, a następnie ponownie zaktualizować ten sam obiekt, użyj tego po usunięciu

session.clear ();

Sumit Singh
źródło
2

Mnie też się to przytrafiło, bo miałem swój id jako Long i otrzymywałem z widoku wartość 0, a kiedy próbowałem zapisać w bazie otrzymałem ten błąd, to naprawiłem go ustawiając id na null.

Roberto Rodriguez
źródło
1

Napotkałem ten problem, gdy ręcznie rozpoczynałem i zatwierdzałem transakcje w ramach metody oznaczonej jako @Transactional. Rozwiązałem problem, wykrywając, czy aktywna transakcja już istniała.

//Detect underlying transaction
if (session.getTransaction() != null && session.getTransaction().isActive()) {
    myTransaction = session.getTransaction();
    preExistingTransaction = true;
} else {
    myTransaction = session.beginTransaction();
}

Następnie pozwoliłem Springowi zająć się zatwierdzeniem transakcji.

private void finishTransaction() {
    if (!preExistingTransaction) {
        try {
            tx.commit();
        } catch (HibernateException he) {
            if (tx != null) {
                tx.rollback();
            }
            log.error(he);
        } finally {
            if (newSessionOpened) {
                SessionFactoryUtils.closeSession(session);
                newSessionOpened = false;
                maxResults = 0;
            }
        }
    }
}
Gibado
źródło
1

Dzieje się tak, gdy zadeklarowałeś zarządzany komponent Bean JSF jako

@RequestScoped;

kiedy należy zadeklarować jako

@SessionScoped;

Pozdrowienia;

John Lopez
źródło
1

Otrzymałem ten błąd, gdy próbowałem zaktualizować obiekt o identyfikatorze, którego nie ma w bazie danych. Powodem mojego błędu było to, że ręcznie przypisałem właściwość o nazwie „id” do reprezentacji obiektu w formacie JSON po stronie klienta, a następnie podczas deserializacji obiektu po stronie serwera ta właściwość „id” nadpisała zmienną instancji ( zwany także „id”), który miał wygenerować Hibernate. Dlatego uważaj na kolizje nazw, jeśli używasz Hibernate do generowania identyfikatorów.

Soggiorno
źródło
1

Trafiłem też na to samo wyzwanie. W moim przypadku aktualizowałem obiekt, który nawet nie istniał, używająchibernateTemplate .

Właściwie w mojej aplikacji otrzymywałem obiekt DB do aktualizacji. Aktualizując jego wartości, przez pomyłkę zaktualizowałem również jego identyfikator i poszedłem dalej, aby go zaktualizować i natrafiłem na wspomniany błąd.

Używam hibernateTemplatedo operacji CRUD.

Kuldeep Verma
źródło
1

Po przeczytaniu wszystkich odpowiedzi nie znalazłem nikogo, kto mógłby mówić o odwrotnym atrybucie hibernacji.

Moim zdaniem powinieneś również sprawdzić w swoich relacjach mapując, czy odwrotne słowo kluczowe jest odpowiednio ustawione. Odwrotność kluczowe jest tworzone w celu określenia, która strona jest właścicielem, który ma utrzymywać relację. Procedura aktualizacji i wstawiania różni się w zależności od tego atrybutu.

Załóżmy, że mamy dwie tabele:

Principal_table , middle_table

w relacji jeden do wielu . Klasy mapowania hiberntacji to odpowiednio Principal i Middle .

Więc klasa Principal ma zbiór obiektów Middle . Plik mapowania XML powinien wyglądać następująco:

<hibernate-mapping>
    <class name="path.to.class.Principal" table="principal_table" ...>
    ...
    <set name="middleObjects" table="middle_table" inverse="true" fetch="select">
        <key>
            <column name="PRINCIPAL_ID" not-null="true" />
        </key>
        <one-to-many class="path.to.class.Middel" />
    </set>
    ...

Ponieważ inverse jest ustawione na „true”, oznacza to, że klasa „średnia” jest właścicielem relacji, więc klasa główna NIE ZAKTUALIZUJE relacji.

Tak więc procedurę aktualizacji można zaimplementować w następujący sposób:

session.beginTransaction();

Principal principal = new Principal();
principal.setSomething("1");
principal.setSomethingElse("2");


Middle middleObject = new Middle();
middleObject.setSomething("1");

middleObject.setPrincipal(principal);
principal.getMiddleObjects().add(middleObject);

session.saveOrUpdate(principal);
session.saveOrUpdate(middleObject); // NOTICE: you will need to save it manually

session.getTransaction().commit();

U mnie to zadziałało, ale możesz zasugerować kilka edycji, aby ulepszyć rozwiązanie. W ten sposób wszyscy będziemy się uczyć.

Luis Teijon
źródło
1

W naszym przypadku w końcu odkryliśmy główną przyczynę StaleStateException.

W rzeczywistości usuwaliśmy wiersz dwukrotnie podczas jednej sesji hibernacji. Wcześniej używaliśmy ojdbc6 lib i w tej wersji było to w porządku.

Ale kiedy zaktualizowaliśmy do odjc7 lub ojdbc8, dwukrotne usuwanie rekordów było wyjątkiem. W naszym kodzie był błąd, który powodował dwukrotne usuwanie, ale nie było to widoczne w ojdbc6.

Udało nam się odtworzyć za pomocą tego fragmentu kodu:

Detail detail = getDetail(Long.valueOf(1396451));
session.delete(detail);
session.flush();
session.delete(detail);
session.flush();

Po pierwszym uruchomieniu hibernacja przechodzi i wprowadza zmiany w bazie danych. Podczas hibernacji drugiego rzutu porównuje obiekt sesji z rzeczywistym rekordem tabeli, ale nie może go znaleźć, stąd wyjątek.

Sandeep Khantwal
źródło
1

Ten problem występuje głównie, gdy próbujemy zapisać lub zaktualizować obiekt, który został już pobrany do pamięci przez uruchomioną sesję. Jeśli pobrałeś obiekt z sesji i próbujesz zaktualizować bazę danych, może zostać zgłoszony ten wyjątek.

Użyłem session.evict (); aby najpierw usunąć pamięć podręczną przechowywaną w stanie hibernacji lub jeśli nie chcesz ryzykować utraty danych, lepiej wykonaj inny obiekt do przechowywania danych temp.

     try
    {
        if(!session.isOpen())
        {
            session=EmployeyDao.getSessionFactory().openSession();
        }
            tx=session.beginTransaction();

        session.evict(e);
        session.saveOrUpdate(e);
        tx.commit();;
        EmployeyDao.shutDown(session);
    }
    catch(HibernateException exc)
    {
        exc.printStackTrace();
        tx.rollback();
    }
Amit Mishra
źródło
1

Problem z Hibernacją 5.4.1 i HHH-12878

Przed wersją Hibernate 5.4.1 optymistyczne wyjątki błędów blokowania (np. StaleStateExceptionLub OptimisticLockException) nie zawierały instrukcji niepowodzenia.

Problem HHH-12878 został utworzony, aby ulepszyć Hibernację, tak aby podczas zgłaszania optymistycznego wyjątku blokującego PreparedStatementimplementacja JDBC była również rejestrowana:

if ( expectedRowCount > rowCount ) {
    throw new StaleStateException(
            "Batch update returned unexpected row count from update ["
                    + batchPosition + "]; actual row count: " + rowCount
                    + "; expected: " + expectedRowCount + "; statement executed: "
                    + statement
    );
}

Czas testowania

Utworzyłem BatchingOptimisticLockingTestw moim wysokowydajnym repozytorium Java Persistence GitHub, aby zademonstrować, jak działa nowe zachowanie.

Najpierw zdefiniujemy Postbyt, który definiuje @Versionwłaściwość, umożliwiając w ten sposób niejawny optymistyczny mechanizm blokujący :

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String title;

    @Version
    private short version;

    public Long getId() {
        return id;
    }

    public Post setId(Long id) {
        this.id = id;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public short getVersion() {
        return version;
    }
}

Włączymy przetwarzanie wsadowe JDBC przy użyciu następujących 3 właściwości konfiguracyjnych:

properties.put("hibernate.jdbc.batch_size", "5");
properties.put("hibernate.order_inserts", "true");
properties.put("hibernate.order_updates", "true");

Mamy zamiar stworzyć 3 Postpodmioty:

doInJPA(entityManager -> {
    for (int i = 1; i <= 3; i++) {
        entityManager.persist(
            new Post()
                .setTitle(String.format("Post no. %d", i))
        );
    }
});

A Hibernate wykona wstawianie wsadowe JDBC:

SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')
SELECT nextval ('hibernate_sequence')

Query: [
    INSERT INTO post (title, version, id) 
    VALUES (?, ?, ?)
], 
Params:[
    (Post no. 1, 0, 1), 
    (Post no. 2, 0, 2), 
    (Post no. 3, 0, 3)
]

Wiemy więc, że grupowanie JDBC działa dobrze.

Teraz powtórzmy optymistyczny problem z blokowaniem:

doInJPA(entityManager -> {
    List<Post> posts = entityManager.createQuery("""
        select p 
        from Post p
        """, Post.class)
    .getResultList();

    posts.forEach(
        post -> post.setTitle(
            post.getTitle() + " - 2nd edition"
        )
    );

    executeSync(
        () -> doInJPA(_entityManager -> {
            Post post = _entityManager.createQuery("""
                select p 
                from Post p
                order by p.id
                """, Post.class)
            .setMaxResults(1)
            .getSingleResult();

            post.setTitle(post.getTitle() + " - corrected");
        })
    );
});

Pierwsza transakcja wybiera wszystkie Postjednostki i modyfikuje pliktitle właściwości.

Jednak zanim pierwsze EntityManagerzostanie opróżnione, wykonamy drugie przejście za pomocąexecuteSync metody.

Druga transakcja modyfikuje pierwszą Post, więc jej wartość versionzostanie zwiększona:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - corrected', 1, 1, 0)
]

Teraz, gdy pierwsza transakcja spróbuje opróżnić EntityManager, otrzymamy OptimisticLockException:

Query:[
    UPDATE 
        post 
    SET 
        title = ?, 
        version = ? 
    WHERE 
        id = ? AND 
        version = ?
], 
Params:[
    ('Post no. 1 - 2nd edition', 1, 1, 0), 
    ('Post no. 2 - 2nd edition', 1, 2, 0), 
    ('Post no. 3 - 2nd edition', 1, 3, 0)
]

o.h.e.j.b.i.AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements

o.h.e.j.b.i.BatchingBatch - HHH000315: Exception executing batch [
    org.hibernate.StaleStateException: 
    Batch update returned unexpected row count from update [0]; 
    actual row count: 0; 
    expected: 1; 
    statement executed: 
        PgPreparedStatement [
            update post set title='Post no. 3 - 2nd edition', version=1 where id=3 and version=0
        ]
], 
SQL: update post set title=?, version=? where id=? and version=?

Tak więc, aby skorzystać z tego ulepszenia, musisz dokonać aktualizacji do wersji Hibernate 5.4.1 lub nowszej.

Vlad Mihalcea
źródło
0

Dzieje się tak, jeśli zmienisz coś w zestawie danych za pomocą natywnego zapytania sql, ale utrwalony obiekt dla tego samego zestawu danych jest obecny w pamięci podręcznej sesji. Użyj session.evict (yourObject);

abhi
źródło
0

Jeden z przypadków

SessionFactory sf=new Configuration().configure().buildSessionFactory();
Session session=sf.openSession();

UserDetails user=new UserDetails();

session.beginTransaction();
user.setUserName("update user agian");
user.setUserId(12);
session.saveOrUpdate(user);
session.getTransaction().commit();
System.out.println("user::"+user.getUserName());

sf.close();
vootla561
źródło
0

Miałem do czynienia z tym wyjątkiem i hibernacja działała dobrze. Próbowałem ręcznie wstawić jeden rekord za pomocą pgAdmin, tutaj problem stał się jasny. Zapytanie SQL wstawiające zwraca 0 wstawiania. i istnieje funkcja wyzwalająca, która powoduje ten problem, ponieważ zwraca wartość null. więc muszę tylko ustawić go na powrót nowy. iw końcu rozwiązałem problem.

mam nadzieję, że pomoże to każdemu ciału.

kamel2005
źródło
0

Otrzymałem ten błąd, ponieważ omyłkowo zamapowałem IDkolumnę za pomocąId(x => x.Id, "id").GeneratedBy.**Assigned**();

Problem rozwiązany za pomocą Id(x => x.Id, "id").GeneratedBy.**Identity**();

roylac
źródło
0

Dzieje się tak, ponieważ brakuje mi deklaracji identyfikatora w klasie fasoli.

Ganesh Giri
źródło
0

W moim przypadku wystąpił problem z bazą danych, ponieważ jeden z przechowywanych procesów zużywał cały procesor, powodując wysokie czasy odpowiedzi bazy danych. Po zabiciu problem został rozwiązany.

Amit
źródło