3 Veelvoorkomende prestatieproblemen tijdens de slaapstand en hoe u ze kunt vinden in uw logbestand

1. Inleiding

U hebt waarschijnlijk enkele van de klachten over slechte Hibernate-prestaties gelezen of misschien heeft u er zelf mee geworsteld. Ik gebruik Hibernate nu al meer dan 15 jaar en ik ben meer dan genoeg van deze problemen tegengekomen.

In de loop der jaren heb ik geleerd dat deze problemen kunnen worden vermeden en dat u er veel in uw logbestand kunt vinden. In dit bericht wil ik je laten zien hoe je er 3 kunt vinden en repareren.

2. Zoek en los prestatieproblemen op

2.1. Log SQL-verklaringen in productie

Het eerste prestatieprobleem is buitengewoon gemakkelijk te herkennen en wordt vaak genegeerd. Het is het loggen van SQL-instructies in een productieomgeving.

Het schrijven van een aantal logboekverklaringen klinkt niet als een groot probleem, en er zijn veel applicaties die precies dat doen. Maar het is buitengewoon inefficiënt, vooral via System.out.println zoals Hibernate het doet als u de show_sql parameter in uw Hibernate-configuratie naar waar:

Hibernate: selecteer order0_.id als id1_2_, order0_.orderNumber als orderNum2_2_, order0_.version als version3_2_ van aankoop Order order0_ Hibernate: selecteer items0_.order_id als order_id4_0_0_, items0_.id als id1_0_0_, items0_.id als id0_.id als id1_0_, items0_.id als id1_0_, items items0_.product_id als product_5_0_1_, items0_.quantity als hoeveelheid2_0_1_, items0_.version als versie3_0_1_ van OrderItem items0_ waar items0_.order_id =? Slaapstand: selecteer items0_.order_id als order_id4_0_0_, items0_.id als id1_0_0_, items0_.id als id1_0_1_, items0_.order_id als order_id4_0_1_, items0_.product_id als product_5_0_1_, items0_.quantity_0 items uit versie0_0. order_id =? Slaapstand: selecteer items0_.order_id als order_id4_0_0_, items0_.id als id1_0_0_, items0_.id als id1_0_1_, items0_.order_id als order_id4_0_1_, items0_.product_id als product_5_0_1_, items0_.quantity_0 items uit versie0_0. order_id =?

In een van mijn projecten heb ik de prestatie binnen een paar minuten met 20% verbeterd door te zetten show_sql naar false. Dat is het soort prestatie dat u graag rapporteert tijdens de volgende stand-up meeting 🙂

Het is vrij duidelijk hoe u dit prestatieprobleem kunt oplossen. Open gewoon uw configuratie (bijv. Uw persistence.xml-bestand) en stel het show_sql parameter naar false. Je hebt deze informatie sowieso niet nodig bij de productie.

Maar je hebt ze misschien nodig tijdens de ontwikkeling. Als u dat niet doet, gebruikt u 2 verschillende slaapstandconfiguraties (wat u niet zou moeten doen). U heeft daar ook de logboekregistratie van SQL-instructies gedeactiveerd. De oplossing hiervoor is het gebruik van 2 verschillende logconfiguraties voor ontwikkeling en productie die zijn geoptimaliseerd voor de specifieke eisen van de runtime-omgeving.

Ontwikkelingsconfiguratie

De ontwikkelingsconfiguratie moet zoveel mogelijk nuttige informatie bevatten, zodat u kunt zien hoe Hibernate samenwerkt met de database. Log daarom in ieder geval de gegenereerde SQL-statements in uw ontwikkelconfiguratie. U kunt dit doen door te activeren DEBUG bericht voor de org.hibernate.SQL categorie. Als u ook de waarden van uw bindparameters wilt zien, moet u het logniveau van org.hibernate.type.descriptor.sql naar SPOOR:

log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern =% d {HH: mm: ss, SSS}% -5p [% c] -% m% n log4j.rootLogger = info, stdout # basislogniveau voor alle berichten log4j.logger.org.hibernate = info # SQL-instructies en parameters log4j.logger.org.hibernate.SQL = debug log4j.logger.org.hibernate.type.descriptor.sql = traceren

