Gyakori hibernált kivételek

1. Bemutatkozás

Ebben az oktatóanyagban megvitatunk néhány általános kivételt, amelyekkel a Hibernate munkája során találkozhatunk.

Áttekintjük céljukat és néhány gyakori okot. Ezenkívül megvizsgáljuk a megoldásaikat.

2. Hibernált kivétel áttekintése

Számos körülmény okozhat kivételeket a hibernált állapot használata közben. Ezek lehetnek térképezési hibák, infrastrukturális problémák, SQL hibák, az adatok integritásának megsértése, munkamenet problémák és tranzakciós hibák.

Ezek a kivételek többnyire a HibernateException. Ha azonban a hibernálást JPA-perzisztencia szolgáltatóként használjuk, ezek a kivételek beburkolódhatnak PerzisztenciaKivétel.

Mindkét alaposztály kiterjed RuntimeException. Ezért mind ellenőrizetlenek. Ezért nem kell elkapnunk vagy deklarálnunk őket minden helyen, ahol használják őket.

Továbbá, ezek többsége helyrehozhatatlan. Ennek eredményeként a művelet újrapróbálása nem segít. Ez azt jelenti, hogy el kell hagynunk az aktuális munkamenetet, amikor találkozunk velük.

Vizsgáljuk meg ezeket mind, egyenként.

3. Hibaképzés

Az objektum-reláció leképezés a Hibernate egyik fő előnye. Pontosabban, megszabadít minket az SQL utasítások kézi írásától.

Ugyanakkor megköveteli, hogy adjuk meg a Java objektumok és az adatbázis táblák közötti leképezést. Ennek megfelelően megjegyzéseket használunk, vagy dokumentumok feltérképezésével. Ezek a leképezések manuálisan kódolhatók. Alternatív megoldásként eszközöket is felhasználhatunk azok előállításához.

A leképezések megadása közben hibákat követhetünk el. Ezek lehetnek a leképezési specifikációban. Vagy előfordulhat eltérés a Java objektum és a megfelelő adatbázis-tábla között.

Az ilyen leképezési hibák kivételt generálnak. A kezdeti fejlesztés során gyakran találkozunk velük. Ezenkívül előfordulhat, hogy összefutunk velük, miközben a változásokat átállítjuk a környezetekre.

Vizsgáljuk meg ezeket a hibákat néhány példával.

3.1. MappingException

Az objektum-relációs leképezéssel kapcsolatos probléma a MappingException hogy dobják:

public void whenQueryExecutedWithUnmappedEntity_thenMappingException () {thrown.expectCause (isA (MappingException.class)); thrown.expectMessage ("Ismeretlen entitás: java.lang.String"); Munkamenet = sessionFactory.getCurrentSession (); NativeQuery lekérdezés = session .createNativeQuery ("válasszon nevet a PRODUCT-ból", String.class); query.getResultList (); }

A fenti kódban az createNativeQuery metódus megpróbálja leképezni a lekérdezés eredményét a megadott Java típusra Húr. A. Implicit leképezését használja Húr osztály tól Metamodell hogy elvégezze a feltérképezést.

Azonban a Húr osztályban nincs megadva leképezés. Ezért Hibernate nem tudja, hogyan kell feltérképezni név oszlop a Húr és dobja a kivételt.

A lehetséges okok és megoldások részletes elemzéséhez nézze meg a Hibernate Mapping Exception - Ismeretlen entitás oldalt.

Hasonlóképpen más hibák is okozhatják ezt a kivételt:

  • A mezők és módszerek kommentárjainak keverése
  • A. Megadása nem sikerült @JoinTable a @ManyToMany Egyesület
  • A leképezett osztály alapértelmezett konstruktora kivételt dob ​​a leképezés feldolgozása során

