Veelvoorkomende uitzonderingen voor slaapstand

1. Inleiding

In deze tutorial bespreken we enkele veelvoorkomende uitzonderingen die we kunnen tegenkomen tijdens het werken met Hibernate.

We zullen hun doel en enkele veelvoorkomende oorzaken bespreken. Bovendien zullen we hun oplossingen bekijken.

2. Overzicht van uitzonderingen in slaapstand

Veel omstandigheden kunnen ervoor zorgen dat er uitzonderingen worden gegenereerd tijdens het gebruik van Hibernate. Dit kunnen kaartfouten, infrastructuurproblemen, SQL-fouten, schendingen van de gegevensintegriteit, sessieproblemen en transactiefouten zijn.

Deze uitzonderingen strekken zich meestal uit van HibernateException. Als we Hibernate echter gebruiken als een JPA-persistentieprovider, kunnen deze uitzonderingen worden ingepakt PersistenceException.

Beide basisklassen strekken zich uit van RuntimeException. Daarom zijn ze allemaal niet aangevinkt. Daarom hoeven we ze niet op elke plaats waar ze worden gebruikt te vangen of aan te geven.

Verder de meeste hiervan zijn onherstelbaar. Als gevolg hiervan zou het opnieuw proberen van de bewerking niet helpen. Dit betekent dat we de huidige sessie moeten verlaten als we ze tegenkomen.

Laten we nu elk van deze een voor een bekijken.

3. Toewijzingsfouten

Object-relationele mapping is een groot voordeel van Hibernate. Concreet bevrijdt het ons van het handmatig schrijven van SQL-instructies.

Tegelijkertijd moeten we de toewijzing tussen Java-objecten en databasetabellen specificeren. Dienovereenkomstig specificeren we ze met behulp van annotaties of door middel van mapping-documenten. Deze toewijzingen kunnen handmatig worden gecodeerd. Als alternatief kunnen we tools gebruiken om ze te genereren.

Bij het specificeren van deze toewijzingen kunnen we fouten maken. Deze zouden in de mapping-specificatie kunnen staan. Of er kan een discrepantie zijn tussen een Java-object en de corresponderende databasetabel.

Dergelijke mappingfouten genereren uitzonderingen. We komen ze vaak tegen tijdens de eerste ontwikkeling. Bovendien kunnen we ze tegenkomen tijdens het migreren van veranderingen tussen omgevingen.

Laten we deze fouten bekijken met enkele voorbeelden.

3.1. MappingException

Een probleem met de object-relationele toewijzing veroorzaakt een MappingException worden gegooid:

openbare leegte whenQueryExecutedWithUnmappedEntity_thenMappingException () {throw.expectCause (isA (MappingException.class)); thrown.expectMessage ("Onbekende entiteit: java.lang.String"); Sessiesessie = sessionFactory.getCurrentSession (); NativeQuery query = session .createNativeQuery ("selecteer naam uit PRODUCT", String.class); query.getResultList (); }

In de bovenstaande code, de createNativeQuery methode probeert het queryresultaat toe te wijzen aan het opgegeven Java-type Draad. Het maakt gebruik van de impliciete toewijzing van het Draad klas van Metamodel om de mapping te doen.

echter, de Draad klasse heeft geen toewijzing gespecificeerd. Daarom weet Hibernate niet hoe het naam kolom naar Draad en gooit de uitzondering.

Voor een gedetailleerde analyse van mogelijke oorzaken en oplossingen, bekijk Hibernate Mapping Exception - Unknown Entity.

Evenzo kunnen andere fouten ook deze uitzondering veroorzaken:

  • Het mengen van annotaties op velden en methoden
  • Het niet specificeren van het @JoinTable voor een @Veel te veel vereniging
  • De standaardconstructor van de toegewezen klasse genereert een uitzondering tijdens de toewijzingsverwerking