Het volgende codefragment toont enkele voorbeeldlogboekberichten die Hibernate schrijft met deze logconfiguratie. Zoals u kunt zien, krijgt u gedetailleerde informatie over de uitgevoerde SQL-query en alle ingestelde en opgehaalde parameterwaarden:

23: 03: 22,246 DEBUG SQL: 92 - selecteer order0_.id als id1_2_, order0_.orderNumber als orderNum2_2_, order0_.version als versie3_2_ van purchaseOrder order0_ waarbij order0_.id = 1 23: 03: 22,254 TRACE BasicExtractor: 61 - geëxtraheerde waarde ( [id1_2_]: [BIGINT]) - [1] 23: 03: 22,261 TRACE BasicExtractor: 61 - geëxtraheerde waarde ([orderNum2_2_]: [VARCHAR]) - [order1] 23: 03: 22,263 TRACE BasicExtractor: 61 - geëxtraheerde waarde ( [version3_2_]: [INTEGER]) - [0]

Hibernate biedt u veel meer interne informatie over een Sessie als u de Hibernate-statistieken activeert. U kunt dit doen door de systeemeigenschap in te stellen hibernate.generate_statistics naar waar.

Maar activeer alsjeblieft alleen de statistieken over je ontwikkel- of testomgeving. Het verzamelen van al deze informatie vertraagt ​​uw applicatie en u kunt zelf prestatieproblemen veroorzaken als u deze in productie activeert.

U kunt enkele voorbeeldstatistieken bekijken in het volgende codefragment:

23: 04: 12,123 INFO StatisticalLoggingSessionEventListener: 258 - Session Metrics {23793 nanoseconden besteed aan het verwerven van 1 JDBC-verbinding; 0 nanoseconden besteed aan het vrijgeven van 0 JDBC-verbindingen; 394686 nanoseconden besteed aan het voorbereiden van 4 JDBC-verklaringen; 2528603 nanoseconden besteed aan het uitvoeren van 4 JDBC-statements; 0 nanoseconden besteed aan het uitvoeren van 0 JDBC-batches; 0 nanoseconden besteed aan het uitvoeren van 0 L2C-putten; 0 nanoseconden besteed aan het uitvoeren van 0 L2C-hits; 0 nanoseconden besteed aan het uitvoeren van 0 L2C-missers; 9700599 nanoseconden besteed aan het uitvoeren van 1 flushes (doorspoelen van in totaal 9 entiteiten en 3 verzamelingen); 42921 nanoseconden besteed aan het uitvoeren van 1 gedeeltelijke spoeling (in totaal 0 entiteiten en 0 verzamelingen leeggemaakt)}

Ik gebruik deze statistieken regelmatig in mijn dagelijkse werk om prestatieproblemen te vinden voordat ze zich voordoen in de productie en ik zou daarover verschillende berichten kunnen schrijven. Laten we ons dus concentreren op de belangrijkste.

De regels 2 t / m 5 laten zien hoeveel JDBC-verbindingen en statements Hibernate tijdens deze sessie heeft gebruikt en hoeveel tijd het eraan heeft besteed. Bekijk deze waarden altijd en vergelijk ze met uw verwachtingen.

Als er veel meer uitspraken zijn dan u had verwacht, heeft u waarschijnlijk het meest voorkomende prestatieprobleem, het n + 1 select-probleem. U vindt het in bijna alle toepassingen en het kan tot enorme prestatieproblemen leiden bij een grotere database. Ik leg dit probleem in de volgende sectie in meer details uit.

De regels 7 tot 9 laten zien hoe Hibernate samenwerkte met de cache van het 2e niveau. Dit is een van de 3 caches van Hibernate en slaat entiteiten op een sessie-onafhankelijke manier op. Als u het 2e niveau in uw toepassing gebruikt, moet u deze statistieken altijd controleren om te zien of Hibernate de entiteiten daar vandaan haalt.

Productieconfiguratie

De productieconfiguratie moet worden geoptimaliseerd voor prestaties en alle berichten die niet dringend nodig zijn, moeten worden vermeden. Over het algemeen betekent dit dat u alleen foutmeldingen moet loggen. Als u Log4j gebruikt, kunt u dat bereiken met de volgende configuratie:

Als u Log4j gebruikt, kunt u dat bereiken met de volgende configuratie:

log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern =% d {HH: mm: ss, SSS}% -5p [% c] -% m% n log4j.rootLogger = info, stdout # basislogniveau voor alle berichten log4j.logger.org.hibernate = fout

2.2. N + 1 Selecteer Probleem

Zoals ik al heb uitgelegd, is het n + 1 select-probleem het meest voorkomende prestatieprobleem. Veel ontwikkelaars geven de schuld aan het concept van OR-Mapping voor dit probleem, en ze hebben niet helemaal ongelijk. Maar u kunt het gemakkelijk vermijden als u begrijpt hoe Hibernate lui opgehaalde relaties behandelt. De ontwikkelaar is daarom ook de schuldige, want het is zijn verantwoordelijkheid om dit soort problemen te vermijden. Dus laat me eerst uitleggen waarom dit probleem bestaat en u vervolgens een gemakkelijke manier laten zien om dit te voorkomen. Als u al bekend bent met de n + 1 select-problemen, kunt u direct naar de oplossing gaan.

Hibernate biedt een zeer handige mapping voor relaties tussen entiteiten. Je hebt alleen een attribuut nodig met het type van de gerelateerde entiteit en een paar annotaties om het te definiëren:

@Entity @Table (name = "purchaseOrder") openbare klasse Order implementeert Serializable {@OneToMany (mappedBy = "order", fetch = FetchType.LAZY) privé Set items = nieuwe HashSet (); ...}

Wanneer u nu een Bestellen entiteit uit de database, hoeft u alleen maar de getItems () methode om alle items van deze bestelling te krijgen. Hibernate verbergt de vereiste databasequery's om het gerelateerde te krijgen Bestel item entiteiten uit de database.

Toen u met Hibernate begon, heeft u waarschijnlijk geleerd dat u FetchType.LAZY voor de meeste relaties en dat dit de standaardinstelling is voor te veel relaties. Dit vertelt Hibernate om alleen de gerelateerde entiteiten op te halen als je het attribuut gebruikt dat de relatie in kaart brengt. Alleen de gegevens ophalen die u nodig hebt, is in het algemeen een goede zaak, maar het vereist ook Hibernate om een ​​extra query uit te voeren om elke relatie te initialiseren. Dit kan resulteren in een groot aantal zoekopdrachten als u aan een lijst met entiteiten werkt, zoals ik doe in het volgende codefragment:

Lijst met orders = em.createQuery ("SELECTEER o VAN BESTELLING o"). GetResultList (); voor (Order order: orders) {log.info ("Order:" + order.getOrderNumber ()); log.info ("Aantal items:" + order.getItems (). size ()); }

U zou waarschijnlijk niet verwachten dat deze paar regels code honderden of zelfs duizenden databasequery's kunnen genereren. Maar het doet het als u FetchType.LAZY voor de relatie met de Bestel item entiteit:

22: 47: 30,065 DEBUG SQL: 92 - selecteer order0_.id als id1_2_, order0_.orderNumber als orderNum2_2_, order0_.version als versie3_2_ van aankoop Order order0_ 22: 47: 30,136 INFO NamedEntityGraphTest: 58 - Order: order1 22: 47: 30,140 DEBUG SQL: 92 - selecteer items0_.order_id als order_id4_0_0_, items0_.id als id1_0_0_, items0_.id als id1_0_1_, items0_.order_id als order_id4_0_1_, items0_.product_id als product_5_0_1_, items0_quantity_.id als id1_0_1_, items0_.order_id als order_id4_0_1_, items0_.product_id als product_5_0_1_, items0_quantity_0 items uit versie0_0_0 items van versie0_0. items0_.order_id =? 22: 47: 30,171 INFO NamedEntityGraphTest: 59 - Aantal items: 2 22: 47: 30,171 INFO NamedEntityGraphTest: 58 - Order: order2 22: 47: 30,172 DEBUG SQL: 92 - selecteer items0_.order_id als order_id4_0_0_, items0_.id als id1_0_0 als id1_0_0 , items0_.id als id1_0_1_, items0_.order_id als order_id4_0_1_, items0_.product_id als product_5_0_1_, items0_.quantity als hoeveelheid2_0_1_, items0_.versie als versie3_0_1_ van OrderItem items0_ waar items0_.order_id =? 22: 47: 30,174 INFO NamedEntityGraphTest: 59 - Aantal items: 2 22: 47: 30,174 INFO NamedEntityGraphTest: 58 - Order: order3 22: 47: 30,174 DEBUG SQL: 92 - selecteer items0_.order_id als order_id4_0_0_, items0_.id als id1_0_0 als id1_0_0 , items0_.id als id1_0_1_, items0_.order_id als order_id4_0_1_, items0_.product_id als product_5_0_1_, items0_.quantity als hoeveelheid2_0_1_, items0_.versie als versie3_0_1_ van OrderItem items0_ waar items0_.order_id =? 22: 47: 30,176 INFO NamedEntityGraphTest: 59 - Aantal items: 2