Továbbá, MappingException van néhány alosztálya, amelyek konkrét leképezési problémákat jelezhetnek:

  • AnnotationException - probléma egy kommentárral
  • DuplicateMappingException - osztály, táblázat vagy tulajdonságnév másolása
  • InvalidMappingException - a leképezés érvénytelen
  • MappingNotFoundException - a leképezési erőforrás nem található
  • PropertyNotFoundException - elvárt getter vagy setter módszer nem található egy osztályon

Ebből kifolyólag, ha találkozunk ezzel a kivétellel, először ellenőriznünk kell a leképezéseinket.

3.2. AnnotationException

Megérteni a AnnotationException, hozzunk létre egy entitást azonosító kommentár nélkül bármely mezőben vagy tulajdonságban:

@Entity public class EntityWithNoId {private int id; public int getId () {return id; } // normál beállító}

Mivel A hibernálás elvárja, hogy minden entitás rendelkezzen azonosítóval, kapunk egy AnnotationException amikor az entitást használjuk:

public void givenEntityWithoutId_whenSessionFactoryCreated_thenAnnotationException () {thrown.expect (AnnotationException.class); thrown.expectMessage ("Az entitáshoz nincs megadva azonosító"); Konfiguráció cfg = getConfiguration (); cfg.addAnnotatedClass (EntityWithNoId.class); cfg.buildSessionFactory (); }

Ezenkívül további valószínű okok lehetnek:

  • Ismeretlen szekvencia generátor, amelyet a @GeneratedValue annotáció
  • @Időbeli Java 8-hoz használt megjegyzés Dátum/Idő osztály
  • A cél entitás hiányzik vagy nem létezik a (z) számára @ManyToOne vagy @Egy a sokhoz
  • Nyers gyűjtési osztályok kapcsolati kommentárokkal @Egy a sokhoz vagy @ManyToMany
  • A gyűjtemény annotációival használt konkrét osztályok @Egy a sokhoz, @ManyToMany vagy @ElementCollection mivel Hibernate a gyűjtési interfészekre számít

A kivétel kiküszöbölése érdekében először ellenőriznünk kell a hibaüzenetben említett konkrét feljegyzést.

4. Séma kezelési hibák

Az automatikus adatbázis-séma kezelése a Hibernate másik előnye. Például generálhat DDL utasításokat adatbázis-objektumok létrehozásához vagy érvényesítéséhez.

A funkció használatához be kell állítanunk a hibernate.hbm2ddl.auto vagyont megfelelően.

Ha problémák vannak a sémakezelés során, kivételt kapunk. Vizsgáljuk meg ezeket a hibákat.

4.1. SchemaManagementException

A séma kezelésének végrehajtásával kapcsolatos bármely infrastruktúrával kapcsolatos probléma a SchemaManagementException.

A bemutatáshoz utasítsuk a Hibernate-t az adatbázis-séma érvényesítésére:

public void givenMissingTable_whenSchemaValidated_thenSchemaManagementException () {thrown.expect (SchemaManagementException.class); thrown.expectMessage ("Séma-érvényesítés: hiányzó tábla"); Konfiguráció cfg = getConfiguration (); cfg.setProperty (AvailableSettings.HBM2DDL_AUTO, "érvényesítés"); cfg.addAnnotatedClass (Termékosztály); cfg.buildSessionFactory (); }

Mivel a táblázat megfelel Termék nincs az adatbázisban, az S felépítése során a séma-érvényesítési kivételt kapjukessionFactory.

Ezen kivétellel kapcsolatban további lehetséges forgatókönyvek is léteznek:

  • nem tud csatlakozni az adatbázishoz sémakezelési feladatok elvégzéséhez
  • a séma nincs az adatbázisban

4.2. CommandAcceptanceException

Bármely probléma, amely egy adott sémakezelő parancsnak megfelelő DDL-t futtat, a CommandAcceptanceException.

Példaként adjuk meg a helytelen nyelvjárást a SessionFactory:

public void whenWrongDialectSpecified_thenCommandAcceptanceException () {thrown.expect (SchemaManagementException.class); thrown.expectCause (isA (CommandAcceptanceException.class)); thrown.expectMessage ("Hald leállítása: Hiba a DDL végrehajtásakor"); Konfiguráció cfg = getConfiguration (); cfg.setProperty (AvailableSettings.DIALECT, "org.hibernate.dialect.MySQLDialect"); cfg.setProperty (AvailableSettings.HBM2DDL_AUTO, "frissítés"); cfg.setProperty (AvailableSettings.HBM2DDL_HALT_ON_ERROR, "true"); cfg.getProperties () .put (AvailableSettings.HBM2DDL_HALT_ON_ERROR, true); cfg.addAnnotatedClass (Termékosztály); cfg.buildSessionFactory (); }

Itt helytelen nyelvjárást határoztunk meg: MySQLDialect. Ugyancsak utasítjuk a hibernátust a sémaobjektumok frissítésére. Következésképpen a Hibernate által a H2 adatbázis frissítéséhez végrehajtott DDL utasítások meghiúsulnak, és kivételt fogunk kapni.

Alapértelmezés szerint a Hibernate csendben naplózza ezt a kivételt, és továbbhalad. Amikor később használjuk a SessionFactory, megkapjuk a kivételt.

Annak biztosítására, hogy ez a hiba kivételt képezzen, beállítottuk a tulajdonságot HBM2DDL_HALT_ON_ERROR nak nek igaz.

Hasonlóképpen, ez a hiba néhány más gyakori oka:

  • Az oszlopnevekben nincs egyezés a leképezés és az adatbázis között
  • Két osztály azonos táblához van hozzárendelve
  • Az osztályhoz vagy táblához használt név az adatbázisban lefoglalt szó, például FELHASZNÁLÓ, például
  • Az adatbázishoz való csatlakozáshoz használt felhasználó nem rendelkezik a szükséges jogosultságokkal

5. SQL végrehajtási hibák

Amikor adatokat szúrunk be, frissítünk, törölünk vagy lekérdezünk a hibernálás segítségével, akkor a DML utasításokat végrehajtja az adatbázissal JDBC segítségével. Ez az API felvet egy SQLEkivétel ha a művelet hibákat vagy figyelmeztetéseket eredményez.

A hibernálás ezt a kivételt konvertálja JDBCEkivétel vagy annak egyik megfelelő alosztálya:

  • ConstraintViolationException
  • DataException
  • JDBCConnectionException
  • LockAcquisitionException
  • PessimisticLockException
  • QueryTimeoutException
  • SQLGrammarException
  • ÁltalánosJDBCEkivétel

Beszéljük meg a gyakori hibákat.

5.1. JDBCEkivétel

JDBCEkivétel mindig egy adott SQL utasítás okozza. Felhívhatjuk a getSQL metódus a sértő SQL utasítás megszerzéséhez.

Továbbá visszakereshetjük az alapul szolgáló elemet SQLEkivétel a ... val getSQLException módszer.

5.2. SQLGrammarException

SQLGrammarException azt jelzi, hogy az adatbázisba küldött SQL érvénytelen volt. Ennek oka lehet egy szintaktikai hiba vagy egy érvénytelen objektum hivatkozás.

Például, hiányzó táblázat eredményezheti ezt a hibát az adatok lekérdezése közben:

public void givenMissingTable_whenQueryExecuted_thenSQLGrammarException () {thrown.expect (isA (PersistenceException.class)); thrown.expectCause (isA (SQLGrammarException.class)); thrown.expectMessage ("SQLGrammarException: nem sikerült elkészíteni az utasítást"); Munkamenet = sessionFactory.getCurrentSession (); NativeQuery lekérdezés = session.createNativeQuery ("select * from NON_EXISTING_TABLE", Product.class); query.getResultList (); }

Ez a hiba az adatok mentése közben is előfordulhat, ha hiányzik a táblázat:

public void givenMissingTable_whenEntitySaved_thenSQLGrammarException () {thrown.expect (isA (PersistenceException.class)); thrown.expectCause (isA (SQLGrammarException.class)); dobott .expectMessage ("SQLGrammarException: nem sikerült elkészíteni az utasítást"); Konfiguráció cfg = getConfiguration (); cfg.addAnnotatedClass (Termékosztály); SessionFactory sessionFactory = cfg.buildSessionFactory (); Munkamenet = null; Tranzakciós tranzakció = null; próbáld ki a {session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); Termék termék = új termék (); product.setId (1); product.setName ("1. termék"); session.save (termék); ügylet.vállalás (); } catch (e kivétel) {rollbackTransactionQuietly (tranzakció); dobás (e); } végül {closeSessionQuietly (session); closeSessionFactoryQuietly (sessionFactory); }}

