2010-07-01 13 views
13

Otrzymuję ten wyjątek w kontrolerze aplikacji WWW opartej na wiosennej strukturze przy użyciu hibernacji. Próbowałem na wiele sposobów, aby temu przeciwdziałać, ale nie mogłem go rozwiązać.Wiersz StaleObjectstateException został zaktualizowany lub usunięty przez

W metodzie sterownika, handleRequestInternal, są wywoływane do bazy danych głównie dla "czytaj", chyba że jest to akcja przesyłania. Używam Spring sesji, ale przeniósł się do getHibernateTemplate() i problem nadal pozostaje.

zasadniczo to drugie wywołanie bazy danych zgłasza ten wyjątek. To jest:

1) getEquipmentsByNumber(number) {pierwsze będący urządzeniem jest pobierany z DB w oparciu o „numer”, który ma listę właściwości, a każda posiada listę wartości. I pętli tych wartości (prymitywne obiekty Struny) do odczytu w zmiennych)

2) getMaterialById(id) {pobiera materiały w oparciu o ID}

Rozumiem, że drugie wezwanie, najprawdopodobniej, czyni sesję do "flush", ale ja tylko "czytam" obiekty, to dlaczego drugie wywołanie rzuca wyjątek stanu przestarzałych obiektów na własności Equipment, jeśli nic się nie zmieni?

Nie mogę wyczyścić pamięci podręcznej po wywołaniu, ponieważ powoduje ona LazyExceptions na obiektach, które przekazuję do widoku.

Przeczytałem: https://forums.hibernate.org/viewtopic.php?f=1&t=996355&start=0 , ale nie udało się rozwiązać problemu w oparciu o dostarczone sugestie.

Jak mogę rozwiązać ten problem? Wszelkie pomysły i przemyślenia są doceniane.

UPDATE: Co ja właśnie testowane jest to, że w funkcji getEquipmentsByNumber() po przeczytaniu zmiennych z listy właściwości, mogę to zrobić: getHibernateTemplate().flush(); i teraz wyjątek na tej linii, a następnie wezwanie do pobierania materiału (czyli to getMaterialById(id)).

UPDATE: Przed jawne wywołanie równo, jestem usunięcie obiektu z pamięci podręcznej sesji tak, że nie zestarzeć obiekt pozostaje w pamięci podręcznej.

getHibernateTemplate().evict(equipment); 
getHibernateTemplate().flush(); 

OK, więc teraz problem został przeniesiony do następnego sprowadzić z DB po Zrobiłem to. Przypuszczam, że muszę oznaczać metody jako zsynchronizowane i eksmitować obiekty, jak tylko skończę czytać ich zawartość! to nie brzmi zbyt dobrze.

UPDATE: Wykonane metodą handleRequestInternal "zsynchronizowane". Błąd zniknął. Oczywiście, nie najlepsze rozwiązanie, ale co robić! Próbowano w handleRequestInternal zamknąć bieżącą sesję i otworzyć nową. Ale spowoduje to, że inne części aplikacji nie będą działać poprawnie. Próbowałem użyć ThreadLocal, który też nie działał.

+1

jeśli mógłbyś opublikować kod metody, która rzuca wyjątek, przyjrzę się temu dalej. Brzmi trochę podejrzanie – walnutmon

Odpowiedz

0

Ten problem był czymś, co przeżyłem i było dość frustrujące, chociaż w twoich rozmowach DAO/Hibernate musi być coś dziwnego, ponieważ jeśli robisz odnośnik przez ID, nie ma powodu, aby uzyskać stan nieaktualny, ponieważ jest to po prostu proste wyszukiwanie obiektu.

Po pierwsze, upewnij się, że wszystkie metody są opatrzone @Transaction(required=true) // you'll have to look up the exact syntax

Jednakże wyjątek ten jest zwykle wyrzucane podczas próby wprowadzania zmian do obiektu, który został odłączony od sesji został pobrany z. Rozwiązanie tego problemu często nie jest proste i wymagałoby opublikowania więcej kodu, abyśmy mogli dokładnie zobaczyć, co się dzieje; moją ogólną sugestią byłoby stworzenie @Service, który wykonuje tego rodzaju rzeczy w ramach jednej transakcji

+0

