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

Navngivningsstrategier i Hibernate 5

JPA og Hibernate giver en standardmapping, der knytter hver enhedsklasse til en databasetabel med samme navn. Hver af dens attributter bliver knyttet til en kolonne med det samme. Men hvad hvis du vil ændre denne standard, f.eks. fordi den ikke matcher din virksomheds navnekonventioner?

Du kan selvfølgelig angive tabelnavnet for hver enhed og kolonnenavnet for hver attribut. Det kræver en @Table annotation på hver klasse og en @Column anmærkning på hver egenskab. Dette kaldes eksplicit navngivning.

Det er en god tilgang, hvis du vil ændre kortlægningen for en egenskab. Men at gøre det for mange egenskaber kræver meget arbejde. Tilpasning af Hibernates navnestrategi er så ofte en bedre tilgang.

I denne artikel vil jeg vise dig, hvordan du bruger det til at justere kortlægningen af ​​alle enheder og attributter. Men før vi gør det, skal vi først tale om forskellen mellem Hibernates logiske og fysiske navnestrategi.

En 2-trins tilgang

Hibernate opdeler tilknytningen af ​​entiteten eller attributnavnet til tabel- eller kolonnenavnet i 2 trin:

  1. Det bestemmer først det logiske navn på en enhed eller en attribut. Du kan udtrykkeligt indstille det logiske navn ved hjælp af @Table og @Column anmærkninger. Hvis du ikke gør det, vil Hibernate bruge en af ​​sine implicitte navnestrategier.
  2. Den knytter derefter det logiske navn til et fysisk navn. Som standard bruger Hibernate det logiske navn som det fysiske navn. Men du kan også implementere en PhysicalNameStrategy  der knytter det logiske navn til et fysisk navn, der følger din interne navnekonvention. Eller siden Hibernate 5.5.4 kan du aktivere Hibernates CamelCaseToUnderscoresNamingStrategy .

Så hvorfor skelner Hibernate mellem en logisk og en fysisk navnestrategi, men det gør JPA-specifikationen ikke?

JPAs tilgang virker, men hvis du ser nærmere på den, erkender du, at Hibernates tilgang giver mere fleksibilitet. Ved at opdele processen i 2 trin giver Hibernate dig mulighed for at implementere en konvertering, der bliver anvendt på alle attributter og klasser.

Hvis dine navnekonventioner for eksempel kræver, at du annoncerer "_TBL" til alle tabelnavne, kan du gøre det i din PhysicalNameStrategy . Det er så ligegyldigt, om du udtrykkeligt angiver tabelnavnet i en @Table annotation, eller hvis du gør det implicit baseret på enhedens navn. I begge tilfælde vil Hibernate tilføje "_TBL" til slutningen af ​​dit tabelnavn.

På grund af den ekstra fleksibilitet kan jeg lide Hibernates tilgang lidt bedre.

Logisk navngivningsstrategi

Som forklaret tidligere kan du enten definere det logiske navn eksplicit eller implicit. Lad os tage et kig på begge muligheder.

Eksplicit navnestrategi

Den eksplicitte navnestrategi er meget nem at bruge. Du har sikkert allerede brugt det selv. Det eneste du skal gøre er at annotere din enhedsklasse med @Table eller din enhedsattribut med @Column og angiv dit foretrukne navn som en værdi til navneattributten.

@Entity
@Table(name = "AUTHORS")
public class Author {

    @Column(name = "author_name")
    private String name;

    ...
}

Hvis du derefter bruger denne enhed i din kode og aktiverer logningen af ​​SQL-sætninger, kan du se, at Hibernate bruger de angivne navne i stedet for standardnavnene.

15:55:52,525 DEBUG [org.hibernate.SQL] - insert into AUTHORS (author_name, version, id) values (?, ?, ?)

Implicit navnestrategi

