jpa queries

25
Java Persistence Queries Effektive DB-Abfragen mit Features aus dem Standard und darüber hinaus Expertenkreis Java, 18.06.2015, GEDOPLAN Dirk Weil, GEDOPLAN GmbH

Upload: gedoplan

Post on 07-Aug-2015

131 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Jpa queries

Java Persistence Queries

Effektive DB-Abfragen mit Features aus dem Standard

und darüber hinaus

Expertenkreis Java, 18.06.2015, GEDOPLAN

Dirk Weil, GEDOPLAN GmbH

Page 2: Jpa queries

Java Persistence Queries

Dirk Weil

GEDOPLAN GmbH, BielefeldJava EE seit 1998Konzeption undRealisierungSeminareVorträgeVeröffentlichungen

2

Page 3: Jpa queries

Java Persistence Queries

Java Persistence

Mapping OO RDBMSPOJOsDetachment macht DTOs verzichtbar

API zum Speichern, Laden, Löschen, Finden von DB-EinträgenEclipselink, Hibernate, OpenJPA, …

Seit Java EE 5 im StandardAuch in SE nutzbar

Aktuelle Version 2.13

Page 4: Jpa queries

Java Persistence Queries

Demo-Entities

4

@Entitypublic class Publisher{ @Id private Integer id; private String name;

@ManyToOne private Country country;

@OneToMany(mappedBy = "publisher") private List<Book> books;

@Entitypublic class Book{ @Id private Integer id; private String name; private String isbn; private int pages;

@ManyToOne private Publisher publisher;

1

n

@Entitypublic class Country{ @Id @Column(name = "ISO_CODE") private String isoCode; private String name;n

1

Page 5: Jpa queries

Java Persistence Queries

JPQL

Java Persistence Query LanguageSQL-ähnlich, jedoch objektorientiertEntityManager.createQuery liefert TypedQuery<T>Ausführung mit getSingleResult bzw. getResultList

5

Publisher publisher = entityManager .createQuery("select p from Publisher p where p.name=?1", Publisher.class) .setParameter(1, "O'Melly Publishing") .getSingleResult();

List<Book> books = entityManager .createQuery("select b from Book b where b.pages>=500", Book.class) .getResultList();

Page 6: Jpa queries

Java Persistence Queries

JPQL

Navigation durch Relationen mit '.' und join

6

List<Book> books = entityManager .createQuery("select b from Book b where b.publisher.country=?1", Book.class) .setParameter(1, countryDE) .getResultList();

List<Publisher> publishers = entityManager .createQuery("select distinct p from Publisher p " + "join p.books b where b.pages>=500", Publisher.class) .getResultList();

Page 7: Jpa queries

Java Persistence Queries

JPQL

JPQL SQL "leichtgewichtig"

7

List<Publisher> publishers = entityManager .createQuery("select distinct p from Publisher p " + "join p.books b where b.pages>=500", Publisher.class) .getResultList();

SELECT DISTINCT t1.ID, t1.NAME, t1.COUNTRY_ISO_CODE FROM JPA_BOOK t0, JPA_PUBLISHER t1 WHERE ((t0.PAGES >= ?) AND (t0.PUBLISHER_ID = t1.ID))

select distinct publisher0_.ID as ID1_35_, publisher0_.COUNTRY_ISO_CODE as COUNTRY_3_35_, publisher0_.name as name2_35_ from JPA_PUBLISHER publisher0_ inner join JPA_BOOK books1_ on publisher0_.ID=books1_.publisher_ID where books1_.pages>=500

Eclipselink

Hibernate

Page 8: Jpa queries

Java Persistence Queries

Extended Queries

Selektion von Einzelattributen etc.

8

List<Object[]> resultList = entityManager .createQuery("select p.name, p.country.name from Publisher p", Object[].class) .getResultList();

List<Object[]> resultList = entityManager .createQuery("select p, count(b) from Publisher p " + "left join p.books b " + "group by p", Object[].class) .getResultList();

Page 9: Jpa queries

Java Persistence Queries

Extended Queries

Selektion von Einzelattributen etc.

9

List<NameAndCount> resultList = entityManager .createQuery("select new somepkg.NameAndCount(p.name, sum(b.pages)) " + "from Publisher p " + "join p.books b " + "where b.name like '%Java%' " + "group by p", NameAndCount.class) .getResultList();

public class NameAndCount{ private String name; private Number count;

public NameAndCount(String name, Number count) {

Page 10: Jpa queries

Java Persistence Queries

Native Queries

bei schon vorhandenen SQL-Queriesfür "besondere Fälle" (proprietäres SQL, spezielles Tuning, …)

"Verlust" des O/R-Mappingsauch Komplettobjekte als Ergebnis möglich

10

@SuppressWarnings("unchecked")List<Object[]> resultList = entityManager .createNativeQuery("SELECT DISTINCT p.ID, p.NAME, p.COUNTRY_ISO_CODE " + "FROM JPA_PUBLISHER p, JPA_BOOK b " + "WHERE b.PUBLISHER_ID = p.ID AND b.PAGES >= ?") .setParameter(1, 500) .getResultList();

Page 11: Jpa queries

Java Persistence Queries

Stored Procedure Queries

für "noch besonderere Fälle"IN-, OUT- oder IN/OUT-Parameter

11

CREATE PROCEDURE GET_COCKTAIL_COUNT(IN ZUTAT_NAME VARCHAR(255)) BEGIN select count(*) from JPA_COCKTAIL C where exists (…); END

Number count = (Number) entityManager .createStoredProcedureQuery("GET_COCKTAIL_COUNT") .registerStoredProcedureParameter("ZUTAT_NAME", String.class, ParameterMode.IN) .setParameter("ZUTAT_NAME", "Sekt") .getSingleResult();

Page 12: Jpa queries

Java Persistence Queries

Criteria Query API

Textbasierte Queries lassen sich zur Compile- oder Deploymentzeit nicht prüfen

SyntaxNamenTypen

12

select p fron Publisher p

select p from Publisher

select p from Publisher p where p.nam=:name

List<Publisher> books = entityManager .createQuery("select b from Book b where b.pages>=500", Publisher.class) .getResultList();

Page 13: Jpa queries

Java Persistence Queries

Criteria Query API

Query wird mittels API kombiniertCriteriaQuery<T> mittels CriteriaBuilder erstellen

from, where, select …

Ausführung als "normale" TypedQuery<T>

13

CriteriaBuilder cb = entityManager.getCriteriaBuilder();CriteriaQuery<Book> cq = cb.createQuery(Book.class);

List<Book> books = this.entityManager .createQuery(cq) .getResultList();

Page 14: Jpa queries

Java Persistence Queries

Criteria Query API

14

// select b from Book b where b.pages>=500Root<Book> b = cq.from(Book.class);cq.select(b) .where(cb.greaterThanOrEqualTo(b.get(Book_.pages), 500));

// select b from Book b where b.publisher.country=:countryRoot<Book> b = cq.from(Book.class);cq.select(b) .where(cb.equal(b.get(Book_.publisher).get(Publisher_.country), cb.parameter(Country.class, "country")));

List<Book> books = this.entityManager .createQuery(cq) .setParameter("country", CountryTest.testCountryDE) .getResultList();

Page 15: Jpa queries

Java Persistence Queries

Criteria Query API

nutzt statisches Metamodell E_ zu persistenter Klasse E

15

@StaticMetamodel(Publisher.class)public abstract class Publisher_ { public static volatile SingularAttribute<GeneratedIntegerIdEntity, Integer> id; public static volatile SingularAttribute<Publisher, String> name; public static volatile SingularAttribute<Publisher, Country> country; public static volatile ListAttribute<Publisher, Book> books;

@Entitypublic class Publisher{ @Id private Integer id; private String name;

@ManyToOne private Country country;

@OneToMany(mappedBy = "publisher") private List<Book> books;

Page 16: Jpa queries

Java Persistence Queries

Criteria Query API

Metamodell wird durch Annotation Processor generiertmuss im Compile Classpath liegen (z. B. als Maven Dependency)

wird vom Compiler aufgerufen (Java 6+)

16

<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-jpamodelgen</artifactId> <version>4.3.10.Final</version> <scope>provided</scope> <optional>true</optional></dependency>

Page 17: Jpa queries

Java Persistence Queries

Criteria Query API

Annotation Processing in Eclipsekann in Luna entsprechend Maven konfiguriert werden

sonst: Im Projekt Annotation Processing manuell konfigurierenJava Compiler Annotation Processing aktivieren, Zielordner wählen Factory Path Compile Classpath einstellen

17

Page 18: Jpa queries

Java Persistence Queries

Root<Publisher> p = cq.from(Publisher.class);ListJoin<Publisher, Book> b = p.join(Publisher_.books);cq.select(cb.construct(NameAndCount.class, p.get(Publisher_.name), cb.sum(b.get(Book_.pages)))) .where(cb.like(b.get(Book_.name), "%Java%")) .groupBy(p);

Criteria Query API

Problem: Lesbarkeit

18

// select new NameAndCount(p.name, sum(b.pages)) // from Publisher p // join p.books b // where b.name like '%Java%' // group by p

Page 19: Jpa queries

Java Persistence Queries

Root<Publisher> p = cq.from(Publisher.class);ListJoin<Publisher, Book> b = p.join(Publisher_.books);cq.select(p.get(Publisher_.name)) .distinct(true) .where(cb.and(cb.equal(p.get(Publisher_.country), cb.parameter(Country.class, "country")), cb.like(b.get(Book_.name), "%Java%")));

Criteria Query API

Problem: API durch CriteriaBuilder (u. a.) nicht "fluent"

19

p ~ Publisher;b ~ p.books;c ~ Parameter(Country);select(p.name) .distinct() .from(p) .where(p.country.equal(c).and(b.name.like("%Java%")))

Wunsch

Ist

Page 20: Jpa queries

Java Persistence Queries

QueryDSL

Open Source (http://www.querydsl.com)Sponsored by Mysema (http://www.mysema.com)Query wird mittels API kombiniert

Query Roots und JPAQuery für EntityManager erzeugen

Methoden from, join, where, orderBy, distinct etc.

Query ausführen mittels singleResult, list

20

QPublisher p = QPublisher.publisher;JPAQuery jpaQuery = new JPAQuery(this.entityManager)

List<Publisher> result = jpaQuery.list(p);

Page 21: Jpa queries

Java Persistence Queries

QueryDSL

benötigt generierte Klassenähnlich JPA Metamodell

21

@Generated("com.mysema.query.codegen.EntitySerializer")public class QPublisher extends EntityPathBase<Publisher> { public static final QPublisher publisher = new QPublisher("publisher"); public final StringPath name = createString("name"); public final ListPath<Book, QBook> books = this.<Book, QBook>createList("books", … public final de.gedoplan.seminar.jpa.demo.basics.entity.QCountry country;

@Entitypublic class Publisher{ @Id private Integer id; private String name;

@ManyToOne private Country country;

@OneToMany(mappedBy = "publisher") private List<Book> books;

Page 22: Jpa queries

Java Persistence Queries

QueryDSL

Metamodell wird durch Annotation Processor generiertz. B. mit Maven Plugin

22

<plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals><goal>process</goal></goals> <configuration> <outputDirectory>target/generated-sources/annotations</outputDirectory> <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions></plugin>

Page 23: Jpa queries

Java Persistence Queries

QueryDSL

23

QPublisher p = QPublisher.publisher;QBook b = QBook.book;Param<Country> countryParam = new Param<>(Country.class);

List<String> names = new JPAQuery(this.entityManager) .from(p) .innerJoin(p.books, b) .where(p.country.eq(countryParam).and(b.name.like("%Java%"))) .distinct() .set(countryParam, CountryTest.testCountryDE) .list(p.name);

Root<Publisher> p = cq.from(Publisher.class);ListJoin<Publisher, Book> b = p.join(Publisher_.books);cq.select(p.get(Publisher_.name)) .distinct(true) .where(cb.and(cb.equal(p.get(Publisher_.country), cb.parameter(Country.class, "country")), cb.like(b.get(Book_.name), "%Java%")));

QueryDSL

Criteria

Query API

Page 24: Jpa queries

Java Persistence Queries

QueryDSL

Bisherige ErfahrungenIntuitives API (meist …)gute Lesbarkeit

API hat noch Schwächenz. B. Umwandlung von JPAQuery nur in Query möglich, nicht in TypedQuery

Noch nicht ganz ausgereifteinige Bugs (z. B. Constructor Result akzeptiert keine Aggregat-Ausdrücke)Dokumentation lückenhaft und fehlerhaftUmstellung com.mysema.querydsl com.querydsl buggy

24

Page 25: Jpa queries

Java Persistence Queries

More

http://www.gedoplan-it-training.deSeminare in Berlin, Bielefeld, Inhousehttp://www.gedoplan-it-consulting.deReviews, Coaching, …

http://javaeeblog.wordpress.com/http://expertenkreisjava.blogspot.de/

[email protected]@dirkweil

25