2013-03-15 6 views
8

Mam do czynienia z dość złożonym wykresem obiektów w mojej bazie danych. Używam XStream do serializacji i deserializacji tego wykresu obiektów, który działa dobrze. Kiedy importuję wykres obiektów obiektu istniejącego w bazie danych, jest on początkowo przejściowy, ponieważ nie ma żadnych identyfikatorów i hibernacja nic o nim nie wie. Następnie mam logikę biznesową, która ustawia identyfikatory na częściach mojego wykresu obiektów, poprzez ustalenie, które obiekty w nowo transjentowanej mapie obiektów importowanych do istniejących obiektów trwałych. Następnie używam funkcji scalania Hibernate() i saveOrUpdate().Użycie elementu Transient w hibernacji do aktualizacji/scalenia istniejącego obiektu trwałego

Niektóre pseudokod dać lepsze wyobrażenie o tym, co robię:

ComplexObject transObj = xstream.import("object.xml"); 
ComplexObject persistObj = someService.getObjByName(transObj.getName()); 
for (OtherObject o : c.getObjects()) { 
    if (persistObj.getObjects().contains(o.getName())) { 
     o.setId(persistObj.getObjectByName(o.getName()).getId()) 
    } 
    ... set a bunch of other IDs deeper in the object graph ... 
} 

transObj = session.merge(transObj); 
session.saveOrUpdate(transObj); 

Teraz to nie działa tak jak pojawiają się błędy takie jak:

org.springframework.dao.InvalidDataAccessApiUsageException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.......SomeObject#353296]; nested exception is org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.......SomeObject#353296] 

i wydaje się, Łączenie hibernacji nie było przeznaczone do powiązania obiektów przejściowych z trwałymi.

Czy jest jakiś sposób osiągnięcia tego, co chcę zrobić, bez konieczności uzyskiwania trwałego obiektu w sesji i modyfikowania go, zamiast modyfikowania przejściowego, i próby zapisania tego i zastąpienia istniejącego trwałego?

+1

To jak bym rozwiązał go: http://stackoverflow.com/questions/4779239/update-persistent-object-with-transient-object-using-hibernate - ze względu na wydajność zalecana albo cache dane odbicie/introspekcja lub utworzyć generator klasy od twoich jednostek. – user1050755

+0

Posiadanie setId jest zawsze złym pomysłem ...? – Rob

+0

user1050755, jedyną rzeczą w tym rozwiązaniu jest to, że nie jest w stanie poradzić sobie z skomplikowanymi kaskadami, jeden-do-wielu, itd. Myślę, że to działałoby tylko dla struktury nie-graficznej, ale popraw mnie jeśli 'Mylę. – eipark

Odpowiedz

8

wydaje się hibernacji seryjnej nie miało kojarzenia przejściowe przedmiotów utrzymującym

scalania JPA Standard - łączy „nowe & oderwać przykłady podmiot” i „(utrzymywanie się od kontekstu) prowadzonego wystąpienia instancji ". Kaskada w relacjach FK oznaczonych jako Cascade.MERGE lub Cascade.ALL.

Z perspektywy JPA - nie, scalanie nie było przeznaczone do powiązania strumieniowych obiektów przejściowych Xstream z trwałymi. JPA zamierza scalać do pracy z normalnym cyklem życia JPA - tworzyć nowe obiekty, utrzymywać je, znajdować/znajdować, odłączać, modyfikować (w tym dodawać nowe obiekty), scalać je, opcjonalnie modyfikować je jeszcze raz, a następnie utrwalać/zapisywać . Jest to celowy projekt, tak aby JPA był przesyłany strumieniem & performant - każdy pojedynczy obiekt utrzymywany w bazie danych nie musi być poprzedzony pobieraniem stanu obiektu w celu określenia, czy/co wstawić/zaktualizować. Stan obiektu kontekstu utrwalania JPA zawiera już wystarczająco dużo szczegółów, aby to ustalić.

Twoim problemem jest to, że masz nowe instancje encji, które mają działać jakby zostały one indywidualny - a to nie jest droga JPA.

SaveOrUpdate jest zastrzeżoną operacją hibernacji - jeśli jednostka ma tożsamość, jest aktualizowana, ale jeśli nie ma jednostki, to jest wstawiana.

Z hibernacji perspektywę - tak, scalanie następnie saveOrUpdate teoretycznie może pracować do kojarzenia (Xstream strumieniowo) przemijające obiekty trwałe, ale mogą mieć ograniczenia przy stosowaniu w połączeniu z operacjami WZP. saveOrUpdate PRZEDSTAWIA każdy obiekt, który utrwala się do bazy danych za pomocą funkcji pobierania w celu ustalenia, czy/co wstawić/zaktualizować - jest inteligentny, ale nie jest to JPA i nie jest najbardziej wydajny. na przykład, powinieneś być w stanie sprawić, by działało to z ostrożnością i właściwą konfiguracją - i używając operacji hibernacji, a nie operacji JPA, gdy wystąpi konflikt.

pojawiają się błędy takie jak:

