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

@Inkubering af funktioner i Hibernate 6

Hvis du prøvede Hibernate 6, har du muligvis genkendt den nye @Incubating anmærkning. Hibernate-teamet introducerer det for at fortælle brugerne om nye API'er og grænseflader, der stadig kan ændre sig. Det er en fantastisk tilføjelse, fordi de fleste udviklere forventer, at nye API'er og funktioner er stabile, efter at de er en del af en endelig udgivelse. Generelt er det tilfældet. Hibernates API'er er utroligt stabile. Men ikke at kunne ændre en ny API efter dens 1. udgivelse gør det umuligt at forbedre det baseret på brugerfeedback. Det gør det også svært at udvikle og frigive et sæt indbyrdes forbundne funktioner. Så det kan være godt for alle involverede at tillade lidt mere fleksibilitet.

Inkubationsmarkør og rapport

Den nye @Incubating annotation giver Hibernates udviklingsteam denne fleksibilitet. Ved at annotere en konfigurationsparameter eller grænseflade med @Incubating , advarer teamet deres brugere om, at denne funktion stadig kan ændre sig. Hibernate 6's udgivelsesmeddelelse erklærede, at de selvfølgelig sigter mod at holde disse API'er stabile. Men afventende arbejde med andre funktioner eller brugerfeedback kan forårsage et par ændringer i fremtiden.

Fra nu af bør du holde øje med Hibernates inkubationsrapport og dobbelttjekke, om en nyligt introduceret funktion er baseret på grænseflader, klasser eller konfigurationsparametre, der er kommenteret med @Incubating . Hvis det er tilfældet, kan du stadig bruge dem. Men du skal være opmærksom på, at en fremtidig udgivelse kan introducere en ændring, der kan påvirke din kode.

@Inkuberingsfunktioner i Hibernate 6.0

Hibernate 6.0 indeholder adskillige funktioner markeret som @Incubating . De fleste af dem er SPI'er, der bruges til at integrere Hibernate i forskellige miljøer og er ikke relevante for os som applikationsudviklere. Men der er nogle nye funktioner, som du bør kende, og som er markeret som @Incubating .

Konfigurationsparametre for foretrukne SQL-typer

Hibernate 6 introducerer 4 konfigurationsparametre, som du kan bruge til at konfigurere den JDBC-type, som Hibernate skal bruge til at kortlægge attributter af typen boolean , UUID , Varighed og Instant . Du kan bruge dem i din persistence.xml-konfiguration og enten indstille dem til en numerisk JDBC-typekode eller henvise til navnet på en konstant defineret i org.hibernate.type.SqlTypes .

  • hibernate.type.preferred_boolean_jdbc_type
    indstiller JDBC-typekoden for attributter af typen boolesk. Som standard får Hibernate denne type kortlægning fra den databasespecifikke dialekt.
  • hibernate.type.preferred_uuid_jdbc_type
    indstiller JDBC-typekoden for attributter af typen UUID. Som standard bliver disse kortlagt til org.hibernate.types.SqlTypes.UUID , som repræsenterer JDBC-typekoden 3000 .
  • hibernate.type.preferred_duration_jdbc_type
    indstiller JDBC-typekoden for attributter af typen Varighed . Som standard bliver disse kortlagt til org.hibernate.types.SqlTypes.INTERVAL_SECOND , som repræsenterer JDBC-typekoden 3100 .
  • hibernate.type.preferred_instant_jdbc_type
    indstiller JDBC-typekoden for attributter af typen Instant . Som standard bliver disse kortlagt til org.hibernate.types.SqlTypes.TIMESTAMP_UTC , som repræsenterer JDBC-typekoden 3003 .

Her kan du se et eksempel på en konfiguration, der fortæller Hibernate at kortlægge attributter af typen UUID til java.sql.Types.CHAR .

<persistence>
    <persistence-unit name="my-persistence-unit">
        ...
        <properties>
 			...

            <property name="hibernate.type.preferred_uuid_jdbc_type" value="CHAR" />
       </properties>
    </persistence-unit>
</persistence>

Hvis du bruger denne konfiguration og fortsætter en Forfatter enhed, der bruger en attribut af typen UUID som sin primære nøgle, kan du se i logoutputtet, at Hibernate kortlagde denne attribut som type CHAR i stedet for UUID .

