Java >> Java tutorial >  >> Tag >> String

Sådan parses en streng til en EntityGraph med Hibernate 5.4

JPA 2.1 introducerede de annotationsbaserede Named Entity Graphs og Entity Graph API. De giver forespørgselsuafhængige måder at definere, hvilke entitetsattributter Hibernate skal hente fra databasen. Og selvom du ikke kan udelade ivrigt hentede associationer eller attributter med Hibernate, kan du angive en forespørgselsspecifik ivrig indlæsning for en graf over associationer.

Det lyder godt, ikke?

Ideen er fantastisk, men definitionen af ​​grafen føles besværlig. Jeg vil vise dig et eksempel på det, efter at vi har taget et hurtigt kig på de enheder, som vi vil bruge i denne artikel.

Eksemplet

Lad os bruge et simpelt eksempel, der modellerer ordrer i en onlinebutik med kunder, bestillingsvarer, produkter og leverandører.

Orden entitet vil være rodobjektet i alle de følgende eksempler. Det modellerer en mange-til-en-tilknytning til en kunde og en en-til-mange tilknytning til OrderItem s. Hver OrderItem refererer til ét produkt , som tilhører én leverandør .

Ok, lad os vende tilbage til definitionen af ​​enhedsgrafen og sammenligne API'en fra JPA 2.1 med den nye i Hibernate 5.4.

Enhedsgrafer i JPA og Hibernate <5.4

Her er et hurtigt eksempel på en @NamedEntityGraph, som du kan bruge på en Ordre enhed for at hente den sammen med alle tilknyttede elementer . Og den refererede undergraf itemsGraph beder Hibernate om at indlæse produktet der refereres til af hver vare .

@NamedEntityGraph(name = "graph.Order.items", 
               attributeNodes = @NamedAttributeNode(value = "items", subgraph = "itemsGraph"), 
               subgraphs = @NamedSubgraph(name = "itemsGraph", attributeNodes = @NamedAttributeNode("product")))

Når du har defineret grafen, skal du instansiere den, før du kan bruge den med et kald af EntityManager's find metode, en JPQL-forespørgsel eller en kriterieforespørgsel.

EntityGraph graph = em.getEntityGraph("graph.Order.items");

Order newOrder = em.find(Order.class, 1L, Collections.singletonMap("javax.persistence.fetchgraph", graph));

Det bliver ikke bedre, hvis du bruger Entity Graph API. Du skal derefter instansiere en ny EntityGraph objekt, skal du oprette en undergraf for det og en anden undergraf til undergrafen.

EntityGraph<Order> graph = em.createEntityGraph(Order.class);
Subgraph<Object> itemGraph = graph.addSubgraph("items");
itemGraph.addSubgraph("product");

Order newOrder = em.find(Order.class, 1L, Collections.singletonMap("javax.persistence.fetchgraph", graph));

Uafhængigt af hvordan du opretter din EntityGraph , Hibernate udvider den udførte SQL-sætning til at vælge kolonnerne for alle enheder, der henvises til i grafen. Så hvis din graf initialiserer elementerne af en Ordre og produktet af hver vare , Hibernate skal vælge alle databasekolonner, der er kortlagt af Orden , OrderItem og Produkt enhed.

14:24:17,342 DEBUG SQL:94 - 
    select
        order0_.id as id1_3_0_,
        order0_.customer_id as customer4_3_0_,
        order0_.orderNumber as orderNum2_3_0_,
        order0_.version as version3_3_0_,
        items1_.order_id as order_id4_1_1_,
        items1_.id as id1_1_1_,
        items1_.id as id1_1_2_,
        items1_.order_id as order_id4_1_2_,
        items1_.product_id as product_5_1_2_,
        items1_.quantity as quantity2_1_2_,
        items1_.version as version3_1_2_,
        product2_.id as id1_2_3_,
        product2_.name as name2_2_3_,
        product2_.price as price3_2_3_,
        product2_.supplier_id as supplier5_2_3_,
        product2_.version as version4_2_3_ 
    from
        purchaseOrder order0_ 
    left outer join
        OrderItem items1_ 
            on order0_.id=items1_.order_id 
    left outer join
        Product product2_ 
            on items1_.product_id=product2_.id 
    where
        order0_.id=?