Néhány egyéb lehetséges ok:

  • A használt elnevezési stratégia nem hozzárendeli az osztályokat a megfelelő táblázatokhoz
  • A megadott oszlop @JoinColumn nem létezik

5.3. ConstraintViolationException

A ConstraintViolationException azt jelzi, hogy a kért DML művelet az integritás korlátozásának megsértését okozta. Ennek a korlátozásnak a nevét a getConstraintName módszer.

Ennek a kivételnek a gyakori oka az ismétlődő rekordok mentése:

public void whenDuplicateIdSaved_thenConstraintViolationException () {thrown.expect (isA (PersistenceException.class)); thrown.expectCause (isA (ConstraintViolationException.class)); thrown.expectMessage ("ConstraintViolationException: nem sikerült végrehajtani az utasítást"); Munkamenet = null; Tranzakciós tranzakció = null; for (int i = 1; i <= 2; i ++) {try {session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); Termék termék = új termék (); product.setId (1); product.setName ("Termék" + i); session.save (termék); ügylet.vállalás (); } catch (e kivétel) {rollbackTransactionQuietly (tranzakció); dobás (e); } végül {closeSessionQuietly (session); }}}

Továbbá, a nulla érték a NEM NULLA oszlop az adatbázisban felvetheti ezt a hibát.

A hiba megoldása érdekében minden validációt el kell végeznünk az üzleti rétegben. Ezenkívül az adatbázis-korlátozásokat nem szabad felhasználni az alkalmazások ellenőrzésére.

5.4. DataException

DataException azt jelzi, hogy egy SQL utasítás kiértékelése valamilyen illegális műveletet, típus-eltérést vagy helytelen kardinalitást eredményezett.

Például a karakteradatok numerikus oszlopokkal való használata ezt a hibát okozhatja:

public void givenQueryWithDataTypeMismatch_WhenQueryExecuted_thenDataException () {thrown.expectCause (isA (DataException.class)); thrown.expectMessage ("org.hibernate.exception.DataException: nem tudta elkészíteni az utasítást"); Munkamenet = sessionFactory.getCurrentSession (); NativeQuery lekérdezés = session.createNativeQuery ("select * from PRODUCT where", Product.class); query.getResultList (); }

A hiba kijavításához biztosítanunk kell, hogy az adattípusok és a hossz megegyezzen az alkalmazás kódja és az adatbázis között.

5.5. JDBCConnectionException

A JDBCConectionException jelzi az adatbázissal való kommunikáció problémáit.

Például egy leépülő adatbázis vagy hálózat okozhatja ezt a kivételt.

Ezenkívül a helytelen adatbázis-beállítás okozhatja ezt a kivételt. Az egyik ilyen eset az, hogy az adatbázis-kapcsolatot a szerver bezárta, mert sokáig tétlen volt. Ez akkor fordulhat elő, ha a kapcsolatkészletet használjuk, és a készlet üresjárati időtúllépési beállítása meghaladja az adatbázisban szereplő kapcsolat időtúllépési értékét.

A probléma megoldásához először meg kell győződnünk arról, hogy az adatbázis-gazdagép jelen van-e, és hogy fenn van-e. Ezután ellenőriznünk kell, hogy az adatbázis-kapcsolathoz a megfelelő hitelesítést használják-e. Végül ellenőriznünk kell, hogy az időtúllépés értéke megfelelően van-e beállítva a csatlakozási készletben.