15:24:58,715 DEBUG [org.hibernate.SQL] - insert into Author (city, postalCode, street, firstName, lastName, version, id) values (?, ?, ?, ?, ?, ?, ?)
15:24:58,716 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [homeCity]
15:24:58,717 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [2] as [VARCHAR] - [12345]
15:24:58,717 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [3] as [VARCHAR] - [homeStreet]
15:24:58,717 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [4] as [VARCHAR] - [firstName]
15:24:58,717 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [5] as [VARCHAR] - [lastName]
15:24:58,717 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [6] as [INTEGER] - [0]
15:24:58,719 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [7] as [CHAR] - [c4e6a76d-d241-4806-aeae-8afca5598cf2]

Separate forespørgselsgrænseflader til læsning og skrivning

SelectionQuery og MutationQuery grænseflader introduceret i Hibernate 6.0.0 er markeret som @Incubating . De forsøger at forbedre en uheldig designbeslutning ved JPA-specifikationen og ældre Hibernate-versioner.

Baseret på JPA-specifikationen og tidligere Hibernate-versioner er alle læse- og ændringsforespørgsler repræsenteret af en Forespørgsel grænsefladen eller dens stærkere version, TypedQuery interface. Hvis du ser på koden, der bruger disse grænseflader, erkender du hurtigt, at læsning og ændring af forespørgsler er forskellige. Men det er ikke indlysende, når du ser på Forespørgslen interface. Den definerer flere metoder, som du kun kan bruge med én type forespørgsel. To eksempler er:

  • den executeUpdate metode, der udfører en modificerende sætning og
  • det sæt første resultat og setMaxResults metoder, der paginerer resultatet af en udvælgelsessætning.

Den nye SelectionQuery og MutationQuery grænseflader adskiller disse ansvarsområder og giver meget renere API'er. Jeg beskrev begge grænseflader mere detaljeret i min guide til MutationQuery og SelectionQuery i Hibernate 6.

Bare rolig; du behøver ikke straks at opdatere hele din applikation for at bruge de nye grænseflader. Hibernate 6 understøtter stadig Forespørgslen og TypedQuery grænseflader, og kravene til bagudkompatibilitet i JPA-specifikationen gør det usandsynligt, at dette vil ændre sig. Forespørgslen grænsefladen udvider nu den nye SelectionQuery og MutationQuery  grænseflader.

Følgende kodestykke viser et simpelt eksempel på en SelectionQuery . Som du kan se, ville dette kodestykke se næsten det samme ud, hvis jeg brugte standard Forespørgsel interface i stedet for. Den eneste forskel er, at jeg kalder createSelectionQuery metoden i stedet for createQuery metode til at oprette forespørgslen.

SelectionQuery<Book> q = s.createSelectionQuery("SELECT b FROM Book b WHERE b.title = :title", Book.class);
q.setParameter("title", "Hibernate Tips - More than 70 solutions to common Hibernate problems");
List<Book> books = q.getResultList();

Den største forskel bliver kun synlig, mens du skriver din kode. SelectionQuery grænsefladen definerer kun de metoder, du kan bruge på en forespørgsel, der udvælger data.

Og det samme gælder for en MutationQuery interface. Ved at bruge denne grænseflade får du endnu mere fordel af adskillelsen mellem læse- og skriveoperationer. Du kan bruge de fleste metoder, der er defineret af Forespørgslen  grænseflade kun på udsagn, der vælger data fra din database. MutationQuery grænsefladen definerer ikke disse metoder, hvilket giver en renere og lettere at bruge API.

MutationQuery q = s.createNamedMutationQuery("Book.updateTitle");
q.executeUpdate();

Få mere at vide om den nye MutationQuery og SelectionQuery grænseflader i min guide til MutationQuery og SelectionQuery i Hibernate 6.

Forbedret håndtering af ZonedDateTime og OffsetDateTime

Som forklaret i en tidligere artikel normaliserer Hibernate 5 en attribut af typen ZonedDateTime eller OffsetDate til en konfigureret tidszone eller din applikations lokale tidszone, før den gemmer den uden tidszoneoplysninger i din database. Og når du læser denne attributs værdi fra databasen, tilføjer Hibernate den konfigurerede eller lokale tidszone til tidsstemplet. Selvom denne tilgang fungerer fint under de rigtige omstændigheder, er den fejltilbøjelig og ufleksibel.

Hibernate 6 forbedrede håndteringen af ​​ZonedDateTime og OffsetDateTime ved at introducere @TimeZoneStorage anmærkning. Det giver dig mulighed for at definere, om du vil:

  • gem dit tidsstempel i en kolonne, der understøtter tidszoneoplysninger,
  • gem tidszonens offset i en separat databasekolonne,
  • normaliser tidsstemplet til UTC, eller
  • normaliser tidsstemplet til en konfigureret eller din lokale tidszone.
@Entity
public class ChessGame {
    