Hvis du ikke angiver tabel- eller kolonnenavnet i en annotering, bruger Hibernate en af ​​dets implicitte navnestrategier. Du kan vælge mellem 4 forskellige navnestrategier og 1 standardstrategi:

  • standard
    Som standard bruger Hibernate den implicitte navngivningsstrategi, der er defineret af JPA-specifikationen. Denne værdi er et alias for jpa .
  • jpa
    Dette er navnestrategien defineret af JPA 2.0-specifikationen.
    Det logiske navn på en enhedsklasse er enten det navn, der er angivet i @Entity annotation eller det ukvalificerede klassenavn. For grundlæggende attributter bruger den navnet på attributterne som det logiske navn. For at få det logiske navn på en sammenføjningskolonne i en tilknytning sammenkæder denne strategi navnet på den refererende attribut, et "_" og navnet på den primære nøgleattribut for den refererede enhed. Det logiske navn på en join-kolonne i en elementsamling består af navnet på den enhed, der ejer tilknytningen, et "_" og navnet på den primære nøgleattribut for den refererede enhed. Og det logiske navn på en join-tabel starter med det fysiske navn på ejertabellen, efterfulgt af et "_" og det fysiske navn på referencetabellen.
  • legacy-hbm
    Dette er Hibernates oprindelige navnestrategi. Den genkender ikke nogen af ​​JPA's anmærkninger. Men du kan bruge Hibernates proprietære konfigurationsfil og annoteringer til at definere et kolonne- eller enhedsnavn.
    Ud over det er der et par andre forskelle til JPA-specifikationen:
    • Det logiske navn på en join-kolonne er kun dens attributnavn.
    • For jointabeller sammenkæder denne strategi navnet på den fysiske tabel, der ejer tilknytningen, et "_" og navnet på den attribut, der ejer tilknytningen.
  • legacy-jpa
    legacy-jpa-strategien implementerer navnestrategien defineret af JPA 1.0.
    De vigtigste forskelle til jpa-strategien er:
    • Det logiske navn på en join-tabel består af det fysiske tabelnavn på ejersiden af ​​foreningen, et "_" og enten det fysiske navn på den refererende side af foreningen eller foreningens ejerattribut.
    • For at få det logiske navn på join-kolonnen i en elementsamling bruger legacy-jpa-strategien det fysiske tabelnavn i stedet for enhedsnavnet på den refererede side af tilknytningen. Det betyder, at det logiske navn på join-kolonnen består af det fysiske tabelnavn på den refererede side af tilknytningen, et "_" og navnet på den refererede primærnøglekolonne.
  • komponent-sti
    Denne strategi er næsten identisk med jpa-strategien. Den eneste forskel er, at den inkluderer navnet på det sammensatte i det logiske attributnavn.

Du kan konfigurere den logiske navnestrategi ved at indstille hibernate.implicit_name_strategy attribut i din konfiguration.

<persistence>
    <persistence-unit name="naming">
        ...
        <properties>
            <property name="hibernate.implicit_naming_strategy"
                      value="jpa" />
            ...
        </properties>
    </persistence-unit>
</persistence>

Fysisk navngivningsstrategi

Som tidligere nævnt bruger Hibernates standard fysiske navngivningsstrategi det logiske navn uden at ændre det. Så uanset logikkens navn, vil det også være navnet på databasetabellen eller kolonnen.

Hvis du foretrækker en anden kortlægning, kan du definere en tilpasset strategi. Jeg vil vise dig, hvordan du gør det senere i denne artikel. Men før det vil jeg vise dig CamelCaseToUnderscoresNamingStrategy  der blev introduceret i Hibernate 5.5.4. Det replikerer den kortlægning, der bruges af Spring Boots SpringPhysicalNamingStrategy .

CamelCaseToUnderscoresNamingStrategy i Dvale>5.5.4

Forårets SpringPhysicalNamingStrategy er blevet meget populær. Det bliver ikke kun brugt som standard i Spring Boot-applikationer, men mange udviklere begyndte også at anvende det til ikke-Spring-projekter. Den erstatter alle prikker og kamelhuse med understregninger og genererer alle tabelnavne med små bogstaver.

Aktivering af Hibernates CamelCaseToUnderscoresNamingStrategy

Hibernate-teamet gentog denne kortlægningsstrategi i CamelCaseToUnderscoresNamingStrategy . Siden Hibernate 5.5.4.Final kan du aktivere denne strategi ved at indstille konfigurationsegenskaben hibernate.physical_name_strategy i din persistence.xml-fil til org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy .

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

        <properties>
            ...
			
            <property name="hibernate.physical_naming_strategy"
                      value="org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy"/>
        </properties>
    </persistence-unit>
</persistence>

Brug af Hibernates CamelCaseToUnderscoresNamingStrategy