5.6. QueryTimeoutException

Amikor az adatbázis lekérdezése lejár, akkor ezt a kivételt kapjuk. Más hibák miatt is láthatjuk, például a táblaterek megtelnek.

Ez a kevés helyreállítható hiba egyike, ami azt jelenti, hogy ugyanabban a tranzakcióban újra megpróbálhatjuk a kimutatást.

A probléma megoldásához növelhetjük a lekérdezés időkorlátját a régóta futó lekérdezéseknél többféle módon:

  • Állítsa be a időtúllépés elem a @NévQuery vagy @NévNativeQuery annotáció
  • Hívja meg a setHint a metódusa a Lekérdezés felület
  • Hívja a setTimeout módszere Tranzakció felület
  • Hívja meg a setTimeout módszere Lekérdezés felület

6. A munkamenet állapotával kapcsolatos hibák

Vizsgáljuk meg a hibernált munkamenet-használat miatti hibákat.

6.1. NonUniqueObjectException

A hibernálás nem engedélyez két azonos azonosítójú objektumot egyetlen munkamenetben.

Ha ugyanazon Java osztály két példányát megpróbáljuk azonos azonosítóval összekapcsolni egyetlen munkamenetben, akkor a NonUniqueObjectException. Az entitás nevét és azonosítóját a getEntityName () és getIdentifier () mód.

A hiba megismétléséhez próbáljunk meg menteni két példányt Termék ugyanazzal az azonosítóval egy munkamenettel:

public void givenSessionContainingAnId_whenIdAssociatedAgain_thenNonUniqueObjectException () {thrown.expect (isA (NonUniqueObjectException.class)); thrown.expectMessage ("A munkamenethez már társítottak egy másik azonos azonosítójú objektumot"); Munkamenet = null; Tranzakciós tranzakció = null; próbáld ki a {session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); Termék termék = új termék (); product.setId (1); product.setName ("1. termék"); session.save (termék); termék = új termék (); product.setId (1); product.setName ("2. termék"); session.save (termék); ügylet.vállalás (); } catch (e kivétel) {rollbackTransactionQuietly (tranzakció); dobás (e); } végül {closeSessionQuietly (session); }}

Kapunk egy NonUniqueObjectException, a várakozásoknak megfelelően.

Ez a kivétel gyakran előfordul, amikor egy leválasztott objektumot egy munkamenethez csatolunk a frissítés módszer. Ha a munkamenetnek van egy másik példánya azonos azonosítóval betöltve, akkor ezt a hibát kapjuk. Ennek kijavítása érdekében használhatjuk a összeolvad módszer hogy rögzítse a levált tárgyat.

6.2. StaleStateException

Hibernált dobások StaleStateExceptions amikor a verziószám vagy az időbélyeg ellenőrzése sikertelen. Azt jelzi, hogy a munkamenet elavult adatokat tartalmazott.

Néha ez belekerül egy OptimisticLockException.

Ez a hiba általában akkor fordul elő, amikor régóta futó tranzakciókat használ a verziószámmal.

Ezenkívül előfordulhat egy entitás frissítése vagy törlése közben is, ha a megfelelő adatbázis-sor nem létezik:

public void whenUpdatingNonExistingObject_thenStaleStateException () {thrown.expect (isA (OptimisticLockException.class)); thrown.expectMessage ("A kötegelt frissítés váratlan sorszámot adott vissza a frissítéstől"); thrown.expectCause (isA (StaleStateException.class)); Munkamenet = null; Tranzakciós tranzakció = null; próbáld ki a {session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); Terméktermék = új Termék (); product.setId (15); product.setName ("Termék1"); session.update (termék); ügylet.vállalás (); } catch (e kivétel) {rollbackTransactionQuietly (tranzakció); dobás (e); } végül {closeSessionQuietly (session); }}