Hibernate voert één zoekopdracht uit om alles te krijgen Bestellen entiteiten en een extra voor elk van de n Bestellen entiteiten om het bestel item relatie. U weet nu dus waarom dit soort probleem n + 1 select issue wordt genoemd en waarom het enorme prestatieproblemen kan veroorzaken.

Wat het nog erger maakt, is dat je het vaak niet herkent in een kleine testdatabase als je je Hibernate-statistieken niet hebt gecontroleerd. Het codefragment vereist slechts enkele tientallen zoekopdrachten als de testdatabase niet veel bestellingen bevat. Maar dat zal heel anders zijn als u uw productieve database gebruikt die er enkele duizenden bevat.

Ik zei eerder dat je deze problemen gemakkelijk kunt vermijden. En dat is waar. U hoeft alleen de orderItem-relatie te initialiseren wanneer u het Bestellen entiteiten uit de database.

Maar doe dat alstublieft alleen als u de relatie in uw bedrijfscode gebruikt en niet FetchType.EAGER om altijd de gerelateerde entiteiten op te halen. Dat vervangt gewoon uw n + 1-probleem door een ander prestatieprobleem.

Initialiseer een relatie met een @NamedEntityGraph

Er zijn verschillende opties om relaties te initialiseren. Ik gebruik liever een @NamedEntityGraph dat is een van mijn favoriete functies die is geïntroduceerd in JPA 2.1. Het biedt een query-onafhankelijke manier om een ​​grafiek van entiteiten op te geven die Hibernate uit de database zal ophalen. In het volgende codefragment ziet u een voorbeeld van een eenvoudige grafiek waarmee Hibernate het itemkenmerk van een entiteit gretig kan ophalen:

@Entity @Table (name = "purchase_order") @NamedEntityGraph (name = "graph.Order.items", attributeNodes = @NamedAttributeNode ("items")) openbare klasse Order implementeert Serializable {...}

U hoeft niet veel te doen om een ​​entiteitsgrafiek te definiëren met een @NamedEntityGraph annotatie. U hoeft alleen maar een unieke naam op te geven voor de grafiek en één @NamedAttributeNode annotatie voor elk kenmerk dat Hibernate gretig zal ophalen. In dit voorbeeld is het alleen het itemkenmerk dat de relatie tussen een Bestellen en meerdere Bestel item entiteiten.

Nu kunt u de entiteitsgrafiek gebruiken om het ophaalgedrag of een specifieke query te regelen. U moet daarom een ​​instantiëren EntityGraph gebaseerd op de @NamedEntityGraph definitie en geef het als een hint naar de EntityManager.find () methode of uw vraag. Ik doe dit in het volgende codefragment waar ik het Bestellen entiteit met id 1 uit de database:

EntityGraph graph = this.em.getEntityGraph ("graph.Order.items"); Kaarthints = nieuwe HashMap (); hints.put ("javax.persistence.fetchgraph", grafiek); retourneer this.em.find (Order.class, 1L, hints);

Hibernate gebruikt deze informatie om één SQL-instructie te maken die de attributen van de Bestellen entiteit en de attributen van de entiteitsgrafiek uit de database:

17:34: 51,310 DEBUG [org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter] (pool-2-thread-1) LoadPlan (entiteit = blog.thoughts.on.java.jpa21.entity.graph.model. Order) - Retouren - EntityReturnImpl (entity = blog.thoughts.on.java.jpa21.entity.graph.model.Order, querySpaceUid =, pad = blog.thoughts.on.java.jpa21.entity.graph.model.Order) - CollectionAttributeFetchImpl (collectie = blog.thoughts.on.java.jpa21.entity.graph.model.Order.items, querySpaceUid =, pad = blog.thoughts.on.java.jpa21.entity.graph.model.Order.items) - (collectie-element) CollectionFetchableElementEntityGraph (entity = blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem, querySpaceUid =, pad = blog.thoughts.on.java.jpa21.entity.graph.model.Order. items.) - EntityAttributeFetchImpl (entity = blog.thoughts.on.java.jpa21.entity.graph.model.Product, querySpaceUid =, path = blog.thoughts.on.java.jpa21.entity.graph.model.Order.items ..product) - QuerySpaces - EntityQuerySpaceImpl (uid =, entity = blog.thoughts.on.java.jpa21.entity.graph.model .Order) - SQL-tabel alias toewijzing - order0_ - alias achtervoegsel - 0_ - achtervoegsel sleutelkolommen - {id1_2_0_} - JOIN (JoinDefinedByMetadata (items)): -> - CollectionQuerySpaceImpl (uid =, collection = blog.thoughts.on.java. jpa21.entity.graph.model.Order.items) - SQL-tabel alias toewijzing - items1_ - alias achtervoegsel - 1_ - achtervoegsel sleutelkolommen - {order_id4_2_1_} - entiteit-element alias achtervoegsel - 2_ - 2_entity-element achtervoegsel sleutel kolommen - id1_0_2_ - JOIN (JoinDefinedByMetadata (elements)): -> - EntityQuerySpaceImpl (uid =, entity = blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem) - SQL-tabel aliastoewijzing - items1_ - aliasachtervoegsel - 2_ - achtervoegsel sleutelkolommen - {id1_0_2_} - JOIN (JoinDefinedByMetadata (product)): -> - EntityQuerySpaceImpl (uid =, entity = blog.thoughts.on.java.jpa21.entity.graph.model.Product) - SQL-tabel aliastoewijzing - product2_ - alias achtervoegsel - 3_ - achtervoegsel sleutelkolommen - {id1_1_3_} 17:34: 51,311 DEBUG [org.hibernate.loader.entity.plan.EntityLoader] (pool-2-thread-1) Statische selectie f of entiteit blog.thoughts.on.java.jpa21.entity.graph.model.Order [NONE: -1]: selecteer order0_.id als id1_2_0_, order0_.orderNumber als orderNum2_2_0_, order0_.version als versie3_2_0_, items1_.order_id als order_id4_2_1_ , items1_.id als id1_0_1_, items1_.id als id1_0_2_, items1_.order_id als order_id4_0_2_, items1_.product_id als product_5_0_2_, items1_.quantity als hoeveelheid2_0_2_, items1_.version als version3_0_2_, product2__3.n_3_, product2__3.id als id als id als id als id .version als versie3_1_3_ van purchase_order order0_ left outer join OrderItem items1_ op order0_.id = items1_.order_id linker buitenste join Product product2_ op items1_.product_id = product2_.id waar order0_.id =?

Het initialiseren van slechts één relatie is goed genoeg voor een blogpost, maar in een echt project wilt u waarschijnlijk complexere grafieken maken. Dus laten we dat doen.

U kunt natuurlijk een scala aan @NamedAttributeNode annotaties om meerdere attributen van dezelfde entiteit op te halen en u kunt @NamedSubGraph om het ophaalgedrag te definiëren voor een extra niveau van entiteiten. Ik gebruik dat in het volgende codefragment om niet alleen alle gerelateerde op te halen Bestel item entiteiten maar ook de Product entiteit voor elk Bestel item:

@Entity @Table (name = "purchase_order") @NamedEntityGraph (name = "graph.Order.items", attributeNodes = @NamedAttributeNode (value = "items", subgraph = "items"), subgraphs = @NamedSubgraph (name = " items ", attributeNodes = @NamedAttributeNode (" product "))) openbare klasse Order implementeert Serializable {...}

Zoals u kunt zien, is de definitie van een @NamedSubGraph lijkt erg op de definitie van een @NamedEntityGraph. U kunt dan naar deze subgraaf verwijzen in een @NamedAttributeNode annotatie om het ophaalgedrag voor dit specifieke attribuut te definiëren.