Vi har brug for et enhedsklassenavn i et kamelhus for at vise denne strategi i alle detaljer. Derfor bruger jeg ChessPlayer enhed i stedet for Forfatteren  enhed, du så i tidligere eksempler.

@Entity
public class ChessPlayer {

	@Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "player_seq")
    @SequenceGenerator(name = "player_seq", sequenceName = "player_seq", initialValue = 100)
	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;
	
	...
	
}

Som du kan se i kodestykket, definerer jeg ikke nogen logiske navne for enhedsklassen og dens attributter. Som standard bruger Hibernate derefter Java-klassens navn og navnene på hver af dens attributter som deres logiske navne.

Hibernates CamelCaseToUnderscoresNamingStrategy  fysisk navngivningsstrategi erstatter alle prikker og kamelhuse med understregninger og ændrer det logiske klassenavn til små bogstaver. Baseret på denne kortlægning er Skakspillet enhedsklassen bliver knyttet til skakspillet bord. Og attributterne firstName , efternavn , og fødselsdato blive knyttet til kolonnerne fornavn , efternavn og fødselsdato .

Du kan se det, når jeg fortsætter med et nyt Skakspil enhedsobjekt.

19:27:25,995 DEBUG SQL:144 - select chessplaye0_.id as id1_1_0_, chessplaye0_.birth_date as birth_da2_1_0_, chessplaye0_.first_name as first_na3_1_0_, chessplaye0_.last_name as last_nam4_1_0_, chessplaye0_.version as version5_1_0_ from chess_player chessplaye0_ where chessplaye0_.id=?

Implementering af en tilpasset fysisk navngivningsstrategi

Hvis ingen af ​​Hibernates fysiske navnestrategier opfylder dine krav, kan du implementere din egen strategi. At gøre det er ikke kompliceret. Du kan enten implementere PhysicalNamingStrategy  grænseflade eller udvide Hibernates PhysicalNamingStrategyStandardImpl  klasse.

Jeg udvider Hibernates PhysicalNamingStrategyStandardImpl i det følgende eksempel for at oprette en navnestrategi, der tilføjer postfixet "_TBL" til hvert tabelnavn. Og i det 2. eksempel vil vi definere en navnestrategi, der konverterer navne på kamelhuse til slangehuse.

Tabel postfix-strategi

Det eneste, jeg vil ændre i denne navnestrategi, er udleveringen af ​​bordnavnet. Udvidelse af Hibernates PhysicalNamingStrategyStandardImpl  klasse er den nemmeste måde at opnå det på.

Implementering af en tilpasset strategi

Jeg overskriver toPhysicalTableName metode, føj et statisk postfix til navnet og konverter det til en Identifier .

public class TablePostfixPhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    private final static String POSTFIX = "_TBL";
    
    @Override
    public Identifier toPhysicalTableName(final Identifier identifier, final JdbcEnvironment jdbcEnv) {
        if (identifier == null) {
            return null;
        }

        final String newName = identifier.getText() + POSTFIX;
        return Identifier.toIdentifier(newName);
    }

}

I næste trin skal du aktivere navngivningsstrategien. Det gør du ved at indstille hibernate.physical_name_strategy attribut til strategiens fuldt kvalificerede klassenavn.

<persistence>
    <persistence-unit name="naming">
        ...
        <properties>
            <property name="hibernate.physical_naming_strategy"
                      value="org.thoughtsonjava.naming.config.TablePostfixPhysicalNamingStrategy" />
            ...
        </properties>
    </persistence-unit>
</persistence>
Brug af tabellens postfix-strategi

Lad os prøve denne kortlægning ved hjælp af denne grundlæggende forfatter enhed. Jeg angiver ikke et logisk navn for enheden. Så den er standard til navnet på klassen, som er Author . Uden vores tilpassede navnestrategi ville Hibernate kortlægge denne enhed til Forfatteren tabel.

@Entity
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Version
    private int version;

    private String name;

    @ManyToMany(mappedBy = "authors", fetch = FetchType.LAZY)
    private Set<Book> books;

    ...
}

Når jeg fortsætter denne enhed, kan du se i logfilen, at Hibernate tilknyttede den til AUTHOR_TBL tabel.

14:05:56,619 DEBUG [org.hibernate.SQL] - insert into Author_TBL (name, version, id) values (?, ?, ?)

