Java >> Java tutorial >  >> Tag >> String

Java-tekstblokke – Brug af flerlinjede strenge med Hibernate &JPA

JPA og Hibernate kræver, at du skriver mange strenge. Du bruger dem til at oprette ad-hoc og navngivne forespørgsler med JPQL, til at definere native SQL-forespørgsler og til at specificere henteadfærden i en EntityGraph. Indtil Java 13 gjorde manglen på multiline-strenge i Java alle disse opgaver enten irriterende at implementere eller koden svær at læse. Du skulle enten sammenkæde flere strenge eller sætte alt i en enkelt-linjes streng.

String sql = "SELECT new com.thorben.janssen.BookAuthorReviewCount(b.title, concat(a.firstName, ' ', a.lastName), size(b.reviews)) FROM Book b JOIN b.author a GROUP BY b.title, a.firstName, a.lastName"

Det ændrede sig drastisk med introduktionen af ​​Java Text Blocks. Du kan endelig definere strenge, der strækker sig over flere linjer ved at bruge 3 anførselstegn.

String sql = """
			SELECT new com.thorben.janssen.BookAuthorReviewCount(
							b.title, 
							concat(a.firstName, ' ', a.lastName), 
							size(b.reviews)
						) 
			FROM Book b 
				JOIN b.author a 
			GROUP BY b.title, a.firstName, a.lastName
			"""

Som du kan se, gjorde det SQL-sætningen meget lettere at læse. Og fordi dette er en standard Java-funktion, kan du bruge den overalt, hvor du bruger en streng. Men det er især nyttigt, hvis strengen er lang og kompleks nok til at tilføje linjeskift. Lad os tage et kig på et par eksempler.

Tekstblokke i JPQL- og HQL-forespørgsler

JPAs forespørgselssprog JPQL og den Hibernate-specifikke udvidelse HQL gør dig i stand til at skrive forespørgsler baseret på din enhedsmodel. Syntaksen minder meget om SQL, og jeg forklarede det meget detaljeret i min guide til JPQL.

JPQL og HQL er ikke så kraftfulde som SQL. Ikke desto mindre kan du oprette temmelig komplekse forespørgsler, der er svære at læse på en enkelt linje.

Følgende forespørgsel returnerer BookAuthorReviewCount-objekter, der kan bruges i en listevisning i brugergrænsefladen. Forespørgslen vælger navnet på forfatteren ved at forbinde fornavn og efternavn, bogens titel, og tæller antallet af anmeldelser af hver bog.

Denne forespørgsel er ikke let at læse, hvis du skriver den som en enkel, enkelt linje streng.

TypedQuery<BookAuthorReviewCount> q = em.createQuery(
				"SELECT new com.thorben.janssen.BookAuthorReviewCount(b.title, concat(a.firstName, ' ', a.lastName), size(b.reviews)) FROM Book b JOIN b.author a GROUP BY b.title, a.firstName, a.lastName",
				BookAuthorReviewCount.class);
List<BookAuthorReviewCount> books = q.getResultList();

Tilføjelse af et par linjeskift og håndtering af det som en flerlinjestreng gør det meget nemmere.

TypedQuery<BookAuthorReviewCount> q = em.createQuery("""
				SELECT new com.thorben.janssen.BookAuthorReviewCount(
								b.title, 
								concat(a.firstName, ' ', a.lastName), 
								size(b.reviews)
							) 
				FROM Book b 
					JOIN b.author a 
				GROUP BY b.title, a.firstName, a.lastName
				""",
				BookAuthorReviewCount.class);
List<BookAuthorReviewCount> books = q.getResultList();

Og det samme gælder, hvis du opretter den samme forespørgsel som en navngivet forespørgsel. Du kan derefter bruge tekstblokken i @NamedQuery-annotationen.

@Entity
@NamedQuery(
	name = "selectBookAuthorReviewCount", 
	query = """
			SELECT new com.thorben.janssen.BookAuthorReviewCount(
							b.title, 
							concat(a.firstName, ' ', a.lastName), 
							size(b.reviews)
						) 
			FROM Book b 
				JOIN b.author a 
			GROUP BY b.title, a.firstName, a.lastName
			""")
public class Author { ... }

Tekstblokke i native SQL-forespørgsler

JPA blev bevidst designet som en utæt abstraktion, der giver dig adgang til det underliggende JDBC-lag. Du kan bruge det til at skrive og udføre native SQL-forespørgsler, som din persistensudbyder ikke analyserer. Ved at bruge denne tilgang kan du bruge alle forespørgselsfunktioner, der understøttes af din database.