Dzięki za sugestię. To, co właśnie przetestowałem, polega na tym, że w funkcji getEquipmentsByNumber po wczytaniu zmiennych z listy właściwości robię to: getHibernateTemplate(). Flush(); a teraz wyjątek znajduje się w tej linii, a nie w wywołaniu pobierania materiału (to jest getMaterialById (id)). Nie używam adnotacji, ponieważ nie jestem do nich zbyt dobrze zaznajomiony. – Saky

2

Oto 3 możliwości (ponieważ nie wiem dokładnie, jakiego rodzaju obsługa sesji hibernacji używasz). Dodaj jeden po drugim i przetestuj:

Użyj odwzorowania dwukierunkowego z inverse=true między obiektem nadrzędnym a obiektem podrzędnym, aby zmiana w elemencie nadrzędnym lub podrzędnym została prawidłowo przeniesiona na drugi koniec relacji.

Dodanie obsługi optymistyczne Blokowanie pomocą TimeStamp lub Version kolumna

Zastosowanie przyłączenia dalszych pobrać cały wykres obiekt [+ dzieci] ze sobą, aby uniknąć całkowicie drugie połączenie.

Wreszcie, wtedy i tylko wtedy, gdy nic nie działa: obciążenia rodzic ponownie Id (masz już) i wypełnić dane zmodyfikowane następnie zaktualizować.

Życie będzie dobrze! :)

+0

Myślę, że sugestia użycia odwrotności jest prawdopodobnie myląca w kontekście propagacji zmian z rodzica na dziecko. W tym przypadku faktycznym zachowaniem do skonfigurowania jest "kaskada". Odwrotna koncepcja jest ortogonalna do kaskadowania – Shailendra

3

Też borykałem się z tym wyjątkiem, ale gdy to się powtarzało, nawet gdy kładę zamek na obiekcie (i w środowisku testowym, gdzie wiedziałem, że byłem jedynym procesem dotykającym obiektu), Postanowiłem nadać nawiasowi w stosie ślad, który należy wziąć pod uwagę.

org.hibernate.StaleObjectStateException: Wiersz został zaktualizowany lub usunięty przez innej transakcji (lub mapowanie niezapisane wartość była błędna): [com.rc.model.mexp.MerchantAccount # 59132]

W naszym przypadku okazało się, że odwzorowanie mylił; mieliśmy type="text" w mapowaniu na jednym polu, które było MEDIUMTEXT typ w bazie danych, a wydaje się, że Hibernate naprawdę nienawidzi, że przynajmniej w pewnych okolicznościach. Usunęliśmy specyfikację typu z odwzorowania dla tego pola, a problem został rozwiązany.

Teraz dziwne jest to, że w naszym środowisku produkcyjnym, ze podobno problematycznej mapowania w miejscu, my NIE otrzymuję ten wyjątek. Czy ktokolwiek ma pojęcie, dlaczego tak się dzieje? Jesteśmy przy użyciu tej samej wersji MySQL - „5.0.22-log” (nie wiem co „-log” znaczy) - w dev i produkcyjnych envs.

5

Jesteś mis-using Hibernate w jakiś sposób, że powoduje to, aby myśleć, że jesteś aktualizowania lub usuwanie obiektów z bazy danych.

Dlatego dzwoniąc flush() rzuca wyjątek.

Jedna z możliwości: niepoprawnie "dzielisz" sesję lub encje, za pośrednictwem pól lub pól członka serwletu lub kontrolera. To jest główny powód, dla którego "zsynchronizowany" zmieniłby twoje symptomy błędu. Krótkie rozwiązanie: nigdy tego nie rób. Sesje i podmioty nie powinny działać w ten sposób - każde żądanie powinno być przetwarzane niezależnie.

Inna możliwość: unsaved-value domyślnie 0 dla "int" pól PK. Możesz być w stanie wpisać je jako "Integer", jeśli naprawdę chcesz użyć 0 jako prawidłowej wartości PK.

Trzecia propozycja: wykorzystanie Hibernate Session wyraźnie, nauczyć się pisać proste poprawny kod, który działa, a następnie załadować źródło Java dla bibliotek Hibernacja/wiosna więc można przeczytać & zrozumieć, co te biblioteki są rzeczywiście robi dla Ciebie.

Powiązane problemy