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

Sådan konfigureres listesemantik i Hibernate 6

Baseret på dens javadoc, en java.util.List formodes at repræsentere en ordnet samling af værdier. Men det er ikke nødvendigvis tilfældet, hvis du bruger det som typen af ​​en til-mange-forening eller en ElementCollection . Som jeg forklarede før, kan Hibernate håndtere en java.util.List som en taske eller en liste. Kun listen kortlægning fortsætter rækkefølgen af ​​dets elementer. Men som standard håndterer Hibernate en java.util.List som en taske . Efter du har hentet det fra databasen, indeholder det elementerne i en udefineret rækkefølge.

Indtil Hibernate 6.0.0 skulle du tilføje en @OrderColumn anmærkning til din ElementCollection , en-til-mange-forening eller ejersiden af ​​din mange-til-mange-forening for at fortsætte rækkefølgen af ​​din java.util.List . Dvale bevarer derefter placeringen af ​​hvert element i en separat kolonne og administrerer indekset for alle elementer under alle indsættelses-, opdaterings- og sletningsoperationer.

Bemærk venligst, at @OrderColumn annotering er ikke understøttet til referencesiden af ​​mange-til-mange-foreninger (@ManyToMany(mappedBy =“…”) ).

Siden Hibernate 6 kan du definere listesemantikken globalt for ejersiden af ​​dine mange-til-mange associationer og ElementCollections ved at indstille konfigurationsegenskaben hibernate.mapping.default_list_semantics til LISTE i din persistence.xml. Hvis du vil anvende den samme håndtering på dine en-til-mange-foreninger, skal du stadig tilføje en @OrderColumn annotation.

Lad os se nærmere på denne nye konfigurationsparameter og konsekvenserne af at administrere og vedligeholde indekset for hvert element i Listen .

Sådan konfigurerer du Hibernates listesemantik

Som standard bevarer Hibernate ikke rækkefølgen af ​​elementerne i nogen ElementCollection eller til-mange forening. Det kan du ændre på 2 måder. Du kan enten konfigurere det globalt eller justere håndteringen af ​​en specifik attribut. Når du gør det, skal du huske på, at den globale indstilling ikke påvirker referencesiden af ​​en en-til-mange eller mange-til-mange-forening.

Ændring af global listesemantik

Siden Hibernate 6.0.0 kan du indstille egenskaben hibernate.mapping.default_list_semantics i din persistence.xml konfiguration til LIST .

<persistence>
    <persistence-unit name="my-persistence-unit">
        <description>Hibernate example configuration - thorben-janssen.com</description>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>

        <properties>
            <property name="hibernate.mapping.default_list_semantics" value="LIST" />
            
			...
        </properties>
    </persistence-unit>
</persistence>

Hibernate behandler derefter din ElementCollection og ejersiden af ​​dine mange-til-mange-foreninger på samme måde, som hvis du annoterede dem med @OrderColumn . Det bevarer placeringen af ​​hvert element i en separat databasekolonne. For at få navnet på den kolonne tilføjer Hibernate postfixet _ORDER til navnet på den kolonne, der kortlægger foreningen.

Lad os bruge dette i et simpelt testscenarie baseret på følgende ChessPlayer og Skakturnering enheder.

@Entity
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")
	private Long id;

    private String firstName;
    
    private String lastName;

    private LocalDate birthDate;

    @OneToMany(mappedBy = "playerWhite")
    private Set<ChessGame> gamesWhite;

    @OneToMany(mappedBy = "playerBlack")
    private Set<ChessGame> gamesBlack;

    @Version
    private int version;
	
	...
}
@Entity
public class ChessTournament {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tournament_seq")
    private Long id;

    private String name;

    private LocalDate startDate;

    private LocalDate endDate;

    @Version
    private int version;

    @ManyToMany
    private List<ChessPlayer> players = new ArrayList<ChessPlayer>();

    @OneToMany
    private Set<ChessGame> games = new HashSet<>();
	
	...
}

