Java >> Java tutorial >  >> Tag >> hibernate

Hibernates StatelessSession - Hvad det er, og hvordan man bruger det

Nogle af Hibernates kernefunktioner er automatiske dirty checks, flushes og 1. niveaus cache. De gør implementeringen af ​​de fleste standard use cases enkel og effektiv. Men de tilføjer også en masse skjult kompleksitet og passer ikke godt til alle anvendelsestilfælde. Dit typiske importjob om natten eller de fleste andre use cases, der udfører mange skriveoperationer, drager ikke fordel af disse funktioner. De bremser ofte endda sådanne brugssager. I disse situationer, Hibernates StatelessSession måske passe bedre.

Hvad er en StatelessSession ?

StatelessSession er en proprietær Hibernate-funktion, der giver en kommandoorienteret API, der er meget tættere på JDBC. Jeg vil vise dig et par eksempler på, hvordan du bruger det til at implementere typiske læse- og skriveoperationer i denne artikel. Men før vi ser nærmere på StatelessSession grænseflade, skal vi tale om de konceptuelle forskelle fra en standard session grænseflade.

Hibernates StatelessSession giver ikke en cache på 1. niveau, automatiserede dirty checks eller skrive-behind-automatisering. Det giver heller ikke doven indlæsning af dine administrerede foreninger og bruger ikke 2. niveau eller forespørgselscache. Enhver handling udført via en StatelessSession udløser heller ikke livscyklushændelser eller interceptorer.

I stedet for alle de automatiske funktioner, som en almindelig session eller JPA's EntityManager giver, StatelessSession giver dig fuld kontrol over de udførte SQL-sætninger. Hvis du vil hente nogle data fra databasen eller initialisere en tilknytning, skal du skrive og udføre en forespørgsel til den. Og hvis du opretter et nyt eller ændrer et eksisterende objektobjekt, skal du kalde indsæt , opdatering eller slet metode på StatelessSession grænseflade for at fortsætte din ændring.

Det kræver, at du tænker mere over den tekniske side af dit vedholdenhedslag. Men hvis din use case ikke kræver automatisk beskidt kontrol, doven indlæsning eller caches på 1. niveau, ved hjælp af en StatelessSession reducerer også drastisk Hibernates ydeevne overhead. Det gør Hibernates StatelessSession velegnet til brugssager, der importerer eller opdaterer et stort sæt data. Det er især tilfældet, hvis du allerede bruger Hibernate til andre dele af din applikation og ønsker at genbruge din enhedsmodel.

Du kan også prøve StatelessSession hvis du har brug for at hente mange entitetsobjekter, som du ikke vil ændre, og hvis du ikke kræver nogen doven hentning af tilknyttede entiteter. Men forespørgsler, der returnerer use case-specifikke DTO-projektioner, passer ofte bedre til disse use cases.

Sådan bruger du en StatelessSession ?

Lad os bruge en StatelessSession til at læse og skrive entitetsobjekter. Du kan få en StatelessSession instans på samme måde som den normale session eksempel. Hvis din applikation kører på en applikationsserver eller er baseret på Spring, kan du blot injicere en StatelessSession eksempel. Og hvis du bruger almindelig dvaletilstand, kan du kalde openStatelessSession metode på din SessionFactory og brug den til at starte en transaktion.

StatelessSession statelessSession = sf.openStatelessSession();
statelessSession.getTransaction().begin();

// do something

statelessSession.getTransaction().commit();

Når du har fået en StatelessSession for eksempel kan du bruge det til at læse og skrive data.

Indsættelse og opdatering af enheder ved hjælp af en StatelessSession

De fleste projekter bruger en StatelessSession at indsætte eller opdatere enorme datasæt. Så lad os starte med 2 simple skriveoperationer.

De vigtigste metoder, du skal vide, når du implementerer skriveoperationer ved hjælp af en StatelessSession er metoderne indsæt , opdatering og slet . Du skal kalde dem, hvis du vil bevare et nyt objektobjekt eller opdatere eller slette et eksisterende. Når du gør det, skal du være opmærksom på, at en StatelessSession understøtter ikke cascading. Så du skal udløse dine skriveoperationer for hvert entitetsobjekt, du ønsker skal bestå.

I det følgende testtilfælde vil jeg indsætte en ny ChessPlayer entity-objekt og ret en tastefejl i fornavn bagefter.

StatelessSession statelessSession = sf.openStatelessSession();
statelessSession.getTransaction().begin();

ChessPlayer player = new ChessPlayer();
player.setFirstName("Torben");
player.setLastName("Janssen");

log.info("Perform insert operation");
statelessSession.insert(player);

