Java >> Java tutorial >  >> Tag >> static

Opret typesikre forespørgsler med JPA statiske metamodel

Når du skriver en kriterieforespørgsel eller opretter en dynamisk enhedsgraf, skal du referere til enhedsklasserne og deres attributter. Den hurtigste og nemmeste måde er at angive de nødvendige navne som streng s. Men dette har flere ulemper, f.eks. du skal huske eller slå alle navnene på enhedsattributterne op, når du skriver forespørgslen. Men det vil også give endnu større problemer i senere faser af projektet, hvis du skal refaktorisere dine entiteter og ændre navnene på nogle attributter. I så fald skal du bruge søgefunktionen på din IDE og prøve at finde alle strenge, der refererer til de ændrede attributter. Dette er en kedelig og fejltilbøjelig aktivitet, som nemt vil tage mest tid af refaktoreringen.

Derfor foretrækker jeg at bruge den statiske metamodel til at skrive kriterieforespørgsler og dynamiske entitetsgrafer. Dette er en lille funktion defineret af JPA-specifikationen, som giver en typesikker måde at referere til enhederne og deres egenskaber på.

Eksempel på enhed

Som du kan se i kodestykkerne nedenfor, har jeg forberedt en simpel enhed til dette eksempel. Det repræsenterer en Forfatter med et id, et for- og et efternavn og en liste over bøger, hun/han har skrevet. Jeg springer bogen over enhed her, fordi vi ikke har brug for det til følgende eksempler.

@Entity
public class Author implements Serializable {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "id", updatable = false, nullable = false)
	private Long id;
	
	@Version
	@Column(name = "version")
	private int version;

	@Column
	private String firstName;

	@Column
	private String lastName;
	
	@ManyToMany(mappedBy="authors")
	private Set<Book> books = new HashSet<Book>();
	
	...
}

Statisk metamodelklasse

Klassen af ​​den statiske metamodel ligner entiteten. Baseret på JPA-specifikationen er der en tilsvarende metamodelklasse for hver administreret klasse i persistensenheden. Du kan finde det i den samme pakke, og det har samme navn som den tilsvarende administrerede klasse med et tilføjet '_' i slutningen. Så metamodelklassen i dette eksempel er placeret i pakken org.thoughts.on.java.model og har navnet Forfatter .

@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(Author.class)
public abstract class Author_ {

	public static volatile SingularAttribute<Author, String> firstName;
	public static volatile SingularAttribute<Author, String> lastName;
	public static volatile SetAttribute<Author, Book> books;
	public static volatile SingularAttribute<Author, Long> id;
	public static volatile SingularAttribute<Author, Integer> version;

}

Som du ser i kildekoden, er metamodelklassen Author_ giver en attribut for hver attribut for Author enhed. Hver metamodel-attribut giver information om dens type og den enhed, den tilhører.

Brug af metamodelklasser

Du kan bruge metamodelklasserne på samme måde, som du bruger String-referencen til entiteterne og attributterne. API'erne til kriterieforespørgsler og dynamiske enhedsgrafer giver overbelastede metoder, der accepterer strenge og implementeringer af attributgrænsefladen.

Jeg bruger metamodel-klassen til at oprette en kriterieforespørgsel for at søge efter alle Author s, hvis fornavn starter med "J". Som du kan se, bruger jeg de samme metoder, som jeg ville gøre med strengreferencerne til entitetsattributterne.

CriteriaBuilder cb = this.em.getCriteriaBuilder();

// create the query
CriteriaQuery<Author> q = cb.createQuery(Author.class);

// set the root class
Root<Author> a = q.from(Author.class);

// use metadata class to define the where clause
q.where(cb.like(a.get(Author_.firstName), "J%"));

// perform query
this.em.createQuery(q).getResultList();

Som jeg beskrev tidligere, kan metamodelklasserne også bruges til at skabe dynamiske entitetsgrafer. Du kan se et eksempel på dette i følgende kodestykke.

// create the entity graph
EntityGraph graph = this.em.createEntityGraph(Author.class);
// use metadata class to define the subgraph
Subgraph<Book> bookSubGraph = graph.addSubgraph(Author_.books);

// perform query
List<Author> authors = this.em
				.createQuery("SELECT DISTINCT a FROM Author a", Author.class)
				.setHint("javax.persistence.fetchgraph", graph).getResultList();

Generering af metamodelklasser

OK, vent et sekund, før du går videre og begynder at oprette metamodelklasser til dine entiteter. Det er der ikke behov for. JPA-specifikationen foreslår at bruge en annotationsprocessor til at generere metamodelklasserne, og det er, hvad de forskellige implementeringer gør. Desværre giver hver implementering sin egen måde for det.

Jeg beskriver den nødvendige maven build-konfiguration til Hibernate nedenfor. Hvis du bruger en anden JPA-implementering (f.eks. EclipseLink, OpenJPA) eller et byggeværktøj, skal du tjekke den tilhørende dokumentation.

Det er ekstremt simpelt at tilføje Hibernate Static Metamodel Generator til din byggeproces. Du behøver kun at tilføje det til din byggeklassesti. Følgende kodestykke viser den nødvendige afhængighedserklæring for en maven-build.

...

<dependencies>
	<dependency>
		<groupId>org.hibernate</groupId>
		<artifactId>hibernate-jpamodelgen</artifactId>
	</dependency>
</dependencies>

...

Hvis du bruger maven, gemmes de genererede metamodelklasser i target/generated-classes folder. Så du skal tilføje denne mappe til klassestidefinitionen i din IDE.

Konklusion

Den statiske metamodel giver en typesikker og nem måde at oprette kriterieforespørgsler og dynamiske entitetsgrafer. Dette fremskynder den indledende implementering og gør fremtidige refactorings meget nemmere end at referere til attributterne via Strings.

De forskellige persistensudbyderimplementeringer giver annotationsprocessorer til at generere metamodelklasserne på byggetidspunktet. Dette sikrer, at ændringer på entiteterne afspejles på metamodellen og undgår fejl under kørsel.


Java tag