Navne i slangekasse i stedet for kamelkasse

I Java foretrækker vi at bruge kamelhus til vores klasse- og attributnavne. Som standard bruger Hibernate det logiske navn som det fysiske navn. Altså entity-attributten LocalDate udgivelsesdato bliver knyttet til databasekolonnen publishingDate .

Nogle virksomheder bruger navngivningskonventioner, der kræver, at du bruger slangekasse til dine tabel- og kolonnenavne. Det betyder, at din udgivelsesdato attribut skal tilknyttes udgivelsesdatoen kolonne.

Som forklaret tidligere kan du bruge den eksplicitte navngivningsstrategi og annotere hver egenskab med en @Column anmærkning. Men for de fleste vedholdenhedslag er det meget arbejde, og det er let at glemme.

Så lad os implementere en navnestrategi, der gør det for os.

Implementering af en tilpasset strategi
public class SnakeCasePhysicalNamingStrategy extends PhysicalNamingStrategyStandardImpl {

    @Override
    public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalCatalogName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalColumnName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalSchemaName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalSequenceName(toSnakeCase(name), context);
    }

    @Override
    public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
        return super.toPhysicalTableName(toSnakeCase(name), context);
    }
    
    private Identifier toSnakeCase(Identifier id) {
        if (id == null)
            return id;
            
        String name = id.getText();
        String snakeName = name.replaceAll("([a-z]+)([A-Z]+)", "$1\\_$2").toLowerCase();
        if (!snakeName.equals(name))
            return new Identifier(snakeName, id.isQuoted());
        else
            return id;
    }
}

Den interessante del af denne navnestrategi er toSnakeCase metode. Jeg kalder det i alle metoder, der returnerer et fysisk navn for at konvertere det angivne navn til slangekasse.

Hvis du er bekendt med regulære udtryk, implementeringen af ​​toSnakeCase metoden er ret enkel. Ved at kalde replaceAll(“([a-z]+)([A-Z]+)”, “$1\\_$2”) , tilføjer vi et "_" foran hvert stort bogstav. Når det er gjort, behøver vi kun at ændre alle tegn til små bogstaver.

I det næste trin skal vi indstille strategien i persistence.xml-filen.

<persistence>
    <persistence-unit name="naming">
        ...
        <properties>
            <property name="hibernate.physical_naming_strategy"
                      value="org.thoughtsonjava.naming.config.SnakeCasePhysicalNamingStrategy" />
            ...
        </properties>
    </persistence-unit>
</persistence>
Brug af slangekassestrategien

Når jeg nu fortsætter denne bog enhed, vil Hibernate bruge den tilpassede strategi til at kortlægge publiceringsdatoen attribut til databasekolonnen udgivelsesdato .

@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    @Version
    private int version;

    private String title;

    private LocalDate publishingDate;

    @ManyToMany
    private Set<Author> authors;

    @ManyToOne
    private Publisher publisher;

    ...
}

Som du kan se i logfilen, fungerede navnestrategien som forventet og ændrede navnet på publishingDate kolonne til udgivelsesdato .

14:28:59,337 DEBUG [org.hibernate.SQL] - insert into books (publisher_id, publishing_date, title, version, id) values (?, ?, ?, ?, ?)

Konklusion

Hibernates navnestrategi giver dig masser af fleksibilitet. Det består af 2 dele, kortlægningen af ​​det logiske og det fysiske navn.

Du kan udtrykkeligt definere det logiske navn ved hjælp af @Table og @Column anmærkning. Hvis du ikke gør det, bruger Hibernate en af ​​sine implicitte navnestrategier. Standarden er kompatibel med JPA 2.0.

Efter at det logiske navn er blevet bestemt, anvender Hibernate en fysisk navngivningsstrategi. Som standard returnerer det det logiske navn. Siden version 5.5.4 tilbyder Hibernate også CamelCaseToUnderscoresNamingStrategy. Den erstatter alle prikker og kamelhuse med understregninger og genererer alle tabelnavne med små bogstaver. Og du kan også implementere din egen fysiske navnestrategi. De fleste teams bruger dette til at anvende en navnekonvention på alle logiske entitets- og attributnavne. Som du har set i eksemplerne, giver dette en nem måde at opfylde dine interne navnekonventioner på.


Java tag