De combinatie van deze annotaties stelt u in staat om complexe entiteitsgrafieken te definiëren die u kunt gebruiken om alle relaties die u in uw use-case gebruikt te initialiseren en n + 1 select-problemen te vermijden. Als u uw entiteitsgrafiek tijdens runtime dynamisch wilt specificeren, kunt u dit ook doen via een Java API.

2.3. Werk entiteiten een voor een bij

Het een voor een bijwerken van entiteiten voelt heel natuurlijk aan als je op een objectgeoriënteerde manier denkt. U krijgt gewoon de entiteiten die u wilt bijwerken en roept een paar setter-methoden aan om hun attributen te wijzigen, zoals u dat met elk ander object doet.

Deze aanpak werkt prima als u maar een paar entiteiten wijzigt.Maar het wordt erg inefficiënt wanneer u met een lijst met entiteiten werkt en het is het derde prestatieprobleem dat u gemakkelijk in uw logbestand kunt zien. U hoeft alleen maar te zoeken naar een aantal SQL UPDATE-instructies die er volledig hetzelfde uitzien, zoals u kunt zien in het volgende logbestand:

22: 58: 05,829 DEBUG SQL: 92 - selecteer product0_.id als id1_1_, product0_.name als naam2_1_, product0_.price als prijs3_1_, product0_.version als versie4_1_ van product product0_ 22: 58: 05,883 DEBUG SQL: 92 - update productset naam = ?, prijs = ?, versie =? waar id =? en versie =? 22: 58: 05,889 DEBUG SQL: 92 - update productset naam = ?, prijs = ?, versie =? waar id =? en versie =? 22: 58: 05,891 DEBUG SQL: 92 - update productset naam = ?, prijs = ?, versie =? waar id =? en versie =? 22: 58: 05,893 DEBUG SQL: 92 - update Naam productset = ?, prijs = ?, versie =? waar id =? en versie =? 22: 58: 05,900 DEBUG SQL: 92 - update productset naam = ?, prijs = ?, versie =? waar id =? en versie =?

De relationele weergave van de databaserecords is veel beter geschikt voor deze use-cases dan de objectgeoriënteerde. Met SQL zou u slechts één SQL-instructie kunnen schrijven die alle records bijwerkt die u wilt wijzigen.

U kunt hetzelfde doen met Hibernate als u JPQL, native SQL of de CriteriaUpdate API gebruikt. Alle drie lijken ze erg op elkaar, dus laten we in dit voorbeeld JPQL gebruiken.

U kunt een JPQL UPDATE-instructie op dezelfde manier definiëren als u die kent van SQL. U definieert gewoon welke entiteit u wilt bijwerken, hoe u de waarden van zijn attributen wijzigt en de betrokken entiteiten beperkt in de WHERE-instructie.

Je kunt er een voorbeeld van zien in het volgende codefragment waarin ik de prijs van alle producten met 10% verhoog:

em.createQuery ("UPDATE Product p SET p.price = p.price * 0.1"). executeUpdate ();

Hibernate maakt een SQL UPDATE-instructie op basis van de JPQL-instructie en stuurt deze naar de database die de updatebewerking uitvoert.

Het is vrij duidelijk dat deze aanpak veel sneller is als u een groot aantal entiteiten moet bijwerken. Maar het heeft ook een nadeel. Hibernate weet niet welke entiteiten worden beïnvloed door de updatebewerking en werkt de cache op het eerste niveau niet bij. U moet er daarom voor zorgen dat u een entiteit met een JPQL-instructie binnen dezelfde Hibernate-sessie niet leest en bijwerkt, of dat u deze loskoppelt om deze uit de cache te verwijderen.

3. Samenvatting

In dit bericht heb ik het je laten zien 3 Prestatieproblemen tijdens de slaapstand die u kunt vinden in uw logbestanden.

Twee daarvan werden veroorzaakt door een groot aantal SQL-instructies. Dit is een veelvoorkomende reden voor prestatieproblemen als u met Hibernate werkt. Hibernate verbergt de databasetoegang achter zijn API en dat maakt het vaak moeilijk om het werkelijke aantal SQL-statements te raden. Controleer daarom altijd de uitgevoerde SQL-instructies wanneer u een wijziging aanbrengt in uw persistentielaag.


$config[zx-auto] not found$config[zx-overlay] not found