Verder MappingException heeft een paar subklassen die op specifieke mappingproblemen kunnen duiden:

  • AnnotationException - een probleem met een annotatie
  • DuplicateMappingException - dubbele toewijzing voor een klasse-, tabel- of eigenschapsnaam
  • InvalidMappingException - toewijzing is ongeldig
  • MappingNotFoundException - toewijzingsbron kan niet worden gevonden
  • PropertyNotFoundException - een verwachte getter- of setter-methode is niet gevonden in een klasse

Daarom Als we deze uitzondering tegenkomen, moeten we eerst onze toewijzingen verifiëren.

3.2. AnnotationException

Om de AnnotationException, laten we een entiteit maken zonder een identifier-annotatie op een veld of eigenschap:

@Entity openbare klasse EntityWithNoId {privé int id; public int getId () {retour-id; } // standaard setter}

Sinds Hibernate verwacht dat elke entiteit een ID heeft, we krijgen een AnnotationException wanneer we de entiteit gebruiken:

openbare ongeldige gegevenEntityWithoutId_whenSessionFactoryCreated_thenAnnotationException () {throw.expect (AnnotationException.class); throw.expectMessage ("Geen ID opgegeven voor entiteit"); Configuratie cfg = getConfiguration (); cfg.addAnnotatedClass (EntityWithNoId.class); cfg.buildSessionFactory (); }

Verder zijn enkele andere mogelijke oorzaken:

  • Onbekende sequentiegenerator die wordt gebruikt in het @GeneratedValue annotatie
  • @Tijdelijk annotatie gebruikt met een Java 8 Datum/Tijd klasse
  • Doelentiteit ontbreekt of bestaat niet voor @ManyToOne of @Een te veel
  • Raw verzamelingsklassen die worden gebruikt met annotaties van relaties @Een te veel of @Veel te veel
  • Concrete klassen die worden gebruikt met de collectieannotaties @Een te veel, @Veel te veel of @ElementCollection zoals Hibernate de collectie-interfaces verwacht

Om deze uitzondering op te lossen, moeten we eerst de specifieke annotatie in het foutbericht controleren.

4. Fouten in schemabeheer

Automatisch databaseschemabeheer is een ander voordeel van Hibernate. Het kan bijvoorbeeld DDL-instructies genereren om databaseobjecten te maken of te valideren.

Om deze functie te gebruiken, moeten we de slaapstand.hbm2ddl.auto onroerend goed op de juiste manier.

Als er problemen zijn bij het uitvoeren van schemabeheer, krijgen we een uitzondering. Laten we deze fouten eens bekijken.

4.1. SchemaManagementException

Elk infrastructuurprobleem bij het uitvoeren van schemabeheer veroorzaakt een SchemaManagementException.

Om dit te demonstreren, laten we Hibernate opdracht geven om het databaseschema te valideren:

openbare leegte gegevenMissingTable_whenSchemaValidated_thenSchemaManagementException () {throw.expect (SchemaManagementException.class); throwwn.expectMessage ("Schema-validatie: ontbrekende tabel"); Configuratie cfg = getConfiguration (); cfg.setProperty (AvailableSettings.HBM2DDL_AUTO, "valideren"); cfg.addAnnotatedClass (Product.class); cfg.buildSessionFactory (); }

Omdat de tabel overeenkomt met Product niet aanwezig is in de database, krijgen we de uitzondering voor schema-validatie tijdens het bouwen van de SessionFactory.

Bovendien zijn er andere mogelijke scenario's voor deze uitzondering:

  • kan geen verbinding maken met de database om schemabeheertaken uit te voeren
  • het schema is niet aanwezig in de database

4.2. CommandAcceptanceException

Elk probleem bij het uitvoeren van een DDL die overeenkomt met een specifieke opdracht voor schemabeheer kan een CommandAcceptanceException.

Laten we als voorbeeld het verkeerde dialect specificeren tijdens het instellen van het SessionFactory:

openbare void whenWrongDialectSpecified_thenCommandAcceptanceException () {throw.expect (SchemaManagementException.class); gegooid.expectCause (isA (CommandAcceptanceException.class)); thrown.expectMessage ("Stoppen bij fout: fout bij het uitvoeren van DDL"); Configuratie cfg = getConfiguration (); cfg.setProperty (AvailableSettings.DIALECT, "org.hibernate.dialect.MySQLDialect"); cfg.setProperty (AvailableSettings.HBM2DDL_AUTO, "update"); cfg.setProperty (AvailableSettings.HBM2DDL_HALT_ON_ERROR, "true"); cfg.getProperties () .put (AvailableSettings.HBM2DDL_HALT_ON_ERROR, true); cfg.addAnnotatedClass (Product.class); cfg.buildSessionFactory (); }

Hier hebben we het verkeerde dialect gespecificeerd: MySQLDialect. We geven Hibernate ook de opdracht om de schema-objecten bij te werken. Bijgevolg zullen de DDL-instructies die door Hibernate worden uitgevoerd om de H2-database bij te werken, mislukken en krijgen we een uitzondering.

Standaard registreert Hibernate deze uitzondering stil en gaat verder. Wanneer we later de SessionFactory, we krijgen de uitzondering.

Om ervoor te zorgen dat er een uitzondering wordt gegenereerd op deze fout, hebben we de eigenschap ingesteld HBM2DDL_HALT_ON_ERROR naar waar.

Evenzo zijn dit enkele andere veelvoorkomende oorzaken van deze fout:

  • Er is een verschil in kolomnamen tussen de toewijzing en de database
  • Twee klassen zijn toegewezen aan dezelfde tabel
  • De naam die wordt gebruikt voor een klasse of tabel is een gereserveerd woord in de database, zoals GEBRUIKER, bijvoorbeeld
  • De gebruiker die is gebruikt om verbinding te maken met de database, heeft niet de vereiste rechten

5. SQL-uitvoeringsfouten

Wanneer we gegevens invoegen, bijwerken, verwijderen of opvragen met behulp van Hibernate, voert het DML-instructies uit tegen de database met behulp van JDBC. Deze API genereert een SQLException als de operatie resulteert in fouten of waarschuwingen.

Hibernate zet deze uitzondering om in JDBCException of een van de geschikte subklassen:

  • ConstraintViolationException
  • DataException
  • JDBCConnectionException
  • LockAcquisitionException
  • PessimisticLockException
  • QueryTimeoutException
  • SQLGrammarException
  • GenericJDBCException

Laten we veel voorkomende fouten bespreken.

5.1. JDBCException

JDBCException wordt altijd veroorzaakt door een bepaalde SQL-instructie. We kunnen de getSQL methode om de aanstootgevende SQL-instructie op te halen.

Verder kunnen we de onderliggende waarde achterhalen SQLException met de getSQLException methode.

5.2. SQLGrammarException

SQLGrammarException geeft aan dat de SQL die naar de database is verzonden, ongeldig is. Het kan te wijten zijn aan een syntaxisfout of een ongeldige objectverwijzing.

Bijvoorbeeld, een ontbrekende tabel kan resulteren in deze fout bij het opvragen van gegevens:

openbare ongeldige gegevenMissingTable_whenQueryExecuted_thenSQLGrammarException () {throw.expect (isA (PersistenceException.class)); gegooid.expectCause (isA (SQLGrammarException.class)); throwwn.expectMessage ("SQLGrammarException: kon instructie niet voorbereiden"); Sessiesessie = sessionFactory.getCurrentSession (); NativeQuery query = session.createNativeQuery ("selecteer * uit NON_EXISTING_TABLE", Product.class); query.getResultList (); }

We kunnen deze fout ook krijgen tijdens het opslaan van gegevens als de tabel ontbreekt:

openbare leegte gegevenMissingTable_whenEntitySaved_thenSQLGrammarException () {throw.expect (isA (PersistenceException.class)); gegooid.expectCause (isA (SQLGrammarException.class)); gegooid .expectMessage ("SQLGrammarException: kon instructie niet voorbereiden"); Configuratie cfg = getConfiguration (); cfg.addAnnotatedClass (Product.class); SessionFactory sessionFactory = cfg.buildSessionFactory (); Sessie sessie = null; Transactietransactie = null; probeer {session = sessionFactory.openSession (); transactie = session.beginTransaction (); Product product = nieuw product (); product.setId (1); product.setName ("Product 1"); session.save (product); transactie.commit (); } catch (uitzondering e) {rollbackTransactionQuietly (transactie); gooien (e); } eindelijk {closeSessionQuietly (sessie); closeSessionFactoryQuietly (sessionFactory); }}

Enkele andere mogelijke oorzaken zijn:

  • De gebruikte naamgevingsstrategie wijst de klassen niet toe aan de juiste tabellen
  • De kolom die is opgegeven in @JoinColumn bestaat niet

5.3. ConstraintViolationException

EEN ConstraintViolationException geeft aan dat de aangevraagde DML-bewerking ervoor zorgde dat een integriteitsbeperking werd geschonden. We kunnen de naam van deze beperking krijgen door de getConstraintName methode.

Een veelvoorkomende oorzaak van deze uitzondering is het proberen dubbele records op te slaan:

openbare leegte whenDuplicateIdSaved_thenConstraintViolationException () {throw.expect (isA (PersistenceException.class)); gegooid.expectCause (isA (ConstraintViolationException.class)); throwwn.expectMessage ("ConstraintViolationException: kon instructie niet uitvoeren"); Sessiesessie = null; Transactietransactie = null; for (int i = 1; i <= 2; i ++) {probeer {session = sessionFactory.openSession (); transactie = session.beginTransaction (); Product product = nieuw product (); product.setId (1); product.setName ("Product" + i); session.save (product); transactie.commit (); } catch (uitzondering e) {rollbackTransactionQuietly (transactie); gooien (e); } eindelijk {closeSessionQuietly (sessie); }}}

Bewaar ook een nul waarde voor een NIET NUL kolom in de database kan deze fout veroorzaken.

Om deze fout op te lossen, we moeten alle validaties in de bedrijfslaag uitvoeren. Bovendien mogen databasebeperkingen niet worden gebruikt om toepassingsvalidaties uit te voeren.

5.4. DataException

DataException geeft aan dat de evaluatie van een SQL-instructie heeft geleid tot een illegale bewerking, niet-overeenkomend type of onjuiste kardinaliteit.

Het gebruik van tekengegevens voor een numerieke kolom kan bijvoorbeeld deze fout veroorzaken:

openbare ongeldige gegevenQueryWithDataTypeMismatch_WhenQueryExecuted_thenDataException () {throw.expectCause (isA (DataException.class)); thrown.expectMessage ("org.hibernate.exception.DataException: kon instructie niet voorbereiden"); Sessie sessie = sessionFactory.getCurrentSession (); NativeQuery query = session.createNativeQuery ("selecteer * uit PRODUCT waar", Product.class); query.getResultList (); }

Om deze fout op te lossen, we moeten ervoor zorgen dat de gegevenstypen en de lengte overeenkomen tussen de applicatiecode en de database.

5.5. JDBCConnectionException

EEN JDBCConectionException geeft problemen aan bij de communicatie met de database.

Als een database of netwerk uitvalt, kan deze uitzondering worden gegenereerd.

Bovendien kan een onjuiste databaseconfiguratie deze uitzondering veroorzaken. Een voorbeeld van zo'n geval is dat de databaseverbinding door de server wordt verbroken omdat deze lange tijd inactief was. Dit kan gebeuren als we pooling van verbindingen gebruiken en de time-outinstelling voor inactiviteit in de pool hoger is dan de time-outwaarde voor de verbinding in de database.

Om dit probleem op te lossen, moeten we er eerst voor zorgen dat de databasehost aanwezig is en dat deze actief is. Vervolgens moeten we controleren of de juiste authenticatie wordt gebruikt voor de databaseverbinding. Ten slotte moeten we controleren of de time-outwaarde correct is ingesteld op de verbindingspool.

5.6. QueryTimeoutException

Wanneer een databasequery een time-out heeft, krijgen we deze uitzondering. We kunnen het ook zien door andere fouten, zoals het vol raken van de tablespace.

Dit is een van de weinige herstelbare fouten, wat betekent dat we de verklaring in dezelfde transactie opnieuw kunnen proberen.

Om dit probleem op te lossen, we kunnen de time-out voor zoekopdrachten verhogen voor langlopende zoekopdrachten op meerdere manieren:

  • Stel de time-out element in een @NamedQuery of @NamedNativeQuery annotatie
  • Roep het setHint methode van de vraag koppel
  • Bel de setTimeout methode van de Transactie koppel
  • Roep het setTimeout methode van de Vraag koppel

6. Fouten in verband met de sessiestatus

Laten we nu eens kijken naar fouten als gevolg van gebruiksfouten in de slaapstand.

6.1. NonUniqueObjectException

Hibernate staat niet twee objecten met dezelfde ID toe in één sessie.

Als we in één sessie twee instanties van dezelfde Java-klasse proberen te associëren met dezelfde identifier, krijgen we een NonUniqueObjectException. We kunnen de naam en identificatie van de entiteit krijgen door de getEntityName () en getIdentifier () methoden.

Laten we, om deze fout te reproduceren, proberen twee exemplaren van Product met dezelfde id met een sessie:

openbare ongeldige gegevenSessionContainingAnId_whenIdAssociatedAgain_thenNonUniqueObjectException () {throw.expect (isA (NonUniqueObjectException.class)); thrown.expectMessage ("Er was al een ander object met dezelfde ID-waarde aan de sessie gekoppeld"); Sessie sessie = null; Transactietransactie = null; probeer {session = sessionFactory.openSession (); transactie = session.beginTransaction (); Product product = nieuw product (); product.setId (1); product.setName ("Product 1"); session.save (product); product = nieuw product (); product.setId (1); product.setName ("Product 2"); session.save (product); transactie.commit (); } catch (uitzondering e) {rollbackTransactionQuietly (transactie); gooien (e); } eindelijk {closeSessionQuietly (sessie); }}

We krijgen een NonUniqueObjectException, zoals verwacht.

Deze uitzondering doet zich vaak voor bij het opnieuw koppelen van een losgemaakt object aan een sessie door de bijwerken methode. Als de sessie een andere instantie heeft met dezelfde id geladen, krijgen we deze foutmelding. Om dit op te lossen, we kunnen de samenvoegen methode om het losgemaakte object opnieuw te bevestigen.

6.2. StaleStateException

Slaapstand gooit StaleStateExceptions wanneer het versienummer of de tijdstempelcontrole mislukt. Het geeft aan dat de sessie verouderde gegevens bevatte.

Soms wordt dit verpakt in een OptimisticLockException.

Deze fout treedt meestal op tijdens het gebruik van langlopende transacties met versiebeheer.

Bovendien kan het ook gebeuren tijdens het bijwerken of verwijderen van een entiteit als de corresponderende databaserij niet bestaat:

openbare ongeldigheid whenUpdatingNonExistingObject_thenStaleStateException () {throw.expect (isA (OptimisticLockException.class)); thrown.expectMessage ("Batch-update heeft onverwacht aantal rijen geretourneerd na update"); gegooid.expectCause (isA (StaleStateException.class)); Sessie sessie = null; Transactietransactie = null; probeer {session = sessionFactory.openSession (); transactie = session.beginTransaction (); Product product = nieuw product (); product.setId (15); product.setName ("Product1"); session.update (product); transactie.commit (); } catch (uitzondering e) {rollbackTransactionQuietly (transactie); gooien (e); } eindelijk {closeSessionQuietly (sessie); }}

Enkele andere mogelijke scenario's zijn:

  • we hebben geen gepaste strategie voor niet-opgeslagen waarde voor de entiteit gespecificeerd
  • twee gebruikers probeerden dezelfde rij bijna tegelijkertijd te verwijderen
  • we hebben handmatig een waarde ingesteld in het automatisch gegenereerde ID- of versieveld

7. Luie initialisatiefouten

We configureren koppelingen meestal om lui te worden geladen om de prestaties van de applicatie te verbeteren. De associaties worden alleen opgehaald wanneer ze voor het eerst worden gebruikt.

Hibernate vereist echter een actieve sessie om gegevens op te halen. Als de sessie al is gesloten wanneer we proberen toegang te krijgen tot een niet-geïnitialiseerde associatie, krijgen we een uitzondering.

Laten we eens kijken naar deze uitzondering en de verschillende manieren om deze op te lossen.

7.1. LazyInitializationException

LazyInitializationException geeft een poging aan om niet-geïnitialiseerde gegevens buiten een actieve sessie te laden. We kunnen deze fout in veel scenario's krijgen.

Ten eerste kunnen we deze uitzondering krijgen tijdens het openen van een luie relatie in de presentatielaag. De reden is dat de entiteit gedeeltelijk in de bedrijfslaag is geladen en de sessie is gesloten.

Ten tweede kunnen we deze fout krijgen met Spring Data als we de getOne methode. Deze methode haalt de instantie lui op.

Er zijn veel manieren om deze uitzondering op te lossen.

Allereerst kunnen we alle relaties gretig belasten.Maar dit zou van invloed zijn op de prestaties van de applicatie, omdat we gegevens laden die niet zullen worden gebruikt.

Ten tweede kunnen we de sessie open houden totdat de weergave wordt weergegeven. Dit staat bekend als de "Open sessie in weergave”En het is een antipatroon. We moeten dit vermijden, aangezien het verschillende nadelen heeft.

Ten derde kunnen we een andere sessie openen en de entiteit opnieuw koppelen om de relaties op te halen. We kunnen dit doen door de samenvoegen methode op de sessie.

Ten slotte kunnen we de vereiste koppelingen in de bedrijfslagen initialiseren. We zullen dit in de volgende sectie bespreken.

7.2. Initialiseren van relevante luie relaties in de bedrijfslaag

Er zijn veel manieren om luie relaties te initiëren.

Een optie is om ze te initialiseren door de overeenkomstige methoden op de entiteit aan te roepen. In dit geval zal Hibernate meerdere databasequery's uitvoeren, waardoor de prestaties afnemen. We noemen het het "N + 1 SELECTEREN" -probleem.

Ten tweede kunnen we gebruiken Fetch Join om de gegevens in een enkele query te krijgen. We moeten hiervoor echter aangepaste code schrijven.

Tenslotte, we kunnen entiteitsgrafieken gebruiken om alle op te halen attributen te definiëren. We kunnen de annotaties gebruiken @NamedEntityGraph, @NamedAttributeNode, en @NamedEntitySubgraph om de entiteitsgrafiek declaratief te definiëren. We kunnen ze ook programmatisch definiëren met de JPA API. Dan, we halen de hele grafiek op in een enkele aanroep door deze op te geven in de ophaalbewerking.

8. Transactieproblemen

Transacties definiëren werkeenheden en isolatie tussen gelijktijdige activiteiten. We kunnen ze op twee verschillende manieren afbakenen. Ten eerste kunnen we ze declaratief definiëren met behulp van annotaties. Ten tweede kunnen we ze programmatisch beheren met behulp van de Hibernate Transaction API.

Bovendien delegeert Hibernate het transactiebeheer aan een transactiebeheerder. Als een transactie om welke reden dan ook niet kan worden gestart, vastgelegd of teruggedraaid, genereert Hibernate een uitzondering.

We krijgen meestal een TransactionException of een IllegalArgumentException afhankelijk van de transactiemanager.

Laten we ter illustratie proberen een transactie vast te leggen die is gemarkeerd voor terugdraaien:

openbare ongeldige gegevenTxnMarkedRollbackOnly_whenCommitted_thenTransactionException () {throw.expect (isA (TransactionException.class)); throwwn.expectMessage ("Transactie is alleen gemarkeerd voor terugdraaien; kan niet vastleggen"); Sessie sessie = null; Transactietransactie = null; probeer {session = sessionFactory.openSession (); transactie = session.beginTransaction (); Product product = nieuw product (); product.setId (15); product.setName ("Product1"); session.save (product); transactie.setRollbackOnly (); transactie.commit (); } catch (uitzondering e) {rollbackTransactionQuietly (transactie); gooien (e); } eindelijk {closeSessionQuietly (sessie); }}

Evenzo kunnen andere fouten ook een uitzondering veroorzaken:

  • Het combineren van declaratieve en programmatische transacties
  • Er wordt geprobeerd een transactie te starten terwijl er al een andere actief is in de sessie
  • Proberen vast te leggen of terug te draaien zonder een transactie te starten
  • Een transactie meerdere keren proberen vast te leggen of terug te draaien

9. Gelijktijdigheidsproblemen

Hibernate ondersteunt twee vergrendelingsstrategieën om inconsistentie in de database als gevolg van gelijktijdige transacties te voorkomen: optimistisch en pessimistisch. Beiden geven een uitzondering op in geval van een vergrendelingsconflict.

Om hoge gelijktijdigheid en hoge schaalbaarheid te ondersteunen, gebruiken we doorgaans optimistische gelijktijdigheidscontrole met versiecontrole. Dit gebruikt versienummers of tijdstempels om conflicterende updates te detecteren.

OptimisticLockingException wordt gegenereerd om een ​​optimistisch vergrendelingsconflict aan te geven. We krijgen bijvoorbeeld deze foutmelding als we twee updates uitvoeren of dezelfde entiteit verwijderen zonder deze na de eerste bewerking te vernieuwen:

openbare leegte whenDeletingADeletedObject_thenOptimisticLockException () {throw.expect (isA (OptimisticLockException.class)); thrown.expectMessage ("Batch-update heeft onverwacht aantal rijen geretourneerd na update"); gegooid.expectCause (isA (StaleStateException.class)); Sessie sessie = null; Transactietransactie = null; probeer {session = sessionFactory.openSession (); transactie = session.beginTransaction (); Product product = nieuw product (); product.setId (12); product.setName ("Product 12"); session.save (product1); transactie.commit (); session.close (); session = sessionFactory.openSession (); transactie = session.beginTransaction (); product = session.get (Product.class, 12); session.createNativeQuery ("verwijderen uit product waarbij id = 12") .executeUpdate (); // We moeten vernieuwen om de fout te herstellen. // session.refresh (product); session.delete (product); transactie.commit (); } catch (uitzondering e) {rollbackTransactionQuietly (transactie); gooien (e); } eindelijk {closeSessionQuietly (sessie); }}

Evenzo kunnen we deze fout ook krijgen als twee gebruikers dezelfde entiteit bijna tegelijkertijd proberen bij te werken. In dit geval kan de eerste slagen en de tweede geeft deze fout weer.

Daarom we kunnen deze fout niet volledig vermijden zonder een pessimistische vergrendeling te introduceren. We kunnen de kans dat het voorkomt echter minimaliseren door het volgende te doen:

  • Houd de updatebewerkingen zo kort mogelijk
  • Werk entiteitsrepresentaties in de client zo vaak mogelijk bij
  • Plaats de entiteit of een waardeobject dat deze vertegenwoordigt niet in de cache
  • Vernieuw altijd de entiteitsweergave op de client na update

10. Conclusie

In dit artikel hebben we gekeken naar enkele veelvoorkomende uitzonderingen die zijn opgetreden tijdens het gebruik van Hibernate. Verder hebben we hun waarschijnlijke oorzaken en oplossingen onderzocht.

Zoals gewoonlijk is de volledige broncode te vinden op GitHub.