Néhány más lehetséges forgatókönyv:

  • nem adtunk meg megfelelő nem mentett értékű stratégiát az entitás számára
  • két felhasználó megpróbálta szinte egyszerre törölni ugyanazt a sort
  • kézzel állítunk be egy értéket az automatikusan létrehozott azonosító vagy verzió mezőben

7. Lusta inicializálási hibák

Az asszociációkat általában lustán töltjük be az alkalmazás teljesítményének javítása érdekében. Az asszociációk csak akkor kerülnek lehívásra, amikor először használják őket.

A hibernáláshoz azonban aktív munkamenetre van szükség az adatok lekéréséhez. Ha a munkamenet már lezárult, amikor megpróbálunk hozzáférni egy inicializálatlan társuláshoz, kivételt kapunk.

Vizsgáljuk meg ezt a kivételt és annak kijavításának különféle módjait.

7.1. LazyInitializationException

LazyInitializationException azt jelzi, hogy kísérletet tettek az inicializálatlan adatok betöltésére egy aktív munkameneten kívül. Ezt a hibát számos forgatókönyvben kaphatjuk meg.

Először is megkapjuk ezt a kivételt, miközben egy lusta kapcsolathoz jutunk a bemutató rétegben. Ennek oka, hogy az entitás részben betöltődött az üzleti rétegbe, és a munkamenet lezárult.

Másodszor, ezt a hibát a Spring Data segítségével kaphatjuk meg, ha a kap egy módszer. Ez a módszer lustán beolvassa a példányt.

Ennek a kivételnek számos módja van.

Először is, minden kapcsolatot lelkesen megterhelhetünk.De ez kihat az alkalmazás teljesítményére, mert olyan adatokat töltünk be, amelyeket nem használunk fel.

Másodszor, a munkamenetet nyitva tudjuk tartani, amíg a nézet meg nem jelenik. Ez az úgynevezett „Nyissa meg a munkamenetet a nézetben”És anti-minta. Ezt el kell kerülnünk, mivel ennek számos hátránya van.

Harmadszor, megnyithatunk egy másik munkamenetet, és újracsatolhatjuk az entitást a kapcsolatok lekérése érdekében. Megtehetjük a összeolvad módszer a munkameneten.

Végül inicializálhatjuk a szükséges társításokat az üzleti rétegekben. Ezt a következő szakaszban tárgyaljuk.

7.2. A releváns lusta kapcsolatok inicializálása az üzleti rétegben

A lusta kapcsolatok inicializálásának számos módja van.

Az egyik lehetőség inicializálni őket az entitás megfelelő metódusainak meghívásával. Ebben az esetben a Hibernate több adatbázis-lekérdezést ad ki, ami romlott teljesítményt okoz. „N + 1 SELECT” problémának nevezzük.

Másodszor, használhatjuk Fetch Join hogy az adatokat egyetlen lekérdezésben kapja meg. Ennek eléréséhez azonban egyedi kódot kell írnunk.

Végül, entitásdiagramok segítségével meghatározhatjuk az összes beolvasandó attribútumot. Használhatjuk az annotációkat @NamedEntityGraph, @NamedAttributeNode, és @NévEntitySubgraph az entitásgráf deklaratív meghatározásához. Programozatosan definiálhatjuk őket a JPA API-val is. Azután, a teljes gráfot egyetlen hívásban kapjuk le a lekérési műveletben megadva.

8. Tranzakciós kérdések

A tranzakciók meghatározzák a munkaegységeket és az egyidejű tevékenységek közötti elszigeteltséget. Kétféle módon határolhatjuk el őket. Először deklaratívan definiálhatjuk őket annotációk segítségével. Másodszor, a Hibernate Transaction API segítségével programozottan kezelhetjük őket.

Ezenkívül a Hibernate delegálja a tranzakciókezelést egy tranzakciókezelőre. Ha a tranzakciót valamilyen ok miatt nem tudták elindítani, elkövetni vagy visszavonni, a hibernálás kivételt vet.

Általában a TransactionException vagy egy IllegalArgumentException a tranzakciókezelőtől függően.