Jeg bruger det i den følgende SQL-sætning til at vælge titlen på alle blogindlæg og bøger, publikationstypen og forfatterens navn. Som du kan se, er bøger og blogindlæg gemt i 2 separate tabeller. Jeg forespørger begge tabeller for at få titlen sammen med publikationstypen og bruger en UNION-klausul til at flette resultaterne til ét resultatsæt.

Query q = em.createNativeQuery("""
				SELECT title, 'blog' as type, firstName, lastName
					FROM blogpost JOIN author on author.id = blogpost.author_id
				UNION
				SELECT title, 'book' as type, firstName, lastName
					FROM book JOIN author on author.id = book.author_id
				""", 
				"PublicationAuthorMapping");
List<PublicationAuthor> pubs = q.getResultList();

Det er noget, du ikke kan gøre med JPQL. Men du kan nemt gøre det ved hjælp af en indbygget SQL-sætning. Og hvis du kombinerer din oprindelige SQL-forespørgsel med en @SqlResultSetMapping, kan du få dit forespørgselsresultat som entitetsobjekter, DTO-objekter eller skalære værdier.

Jeg refererede til en sådan kortlægning i det forrige kodestykke for at kortlægge hver post i resultatsættet til et PublicationAuthor-objekt. Den nødvendige kortlægningsdefinition er relativt enkel. Du behøver kun at bruge en @ConstructorResult-annotering, angive den klasse, du vil instantiere som targetClass, og definere en matrix af @ColumnResult-annoteringer for at specificere konstruktørparametrene.

@Entity
@SqlResultSetMapping(
	name = "PublicationAuthorMapping", 
	classes = @ConstructorResult(
					targetClass = PublicationAuthor.class, 
					columns = {@ColumnResult(name = "title"),
							   @ColumnResult(name = "type"),
							   @ColumnResult(name = "firstName"),
							   @ColumnResult(name = "lastName")}))
public class Author { ... }

Tekstblokke for at definere EntityGraphs

Du kan ikke kun bruge Java-tekstblokke til at definere dine forespørgsler. I version 5.4 introducerede Hibernate en API til at parse en streng i en EntityGraph. Disse strenge beskriver en hierarkisk struktur og deres læsbarhed fordele ved multiline strenge.

En EntityGraph fortæller Hibernate, hvilke associationer den skal initialisere, når resultatet af en forespørgsel hentes. Dette er et vigtigt værktøj til justering af ydeevne, som du skal kende, når du arbejder med Hibernate.

Den streng, der bruges i det følgende eksempel, bliver parset ind i en EntityGraph, der fortæller Hibernate at hente bogen og blogPost-tilknytningerne, der er defineret på Author-entiteten. For bogforeningen vil den også hente det tilknyttede forlag og den redaktør, der har arbejdet på bogen.

RootGraph graph = GraphParser.parse(Author.class, 
									"""
									blogPosts, 
									books(publisher(editor))""", 
									em);

TypedQuery<Author> q = em.createQuery("SELECT a FROM Author a", Author.class);
q.setHint(GraphSemantic.FETCH.getJpaHintName(), graph);
List<Author> authors = q.getResultList();

Hvis du bruger sådan en EntityGraph, kan Javas tekstblokfunktion forbedre læsbarheden af ​​din kode. Men du skal også dobbelttjekke din forespørgsel og analysere, om hentning af så mange associationer gør din forespørgsel for kompleks. Afhængigt af antallet af elementer i hver tilknytning kan det være bedre at opdele denne forespørgsel i flere.

Konklusion

Javas tekstblokke kan ligne en lille funktion, men de kan forbedre læsbarheden af ​​din kode meget.

Fordi det er en standard Java-funktion, kan du bruge den overalt i din kode. Men ikke alle steder vil gavne det samme. Tekstblokke er især nyttige, hvis den oprettede streng naturligt indeholder linjeskift eller bliver lettere at læse, hvis du deler den over flere linjer.

Med JPA og Hibernate opretter du en masse Strings, der hører til i 2. kategori. Især forespørgsler bliver ofte lange og komplekse. Ved at sprede dem over flere linjer kan du strukturere dem visuelt og forbedre deres læsbarhed. Jeg er sikker på, at det ikke vil tage lang tid, før alle bruger Java-tekstblokke til at skrive deres forespørgsler.


Java tag