log.info("Update firstName");
player.setFirstName("Thorben");
statelessSession.update(player);

statelessSession.getTransaction().commit();

Du har sikkert allerede genkendt de vigtigste forskelle, hvis du er bekendt med Hibernates Session interface eller JPA's EntityManager . Jeg kaldte indsættet metode til at fortsætte den nye ChessPlayer objektet og opdateringen metode til at bevare det ændrede fornavn .

Som jeg nævnte tidligere, en StatelessSession giver ikke en cache på 1. niveau, beskidte checks og automatiske bagskrivningsoptimeringer. Derfor udfører Hibernate straks en SQL INSERT-sætning, når du kalder insert metode med et enhedsobjekt. Og Hibernate registrerer ikke det ændrede fornavn attribut. Du skal ringe til opdateringen metode, hvis du ønsker at fastholde denne ændring. Hibernate udfører derefter straks en SQL UPDATE-sætning.

Hvis du bruger min anbefalede logkonfiguration til udviklingssystemer, kan du se alt det i logoutputtet.

17:46:23,963 INFO  [com.thorben.janssen.TestStatelessSession] - Perform insert operation
17:46:23,968 DEBUG [org.hibernate.SQL] - 
    select
        nextval('player_seq')
17:46:23,983 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        ChessPlayer
        (birthDate, firstName, lastName, version, id) 
    values
        (?, ?, ?, ?, ?)
17:46:23,988 INFO  [com.thorben.janssen.TestStatelessSession] - Update firstName
17:46:23,989 DEBUG [org.hibernate.SQL] - 
    update
        ChessPlayer 
    set
        birthDate=?,
        firstName=?,
        lastName=?,
        version=? 
    where
        id=? 
        and version=?

Som du kan se i dette eksempel, kræver det, at du udløser alle databaseinteraktioner, hvis du ikke har en cache på 1. niveau, automatiske snavsede kontroller og flush-operationer. Dette giver dig fuld kontrol over udførelsen af ​​SQL-sætningerne, og det giver bedre ydeevne, når du skriver store datasæt.

Læsning af entitetsobjekter ved hjælp af en StatelessSession

Når du bruger en StatelessSession for at læse entitetsobjekter fra databasen, ser din kode identisk ud med den, der bruger en standard session . Men der er et par vigtige interne forskelle i Hibernate, du skal vide.

Jeg nævnte tidligere, at en StatelessSession giver ikke doven indlæsning. Derfor skal du initialisere alle de nødvendige tilknytninger, når du henter et entitetsobjekt fra databasen. Ellers kaster Hibernate en LazyInitializationException når du tilgår foreningen for 1. gang. Den bedste måde at initialisere en tilknytning på er at bruge en EntityGraph eller inkludere en JOIN FETCH-klausul i din JPQL-forespørgsel.

I de følgende eksempler bruger jeg en JPQL-forespørgsel med 2 JOIN FETCH-klausuler til at indlæse en ChessPlayer enhedsobjekt. JOIN FETCH-klausulerne fortæller Hibernate at initialisere tilknytningen til de spil, de spillede med de hvide og sorte brikker.

StatelessSession statelessSession = sf.openStatelessSession();
statelessSession.getTransaction().begin();

ChessPlayer player = statelessSession.createQuery("""
											SELECT p 
											FROM ChessPlayer p 
												JOIN FETCH p.gamesWhite 
												JOIN FETCH p.gamesBlack 
											WHERE p.id=:id""", ChessPlayer.class)
									 .setParameter("id", 1L)
									 .getSingleResult();

log.info(player.getFirstName() + " " + player.getLastName());
log.info("White pieces: " + player.getGamesWhite().size());
log.info("Black pieces: " + player.getGamesBlack().size());

statelessSession.getTransaction().commit();

Som tidligere nævnt er forskellene mellem en læseoperation implementeret ved hjælp af en StatelessSession, og en session instans er ikke direkte synlig i din kode. Og det samme gælder for log-outputtet.

17:58:09,648 DEBUG [org.hibernate.SQL] - 
    select
        c1_0.id,
        c1_0.birthDate,
        c1_0.firstName,
        g2_0.playerBlack_id,
        g2_0.id,
        g2_0.chessTournament_id,
        g2_0.date,
        g2_0.playerWhite_id,
        g2_0.round,
        g2_0.version,
        g1_0.playerWhite_id,
        g1_0.id,
        g1_0.chessTournament_id,
        g1_0.date,
        g1_0.playerBlack_id,
        g1_0.round,
        g1_0.version,
        c1_0.lastName,
        c1_0.version 
    from
        ChessPlayer c1_0 
    join
        ChessGame g1_0 
            on c1_0.id=g1_0.playerWhite_id 
    join
        ChessGame g2_0 
            on c1_0.id=g2_0.playerBlack_id 
    where
        c1_0.id=?