Illusztrációként próbáljunk meg egy olyan tranzakciót végrehajtani, amelyet visszagörgetésként jelöltek meg:

public void givenTxnMarkedRollbackOnly_whenCommitted_thenTransactionException () {thrown.expect (isA (TransactionException.class)); thrown.expectMessage ("A tranzakció csak visszagörgetéshez volt jelölve; nem hajlandó végrehajtani"); Munkamenet = null; Tranzakciós tranzakció = null; próbáld ki a {session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); Terméktermék = új Termék (); product.setId (15); product.setName ("Termék1"); session.save (termék); ügylet.setRollbackOnly (); ügylet.vállalás (); } catch (e kivétel) {rollbackTransactionQuietly (tranzakció); dobás (e); } végül {closeSessionQuietly (session); }}

Hasonlóképpen más hibák is kivételt okozhatnak:

  • Deklaratív és programozott tranzakciók keverése
  • Kísérlet egy tranzakció elindítására, amikor egy másik már aktív a munkamenetben
  • Megpróbál végrehajtani vagy visszagörgetni tranzakció megkezdése nélkül
  • Többször megpróbál tranzakciót végrehajtani vagy visszavonni

9. Egyidejűségi kérdések

A hibernálás két zárolási stratégiát támogat az egyidejű tranzakciók miatti adatbázis-következetlenségek megelőzésére - optimista és pesszimista. Mindketten kivételt vetnek fel zárási konfliktus esetén.

A nagy egyidejűség és a nagy skálázhatóság támogatásához általában optimista egyidejűség-vezérlést alkalmazunk a verzióellenőrzéssel. Ez verziószámokat vagy időbélyegeket használ az ütköző frissítések észlelésére.

OptimisticLockingException optimista zárási konfliktus jelzésére dobják. Például akkor kapjuk meg ezt a hibát, ha ugyanazon entitás két frissítését vagy törlését hajtjuk végre anélkül, hogy az első művelet után frissítenénk:

public void whenDeletingADeletedObject_thenOptimisticLockException () {thrown.expect (isA (OptimisticLockException.class)); thrown.expectMessage ("A kötegelt frissítés váratlan sorszámot adott vissza a frissítéstől"); thrown.expectCause (isA (StaleStateException.class)); Munkamenet = null; Tranzakciós tranzakció = null; próbáld ki a {session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); Terméktermék = új Termék (); product.setId (12); product.setName ("12. termék"); session.save (termék1); ügylet.vállalás (); session.close (); session = sessionFactory.openSession (); tranzakció = session.beginTransaction (); termék = session.get (Product.class, 12); session.createNativeQuery ("törlés a termékből, ahol id = 12") .executeUpdate (); // Frissítenünk kell a hiba kijavításához. // session.refresh (termék); session.delete (termék); ügylet.vállalás (); } catch (e kivétel) {rollbackTransactionQuietly (tranzakció); dobás (e); } végül {closeSessionQuietly (session); }}

Hasonlóképpen, akkor is kaphatjuk ezt a hibát, ha két felhasználó megpróbálja frissíteni ugyanazt az entitást szinte egyszerre. Ebben az esetben az első sikeres lehet, a második pedig felveti ezt a hibát.

Ebből kifolyólag, nem tudjuk teljesen elkerülni ezt a hibát anélkül, hogy bevezetnénk a pesszimista zárat. Ennek minimalizálásával azonban minimalizálhatjuk a következőket:

  • Legyen a frissítési műveletek a lehető legrövidebbek
  • A lehető leggyakrabban frissítse az entitás reprezentációit az ügyfélben
  • Ne tegye a gyorsítótárba az entitást vagy az azt képviselő értékobjektumot
  • A frissítést követően mindig frissítse az entitás reprezentációját az ügyfélen

10. Következtetés

Ebben a cikkben megvizsgáltunk néhány hibát a hibernálás használata során. Továbbá megvizsgáltuk azok valószínű okait és megoldásaikat.

Szokás szerint a teljes forráskód megtalálható a GitHubon.