Java >> Java tutorial >  >> Tag >> java.util

Sådan kortlægger du en forening som et java.util.Map

java.util.List er den mest almindelige repræsentation af en til-mange-forbindelse med JPA og Hibernate. Men er det også den, du vil bruge i din domænemodel? Passer det til dine use cases eller har du brug for smartere adgang til de tilhørende elementer?

Lad os være ærlige, en simpel java.util.List er godt nok i de fleste tilfælde. Men fra tid til anden et java.util.Map ville gøre implementeringen af ​​din forretningslogik så meget lettere.

Så hvorfor ikke bruge JPA's og Hibernates kortlægningsmuligheder og kortlægge foreningen til et Kort ? Kun for de relationer, der bruges som Kort s, selvfølgelig. Kortlægningsdefinitionen kræver blot nogle få yderligere anmærkninger.

Kortlægning af simple nøgleværdi-par

De grundlæggende kortlægninger er ret enkle. JPA og Hibernate giver dig mulighed for at kortlægge en samling af elementer eller en enhedstilknytning til et java.util.Map . Nøglen og værdien af ​​Kort kan være en grundlæggende type, en indlejrbar klasse eller en enhed.

Du har måske genkendt den samling s er ikke understøttet. Hvis du ønsker at tilknytte din tilknytning til et Map> , skal du bruge en lille løsning, som jeg viser dig i slutningen af ​​dette indlæg.

Men lad os først tage et kig på nogle simple kortlægninger og diskutere kortlægningskonceptet.

Repræsenter en til-mange-forening som en Kort

Lad os starte med en standard kortlægningsdefinition af en mange-til-mange-forening, der bruger en Liste . Følgende diagram viser et eksempel på en mange-til-mange-forbindelse mellem Forfatteren og bogen .

Og du kan se standardkortlægningen ved hjælp af en Liste i følgende kodestykke. Hvis du ikke er bekendt med kortlægningen af ​​en-til-mange eller mange-til-mange-foreninger, bør du tage et kig på min ultimative guide til tilknytningskortlægning med JPA og Hibernate, før du fortsætter.

@Entity
public class Author {

	@ManyToMany
	@JoinTable(
		name="BookAuthor",
		joinColumns={@JoinColumn(name="bookId", referencedColumnName="id")},
		inverseJoinColumns={@JoinColumn(name="authorId", referencedColumnName="id")})
	private List<Book> books = new ArrayList<Book>();

	...
}

Hvis du vil repræsentere foreningen med et Kort i stedet for Listen , du skal bare gøre 2 ting:

  1. ændre typen af ​​attributten fra Liste til kort og
  2. definer den databasekolonne, du vil bruge som kortnøgle.

Lad os sige, at du vil bruge titlen kolonne i bogen tabel som kortnøgle. Titlen er en streng attribut for bogen . Så det er en grundlæggende type, der allerede er kortlagt af dine enheder, og du skal blot henvise til den i en @MapKey annotation.

Hvis du ikke ønsker at bruge en af ​​attributterne for den tilknyttede enhed, kan du også bruge:

  • en grundlæggende type med en @MapKeyColumn anmærkning
  • en enum med et @MapKeyEnumerated anmærkning
  • en java.util.Date eller en java.util.Calendar med en @MapKeyTemporal anmærkning

Dvale vil bevare kortnøglen i en ekstra databasekolonne.

Men tilbage til vores eksempel. Når du anvender de beskrevne ændringer, vil kortlægningen af ​​Forfatteren enhed ser sådan ud.

@Entity
public class Author {

	@ManyToMany
	@JoinTable(
		name="AuthorBookGroup",
		joinColumns={@JoinColumn(name="fk_author", referencedColumnName="id")},
		inverseJoinColumns={@JoinColumn(name="fk_group", referencedColumnName="id")})
	@MapKey(name = "title")
	private Map<String, Book> books = new HashMap<String, Book>();

	...
}

Arbejde med Map s i stedet for Liste s

Det er alt, du skal gøre for at definere kortlægningen. Du kan nu bruge kortet i din domænemodel til at tilføje, hente eller fjerne elementer fra tilknytningen.

Author a = new Author();
a.setFirstName("Thorben");
a.setLastName("Janssen");
em.persist(a);

Book b = new Book();
b.setTitle("Hibernate Tips");
b.setFormat(Format.PAPERBACK);
b.getAuthors().add(a);
em.persist(b);

a.getBooks().put(b.getTitle(), b);

Du har også 2 muligheder for at bruge tilknytningen i JPQL-forespørgsel. Du kan blot slutte dig til de 2 entiteter og bruge dem som et almindeligt mange-til-mange forhold.

TypedQuery<Author> q = em.createQuery("SELECT a FROM Author a JOIN a.books b WHERE b.title LIKE :title", Author.class);
q.setParameter("title", "%Hibernate Tips%");
a = q.getSingleResult();

Eller du kan bruge KEY funktion til at referere til kortnøglen i din forespørgsel.

TypedQuery<Author> q = em.createQuery("SELECT a FROM Author a JOIN a.books b WHERE KEY(b) LIKE :title ", Author.class);
q.setParameter("title", "%Hibernate Tips%");
a = q.getSingleResult();