org.hibernate.ObjectDeletedException: usuniętego obiektu zostaną ponownie zapisane przez kaskadę (usunąć usunięty obiekt od stowarzyszeń): [pl .... ... SomeObject # 353296]

wierzę, że to może być spowodowane przez dwa czynniki:

  • gdzieś w (Xstream utworzonego) wykresu obiektu, instancja jednostka dziecko brakuje, że będzie obecny, jeśli zrobił (kaskadowo) odzyskać za pomocą JPA: daje to usunięcie obiektu
  • gdzieś indziej w was (Xstream - wykreowany) wykres obiektów, ta sama jednostka podrzędna jest obecna: daje to możliwość ponownego zapisania obiektu

    Jak przeprowadzić debugowanie: (a) utworzyć wykres obiektu z Xstream i wydrukować go W PEŁNYM - każdy podmiot, każdy pole; (b) ładuje ten sam wykres obiektowy za pośrednictwem JPA, kaskadowanie pobierania z górnej jednostki & wydrukuje go w PEŁNI - każdy podmiot, każde pole (c) porównuje dwa - brakuje czegoś z (a) obecnego w (b))?

Czy istnieje jakiś sposób, aby osiągnąć to, co chcę zrobić bez konieczności uzyskania utrzymującej obiekt w sesji, modyfikowanie, że zamiast modyfikując przemijające jeden, i stara się uratować, że i zastąpić istniejący trwały jeden ?

Za pomocą sugerowanego debugowania/naprawy - miejmy nadzieję.

ALBO moja sugestia o brutalnej sile do (dumbly) side-step tego błędu (nieco spowalnia wydajność): po wypełnieniu identyfikatorów na wykresie obiektów, wywołaj EntityManager.clear(), następnie kontynuuj z połączeniem() i saveOrUpdate(). Ale JEDNOSTKA TEST wyniki DB - aby upewnić się, że nie przeoczyłeś coś ważnego w zapełnianiu wykresu Xstream.

Do testowania/w ostateczności, spróbuj saveOrUpdate() bez scalania(), ale możesz być zmuszony do wyczyszczenia() menedżera jednostek, aby uniknąć wyjątków Hibernate, takich jak wyjątek NonUniqueObjectException.

Daj mi znać, jeśli nauczysz się więcej/mieć więcej informacji B ^)

+0

Próbowałem już wyczyścić clear() i saveOrUpdate() bez scalania. Dają one inne błędy hibernacji ... Myślę, że takie jak kopia obiektu już istnieje i tymczasowe tworzenie obiektów. Podam twoje inne sugestie. Dziękuję za dokładną odpowiedź. – eipark

+0

Jak można "wydrukować" cały obiekt? Zasadniczo jest to pierwotny problem, nad którym pracowałem, w którym przyszedł XStream. – eipark

1
Hibernate merge was not meant for associating transient objects 
to persistent ones. 

scaleniu() powinna idealnie pracować dla Ciebie, ponieważ jesteś rzeczywiście do czynienia z obiektem wolnostojącym. Ponieważ ustawiasz identyfikatory w 'transObj' przed wywołaniem łączenia, hibernacja traktuje je jako odłączone (nie przejściowe).

Myślę, że problem z twoim kodem polega na tym, że zadziałał hibernacji.

W kodzie ładujesz "persistObj" z bazy danych. Teraz hibernacja zatrzymuje ten "persistObj" w sesji. Następnie ustawiasz niektóre identyfikatory z "persistObj" w "transObj", a następnie wywołując połączenie. Niektóre obiekty podrzędne w 'persistObj' i 'transObj' mają te same identyfikatory, hibernacja zostaje pomieszana.

ComplexObject transObj = xstream.import("object.xml"); 
ComplexObject persistObj = someService.getObjByName(transObj.getName()); 
for (OtherObject o : c.getObjects()) { 
    if (persistObj.getObjects().contains(o.getName())) { 
     o.setId(persistObj.getObjectByName(o.getName()).getId()) 
    } 
    ... set a bunch of other IDs deeper in the object graph ... 
} 
transObj = session.merge(transObj); 
session.saveOrUpdate(transObj); 

Spróbuj zadzwonić The session.clear() Po załadowaniu 'persistObj', tak że hibernacji usuwa persistObj i uznaje tylko swoją wolnostojący obiekt.

ComplexObject transObj = xstream.import("object.xml"); 
ComplexObject persistObj = someService.getObjByName(transObj.getName()); 
// Clear the session, so that hibernate removes 'persistObj' from it's cache 
session.clear(); 
for (OtherObject o : c.getObjects()) { 
    if (persistObj.getObjects().contains(o.getName())) { 
     o.setId(persistObj.getObjectByName(o.getName()).getId()) 
    } 
    ... set a bunch of other IDs deeper in the object graph ... 
} 
transObj = session.merge(transObj); 
session.saveOrUpdate(transObj); 
+0

Jeśli zaznaczysz komentarz, w którym umieściłem odpowiedź Glen'a, spróbowałem użyć polecenia clear początkowo przed scaleniem() i saveOrUpdate(), ale miało tendencję do wywoływania inne problemy. Spróbuję jeszcze raz i wymyślę wyraźniejszą odpowiedź na pytanie, dlaczego to nie zadziałało lub przynajmniej niektóre komunikaty o błędach. Dzięki. – eipark

+1

Niestety, nie widziałem drugiego komentarza. Tak, pls opublikuj błędy, które widzisz z clear(). Zdecydowanie nie miałbym "persistObt" i "tranObj" dołączone do tej samej sesji, ponieważ próbujesz reprezentować ten sam wiersz bazy danych za pomocą dwóch różnych instancji obiektu. Hibernacja zwykle na to nie pozwala. – Sashi

+0

Po raz kolejny spróbowałem. Uruchomiłem clearSession(), scalam(), a następnie saveOrUpdate(). Tak jak w moim pierwotnym pytaniu, otrzymuję komunikat, że usunięty obiekt zostałby ponownie zapisany. – eipark

Powiązane problemy