Som du kan se, er der ikke noget særligt ved disse 2 enhedsklasser. Begge bruger en databasesekvens til at generere deres primære nøgleværdier og en versionsattribut til optimistisk låsning. Den konfigurerede listesemantik vil påvirke mange-til-mange-tilknytningen modelleret af spillerne  attributten for ChessTournament  enhed.

Når du udfører følgende testcase, kan du se, at Hibernate gemmer hver ChessPlayer 's position i Listen spillere i players_ORDER kolonne.

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

ChessTournament tournament = em.find(ChessTournament.class, 1L);
ChessPlayer player1 = em.find(ChessPlayer.class, 1L);
ChessPlayer player2 = em.find(ChessPlayer.class, 2L);
ChessPlayer player3 = em.find(ChessPlayer.class, 3L);

tournament.getPlayers().add(player1);
tournament.getPlayers().add(player2);
tournament.getPlayers().add(player3);

em.getTransaction().commit();
em.close();
18:13:54,250 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.endDate,c1_0.name,c1_0.startDate,c1_0.version from ChessTournament c1_0 where c1_0.id=?
18:13:54,277 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
18:13:54,281 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
18:13:54,284 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
18:13:54,295 DEBUG [org.hibernate.SQL] - select p1_0.ChessTournament_id,p1_0.players_ORDER,p1_1.id,p1_1.birthDate,p1_1.firstName,p1_1.lastName,p1_1.version from ChessTournament_ChessPlayer p1_0 join ChessPlayer p1_1 on p1_1.id=p1_0.players_id where p1_0.ChessTournament_id=?
18:13:54,326 DEBUG [org.hibernate.SQL] - update ChessTournament set endDate=?, name=?, startDate=?, version=? where id=? and version=?
18:13:54,334 DEBUG [org.hibernate.SQL] - insert into ChessTournament_ChessPlayer (ChessTournament_id, players_ORDER, players_id) values (?, ?, ?)
18:13:54,340 DEBUG [org.hibernate.SQL] - insert into ChessTournament_ChessPlayer (ChessTournament_id, players_ORDER, players_id) values (?, ?, ?)
18:13:54,343 DEBUG [org.hibernate.SQL] - insert into ChessTournament_ChessPlayer (ChessTournament_id, players_ORDER, players_id) values (?, ?, ?)

Når Hibernate henter listen over spillere, bruger den ikke en ORDER BY klausul for at få spillerne i den rigtige rækkefølge, men bestiller dem i hukommelsen i stedet.

18:20:49,230 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.endDate,c1_0.name,c1_0.startDate,c1_0.version from ChessTournament c1_0 where c1_0.id=?
18:20:49,234 DEBUG [org.hibernate.SQL] - select p1_0.ChessTournament_id,p1_0.players_ORDER,p1_1.id,p1_1.birthDate,p1_1.firstName,p1_1.lastName,p1_1.version from ChessTournament_ChessPlayer p1_0 join ChessPlayer p1_1 on p1_1.id=p1_0.players_id where p1_0.ChessTournament_id=?

Justering af specifik listesemantik

Hvis du kun ønsker at bevare rækkefølgen af ​​elementerne i en specifik ElementCollection eller mange-til-mange-tilknytning, eller hvis du vil bevare rækkefølgen af ​​en en-til-mange-tilknytning, skal du annotere attributten med en @OrderColumn anmærkning. Når du gør det, kan du enten angive navnet på ordrekolonnen eller bruge standarden, som tilføjer postfixet _ORDER til navnet på den kolonne, der kortlægger foreningen.

@Entity
public class ChessPlayer {

    @OneToMany(mappedBy = "playerWhite")
    @OrderColumn
    private Set<ChessGame> gamesWhite;

    @OneToMany(mappedBy = "playerBlack")
    @OrderColumn(name="myOrderColumn")
    private Set<ChessGame> gamesBlack;
	
	...
}

Hibernate håndterer derefter tilknytningen på samme måde som i det foregående eksempel. Det bevarer indekset for hvert element i en separat kolonne. I dette eksempel bruger Hibernate standardnavnet gamesWhite_ORDER til gamesWhite attributten og myOrderColumn angivet af @OrderColumn annotation til spillene Sort attribut.