Enhedsgrafer med Hibernate 5.4

Hibernate 5.4 bringer to forbedringer til denne tilgang. Hibernate kan nu parse en enhedsgraf fra en angivet streng , og det giver dig mulighed for at flette flere grafer til én.

Lad os tage et kig på dem begge.

Parse en streng ind i en EntityGraph

Hibernates GraphParser klasse giver et sæt statiske metoder til at parse en streng ind i en ny RootGraph objekt eller for at parse det i en eksisterende RootGraph eller SubGraph objekt.

Før vi tager et kig på formatet af den parsede streng , husk, at Hibernate bruger ivrig hentning til alle attributter, der ikke kortlægger en tilknytning. Så det gør ingen forskel, om du inkluderer dem i grafen eller ej.

Formatet på strengen er ret simpelt. Du starter på rodenheden og angiver en kommasepareret liste over attributter, som du vil inkludere i grafen. Følgende kodestykke viser en simpel EntityGraph, der kun henter emnerne attribut for Ordre enhed.

RootGraph graph = GraphParser.parse(Order.class, "items", em);

Order newOrder = em.find(Order.class, 1L, Collections.singletonMap("javax.persistence.fetchgraph", graph));

emnerne attribut kortlægger en-til-mange-tilknytningen til OrderItem enhed. I mange tilfælde vil du måske også definere en tilknyttet enheds henteadfærd. Du kan gøre det ved at angive en kommasepareret liste over attributnavne i runde parenteser. Jeg bruger denne notation i følgende eksempel til at oprette en EntityGraph der indlæser en Ordre enhed med alle tilknyttede elementer . For hver vare , vil jeg også hente det refererede produkt og leverandøren af det pågældende produkt.

RootGraph graph = GraphParser.parse(Order.class, "items(product(supplier))", em);

Order newOrder = em.find(Order.class, 1L, Collections.singletonMap("javax.persistence.fetchgraph", graph));

Når du udfører dette korte kodestykke, vil du se, at Hibernate bruger denne EntityGraph på samme måde som den bruger en graf defineret via @NamedEntityGraph annotation eller Entity Graph API. Den udvider SQL-sætningen til at vælge alle databasekolonner, der er kortlagt af de entiteter, der er inkluderet i EntityGraph .

14:26:02,824 DEBUG SQL:94 - 
    select
        order0_.id as id1_3_0_,
        order0_.customer_id as customer4_3_0_,
        order0_.orderNumber as orderNum2_3_0_,
        order0_.version as version3_3_0_,
        items1_.order_id as order_id4_1_1_,
        items1_.id as id1_1_1_,
        items1_.id as id1_1_2_,
        items1_.order_id as order_id4_1_2_,
        items1_.product_id as product_5_1_2_,
        items1_.quantity as quantity2_1_2_,
        items1_.version as version3_1_2_,
        product2_.id as id1_2_3_,
        product2_.name as name2_2_3_,
        product2_.price as price3_2_3_,
        product2_.supplier_id as supplier5_2_3_,
        product2_.version as version4_2_3_,
        supplier3_.id as id1_4_4_,
        supplier3_.name as name2_4_4_ 
    from
        purchaseOrder order0_ 
    left outer join
        OrderItem items1_ 
            on order0_.id=items1_.order_id 
    left outer join
        Product product2_ 
            on items1_.product_id=product2_.id 
    left outer join
        Supplier supplier3_ 
            on product2_.supplier_id=supplier3_.id 
    where
        order0_.id=?