    @TimeZoneStorage(TimeZoneStorageType.NORMALIZE_UTC)
    private ZonedDateTime zonedDateTime;
	
	...
	
}

Få mere at vide om Hibernates forbedrede kortlægning af ZonedDateTime og OffsetDateTime .

Refaktoreret resultattransformator

ResultTransformer grænsefladen blev forældet i Hibernate 5. Den definerede transformTuple og transformList metoder, som du kan implementere for at fortælle Hibernate, hvordan et forespørgselsresultat skal knyttes til din foretrukne datastruktur. Hovedproblemet med denne tilgang var, at de fleste transformere kun skulle implementere 1 af de 2 metoder og holdt den anden tom. På grund af det gjorde disse 2 metoder grænsefladen unødvendigt kompleks og forhindrede os i at bruge den som en funktionel grænseflade.

I Hibernate 6 opdelte udviklingsteamet ResultTransformer interface til TupleTransformer og ListTransformer grænseflader. Hver af dem definerer en af ​​de metoder, der tidligere er defineret af ResultTransformer og kan bruges som en funktionel grænseflade.

BookPublisherValue bpv = (BookPublisherValue) session
		.createQuery("SELECT b.title as title, b.publisher.name as publisher FROM Book b WHERE id = 1", Object[].class)
		.setTupleTransformer((tuple, aliases) -> {
				log.info("Transform tuple");
				BookPublisherValue v = new BookPublisherValue();
				v.setTitle((String) tuple[0]);
				v.setPublisher((String) tuple[1]);
				return v;
		}).getSingleResult();

Og der er gode nyheder, hvis du brugte en af ​​ResultTransformer implementeringer leveret af Hibernate 5. I Hibernate 6 kan du finde de samme transformerimplementeringer, som nu implementerer TupleTransformer eller ListTransformer grænseflade.

BookPublisherValue bpv = (BookPublisherValue) session
                .createQuery("SELECT b.title as title, b.publisher.name as publisher FROM Book b WHERE id = 1", Object[].class)
                .setTupleTransformer(new AliasToBeanResultTransformer<BookPublisherValue>(BookPublisherValue.class)).getSingleResult();

Jeg beskriver alt dette mere detaljeret i min guide til Hibernates ResultTransformer .

Tilpasset instansiering af indlejrede enheder

EmbeddableInstantiator er endnu en forbedring i Hibernate 6, som er markeret som inkuberende. Baseret på JPA-specifikationen skal en embeddable levere en no-arguments constructor. Siden Hibernate 6 kan du levere en EmbeddableInstantiator i stedet.

Som du kan se nedenfor, implementering af EmbeddableInstantiator grænsefladen er ikke kompleks. Hoveddelen er implementeringen af ​​instansen metode. I den metode skal du få attributværdierne i alfabetisk rækkefølge efter deres navne og bruge dem til at instantiere og initialisere din indlejrede.

public class AddressInstantiator implements EmbeddableInstantiator {

    Logger log = LogManager.getLogger(this.getClass().getName());

    public boolean isInstance(Object object, SessionFactoryImplementor sessionFactory) {
        return object instanceof Address;
    }

    public boolean isSameClass(Object object, SessionFactoryImplementor sessionFactory) {
        return object.getClass().equals( Address.class );
    }

    public Object instantiate(Supplier<Object[]> valuesAccess, SessionFactoryImplementor sessionFactory) {
        final Object[] values = valuesAccess.get();
        // valuesAccess contains attribute values in alphabetical order
        final String city = (String) values[0];
        final String postalCode = (String) values[1];
        final String street = (String) values[2];
        log.info("Instantiate Address embeddable for "+street+" "+postalCode+" "+city);
        return new Address( street, city, postalCode );
    }

}

Hvis du vil lære mere om Hibernate 6's EmbeddableInstantiator , sørg for at læse mit seneste blogindlæg om det.

Konklusion

Alle udviklere, der bruger Hibernate 6, bør kende til @Incubating annotation og den nye inkubationsrapport. Hibernates udviklingsteam bruger dem til at advare deres brugere om API'er, der kan ændre sig i fremtidige udgivelser.

Det er en god idé at introducere en sådan annotering, fordi det giver udviklingsteamet mere fleksibilitet, når de frigiver nye funktioner og justerer dem, indtil de finder en løsning, der løser de fleste brugeres behov. Men det introducerer også risikoen for, at en af ​​de nye funktioner, du lige er begyndt at bruge i din applikation, ændres, og du skal tilpasse din kode til den. Vi bliver nødt til at se, hvor ofte det sker, og hvor alvorlige disse ændringer vil være.


Java tag