17:58:09,682 DEBUG [org.hibernate.stat.internal.StatisticsImpl] - HHH000117: HQL: SELECT p
FROM ChessPlayer p
    JOIN FETCH p.gamesWhite
    JOIN FETCH p.gamesBlack
WHERE p.id=:id, time: 56ms, rows: 1
17:58:09,685 INFO  [com.thorben.janssen.TestStatelessSession] - Magnus Carlsen
17:58:09,685 INFO  [com.thorben.janssen.TestStatelessSession] - White pieces: 1
17:58:09,686 INFO  [com.thorben.janssen.TestStatelessSession] - Black pieces: 2

Men der er vigtige interne forskelle. Hibernate understøtter ikke kun doven indlæsning til StatelessSessions men bruger heller ingen caches, inklusive 1. niveaus cache. Det reducerer den overhead, der udføres for hver databaseforespørgsel. Men Hibernate kan ikke længere garantere, at du altid får det samme objekt, hvis du læser den samme enhed flere gange inden for samme session.

Det kan du se i følgende testcase, hvor jeg udfører den samme forespørgsel to gange.

StatelessSession statelessSession = sf.openStatelessSession();
statelessSession.getTransaction().begin();

ChessPlayer player1 = statelessSession.createQuery("""
											SELECT p 
											FROM ChessPlayer p 
												JOIN FETCH p.gamesWhite 
												JOIN FETCH p.gamesBlack 
											WHERE p.id=:id""", ChessPlayer.class)
									  .setParameter("id", 1L)
									  .getSingleResult();
ChessPlayer player2 = statelessSession.createQuery("""
											SELECT p 
											FROM ChessPlayer p 
												JOIN FETCH p.gamesWhite 
												JOIN FETCH p.gamesBlack 
											WHERE p.id=:id""", ChessPlayer.class)
									  .setParameter("id", 1L)
									  .getSingleResult();

assertNotEquals(player1, player2);

statelessSession.getTransaction().commit();

Brug af en standard session For eksempel vil Hibernate udføre den første forespørgsel, instansiere et enhedsobjekt for den returnerede post og gemme den i cachen på 1. niveau. Derefter vil den udføre den 2. forespørgsel, kontrollere cachen på 1. niveau for et objektobjekt, der repræsenterer posten, der returneres i resultatsættet, og returnere det objekt. Det sikrer, at du altid får det samme objektobjekt, hvis du henter en databasepost flere gange inden for samme session.

Uden cachen på 1. niveau, StatelessSession kender ikke til tidligere valgte enhedsobjekter. Den skal instansiere et nyt objekt for hver post, der returneres af en forespørgsel. På grund af det kan du få flere objekter, der repræsenterer den samme databasepost. I det foregående eksempel er det tilfældet for spiller1 og spiller2 genstande.

Husk det, når du skriver din virksomhedskode, og sørg for, at du altid bruger det samme entity-objekt til dine skriveoperationer. Ellers kan du overskrive tidligere udførte ændringer.

Konklusion

Hibernates StatelessSession interface giver en kommando-orienteret API, der giver dig mere kontrol over de udførte SQL-sætninger. Det er meget tættere på JDBC og understøtter ingen caches, automatiske skylninger, dirty checks, cascading og doven indlæsning.

Det gør en StatelessSession en god pasform til alle brugssager, der ikke drager fordel af disse funktioner. Typiske eksempler er batchjobs eller andre use cases, der udfører mange simple skriveoperationer.

Men uden alle disse funktioner kræver implementering af dit persistens-lag lidt mere arbejde. Du skal selv udløse alle databaseoperationer. Når du f.eks. har ændret en eller flere enhedsattributter, skal du kalde opdateringen metode på din StatelessSession forekomst for at fortsætte ændringen i databasen. Ellers vil Hibernate ikke være opmærksom på ændringen og vil ikke udløse nogen SQL-sætninger.

Du skal også initialisere alle nødvendige tilknytninger, når du henter et objektobjekt fra databasen. Og du skal være opmærksom på, at en StatelessSession returnerer ikke det samme objektobjekt, hvis du henter den samme post flere gange. Det gør håndteringen af ​​forespørgselsresultater lidt mere kompleks.

Samlet set Hibernates StatelessSession er en fantastisk funktion, hvis du vil reducere omkostningerne ved Hibernates session håndtering og behøver ikke funktioner som doven indlæsning, cascading, en cache på 1. niveau og automatiske skylninger.


Java tag