Som du har set i kodestykkerne, kan du nemt definere en EntityGraph der omfatter flere foreninger. Hvis du sammenligner det med JPA's grafdefinitioner, som jeg viste dig i begyndelsen af ​​denne artikel, kan du se, at Hibernates nye streng parsing-funktionen gør grafdefinitionen meget nemmere.

Kombiner flere grafer til én EntityGraph

En anden funktion tilføjet i Hibernate 5.4 giver dig mulighed for at flette flere grafer til én. Det giver dig mulighed for at definere grafer uafhængigt af hinanden og kombinere dem, hvis det er nødvendigt.

I eksemplet med denne artikel kan du definere en EntityGraph der henter kunden af ​​en Ordre og kombiner det med grafen, som vi brugte i det foregående eksempel. Så Hibernate ville hente ordren enhed med den tilknyttede Kunde , OrderItem s og Produkt s.

Det eneste du skal gøre for at flette flere grafer er at kalde den statiske flet metode på Hibernates EntityGraphs klasse med referencer til den aktuelle session eller EntityManager , den klasse, som grafen skal anvendes på, og flere EntityGraph genstande.

RootGraph graph1 = GraphParser.parse(Order.class, "items(product(supplier))", em);
RootGraph graph2 = GraphParser.parse(Order.class, "customer", em);
EntityGraph graph = EntityGraphs.merge(em, Order.class, graph1, graph2);

Order newOrder = em.find(Order.class, 1L, Collections.singletonMap("javax.persistence.fetchgraph", graph));

Som du kan se i logoutputtet, flettede Hibernate de to grafer og indlæste ordreenheden med dens kunde , OrderItem s, Produkt s og leverandør s.

13:39:33,975 DEBUG SQL:94 - 
    select
        order0_.id as id1_3_0_,
        order0_.customer_id as customer4_3_0_,
        order0_.orderNumber as orderNum2_3_0_,
        order0_.version as version3_3_0_,
        customer1_.id as id1_0_1_,
        customer1_.name as name2_0_1_,
        items2_.order_id as order_id4_1_2_,
        items2_.id as id1_1_2_,
        items2_.id as id1_1_3_,
        items2_.order_id as order_id4_1_3_,
        items2_.product_id as product_5_1_3_,
        items2_.quantity as quantity2_1_3_,
        items2_.version as version3_1_3_,
        product3_.id as id1_2_4_,
        product3_.name as name2_2_4_,
        product3_.price as price3_2_4_,
        product3_.supplier_id as supplier5_2_4_,
        product3_.version as version4_2_4_,
        supplier4_.id as id1_4_5_,
        supplier4_.name as name2_4_5_ 
    from
        purchaseOrder order0_ 
    left outer join
        Customer customer1_ 
            on order0_.customer_id=customer1_.id 
    left outer join
        OrderItem items2_ 
            on order0_.id=items2_.order_id 
    left outer join
        Product product3_ 
            on items2_.product_id=product3_.id 
    left outer join
        Supplier supplier4_ 
            on product3_.supplier_id=supplier4_.id 
    where
        order0_.id=?

Denne forespørgsel viser også en ulempe ved enorme enhedsgrafer. De tvinger Hibernate til at vælge en masse databasekolonner, så den kan instansiere de anmodede entiteter. Sørg for, at du virkelig har brug for disse entiteter med alle deres egenskaber. Ellers er en enklere fremskrivning, f.eks. ved hjælp af en DTO, den bedre tilgang.

Konklusion

Entitetsgrafer er en kraftfuld funktion til at hente en enhed og initialisere de nødvendige tilknytninger. Den eneste ulempe ved disse grafer er, at JPA kun giver en besværlig API og et sæt annoteringer til at definere dem.

Det ændrer sig med Hibernate 5.4. Du kan nu definere hele grafen i en simpel streng . Du kan derefter parse strengen med metoderne i Hibernates GraphParser klasse og brug den returnerede RootGraph på samme måde som du bruger enhver anden EntityGraph .


Java tag