Repræsenter en samling af Embeddables som et kort

Kortlægningen for indlejrbare klasser er ret ens. Den største forskel er, at du skal bruge @ElementCollection annotation i stedet for en @ManyToMany annotation.

Lad os sige, at du vil fortsætte en forfatter med hendes virksomheds- og privatadresse. I din domænemodel kan du modellere det med de 3 klasser, du kan se i følgende diagram.

Forfatteren klasse bliver en enhed, og du kan implementere Adressen klasse som en indlejring. Og for dette eksempel ønsker jeg ikke at beholde adressen es i en simpel ElementCollection . Jeg vil bruge et Kort med AddressType enum.

Som i det foregående eksempel skal du kun gøre 2 ting for at bruge et Kort i stedet for en liste :

  1. ændre typen af ​​attributten fra Liste til kort
  2. definer kortnøglens tilknytning

Det andet trin bliver endda valgfrit, hvis du kun bruger enum som en kortnøgle og ikke som en attribut for den indlejrbare. Hvis du ikke angiver yderligere oplysninger, bruger Hibernate et sæt standardindstillinger til at bevare kortnøglen i en kolonne i samlingstabellen.

Jeg foretrækker at definere kolonnenavnet og EnumType Hibernate skal bruges til at bevare enum. Du kan se et eksempel på det i følgende kodestykke. navnet attribut for @MapKeyColumn annotation definerer navnet på databasekolonnen og @MapKeyEnumerated definerer, hvordan Hibernate skal bestå optællingen.

@Entity
public class Author {

	@ElementCollection
	@MapKeyColumn(name = "address_type")
	@MapKeyEnumerated(EnumType.STRING)
	private Map<AddressType, Address>address = new HashMap<AddressType, Address>();

	...
}

Det er alt, du skal gøre for at repræsentere en samling af indlejrede elementer som et java.util.Map . Du kan nu bruge det på samme måde, som jeg viste dig i det forrige eksempel.

Kortlægning af flere værdier til den samme nøgle

Som du har set i de foregående eksempler, understøtter JPA og Hibernate ingen samling skriv som en kortværdi. Det er uheldigt, fordi det efter min erfaring er det mest almindelige anvendelsestilfælde.

Men der er en lille og nem løsning til disse situationer. Du skal blot introducere en indpakningsentitet, der omslutter Listen eller Kort af enheder.

Lad os udvide eksemplet med ForfatterenBook -forening viste jeg dig i begyndelsen af ​​dette indlæg.

Mest bog s bliver udgivet i andet Format s, f.eks. som e-bog og som paperback. bogen har et unikt ISBN for hvert Format . Så du har brug for 2 databaseposter for at bevare e-bogs- og paperbackversionen af ​​en bog .

Men du kan ikke fortsætte disse 2 poster med kortlægningen fra vores første eksempel. Den 2. bog enhed ville erstatte den første, når du føjer den til Kort-bøgerne .

Du kan rette det ved at introducere en ekstra enhed, der omslutter Listen af Bog enheder.

Kortlægningsdefinitionen af ​​denne enhed er meget enkel. Du skal bare bruge en primær nøgle og en en-til-mange tilknytning til bogen enhed. Jeg introducerede også en titel for Boggruppen som jeg vil bruge som korttast på Forfatter enhed.

@Entity
public class BookGroup {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", updatable = false, nullable = false)
	private Long id;
	
	@OneToMany(mappedBy = "group")
	private List<Book> books = new ArrayList<Book>();
  
	private String title;
  
	...
}

Du kan derefter modellere tilknytningen mellem Boggruppen og Forfatteren enhed som et Kort .

@Entity
public class Author {
  
	@ManyToMany
	@JoinTable(
		      name="AuthorBookGroup",
		      joinColumns={@JoinColumn(name="fk_author", referencedColumnName="id")},
		      inverseJoinColumns={@JoinColumn(name="fk_group", referencedColumnName="id")})
	@MapKey(name = "title")
	private Map<String, BookGroup> bookGroups = new HashMap<String, BookGroup>();
  
  	...
}

Det er alt, du skal gøre for at modellere en forening som et Kort med flere værdier pr. kortnøgle. Du kan nu bruge det på samme måde, som jeg viste dig i det første eksempel.

Vær opmærksom på, at denne kortlægning skaber overhead og redundans. Boggruppen enhed bliver kortlagt til en databasetabel, som vi ikke havde brug for i de foregående eksempler. Og jeg bruger nu titlen af Boggruppen som nøglen til Map bookGroups . Boggruppen titel vil være den samme som titlen af alle bog er i gruppen.

Oversigt

Som du har set, giver JPA og Hibernate dig også mulighed for at repræsentere en forening som et java.util.Map . Det kan være en fordel, når du har brug for mere sofistikeret adgang til de relaterede enheder. Men vær opmærksom på, at repræsentationen som et Kort introducerer en overhead. Du bør derfor kun bruge det, når du virkelig har brug for kortet repræsentation.

Kortlægningsdefinitionen er ret nem. Du definerer blot tilknytningen mellem dine enheder, brug et Kort som attributtypen og definere kortnøglen med en @MapKey , @MapKeyColumn , @MapKeyEnumerated eller @MapKeyTemporal annotation.


No
Java tag