Konsekvenser af at bevare elementets rækkefølge

At fastholde rækkefølgen af ​​elementerne i en forening kan lyde som en god idé. Men det kræver en ledelsesindsats, der kan bremse dine skriveoperationer. Fordi Hibernate bevarer hvert elements position, skal det opdatere flere poster, hvis du tilføjer eller fjerner et element, der ikke er det sidste på listen .

I de foregående eksempler tilføjede jeg et element til slutningen af ​​Listen . Det krævede ingen ændringer i indekset for de andre elementer. Men det ændrer sig, når du tilføjer et element et sted i midten.

tournament = em.find(ChessTournament.class, 1L);
ChessPlayer player4 = em.find(ChessPlayer.class, 4L);
tournament.getPlayers().add(1, player4);

Ved at tilføje Skakspilleren på position 1 bliver indekset for alle elementer, der var på position 1 eller højere, forøget med én. Hibernate skal derefter opdatere det i databasen.

18:32:07,152 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.endDate,c1_0.name,c1_0.startDate,c1_0.version from ChessTournament c1_0 where c1_0.id=?
18:32:07,159 DEBUG [org.hibernate.SQL] - select c1_0.id,c1_0.birthDate,c1_0.firstName,c1_0.lastName,c1_0.version from ChessPlayer c1_0 where c1_0.id=?
18:32:07,164 DEBUG [org.hibernate.SQL] - select p1_0.ChessTournament_id,p1_0.players_ORDER,p1_1.id,p1_1.birthDate,p1_1.firstName,p1_1.lastName,p1_1.version from ChessTournament_ChessPlayer p1_0 join ChessPlayer p1_1 on p1_1.id=p1_0.players_id where p1_0.ChessTournament_id=?
18:32:07,177 DEBUG [org.hibernate.SQL] - update ChessTournament set endDate=?, name=?, startDate=?, version=? where id=? and version=?
18:32:07,183 DEBUG [org.hibernate.SQL] - update ChessTournament_ChessPlayer set players_id=? where ChessTournament_id=? and players_ORDER=?
18:32:07,187 DEBUG [org.hibernate.SQL] - update ChessTournament_ChessPlayer set players_id=? where ChessTournament_id=? and players_ORDER=?
18:32:07,191 DEBUG [org.hibernate.SQL] - insert into ChessTournament_ChessPlayer (ChessTournament_id, players_ORDER, players_id) values (?, ?, ?)

Og det samme sker selvfølgelig, hvis du fjerner ethvert element undtagen det sidste.

Som du kan se i dette eksempel, fortsætter rækkefølgen af ​​Listen skaber en overhead. Afhængigt af størrelsen på Listen og den slags operationer, du udfører, kan dette forårsage alvorlige ydeevneproblemer. Jeg anbefaler derfor, at du holder dig til Hibernates gamle liste semantik og bruge den nye håndtering med stor omhu.

Konklusion

En liste formodes at være en ordnet samling af elementer. Men som standard bevarer Hibernate ikke rækkefølgen, og databasen returnerer elementerne i tilfældig rækkefølge. På grund af dette går den indledende rækkefølge af elementerne tabt, efter at Hibernate henter Listen fra databasen.

Før Hibernate 6 kan du undgå det ved at annotere din ElementCollection , en-til-mange-forening eller ejersiden af ​​din mange-til-mange-forening med en @OrderColumn anmærkning. Hibernate gemmer derefter placeringen af ​​hvert element i en separat databasekolonne.

Siden Hibernate 6 kan du bruge en ny konfigurationsparameter til at ændre håndteringen af ​​din Liste s globalt for alle dine ElementCollection s og ejersiden af ​​dine mange-til-mange-foreninger. For at gøre det behøver du kun at indstille hibernate.mapping.default_list_semantics konfigurationsegenskab til LIST .

Når du beslutter dig for at fortsætte rækkefølgen af ​​din liste s, husk venligst, at dette kan sinke dine skriveoperationer. Hibernate skal administrere indekset for alle lister elementer, som kræver flere opdateringshandlinger, hvis du indsætter eller fjerner et element, der ikke er det sidste.


Java tag