Java Persistenz mit JPA und Hibernate Michael Plöd - Senacor Technologies AG Michael Plöd • Partner bei Senacor Technologies AG • Hibernate Erfahrung seit 2002 • [email protected] Ihr Hintergrund • Erfahrung mit Java? • Erfahrung mit Java Persistenz? • Tägliche Projektarbeit? • Einsatz von Hibernate im Projekt? • Ihre Erwartungen an die Schulung? Agenda Zeit Thema 08:00 - 09:00 Einleitung und theoretische Grundlagen 09:00 - 09:30 Hibernate im Überblick 09:45 - 10:30 Mapping von Klassen 10:30 - 11:15 Arbeiten mit Hibernate 11:15 - 11:45 Queries Datenbank Java Anwendung <<TABLE>> FOTOS ID <PK> NAME ... Foto <<TABLE>> FOTOS_IN_GALERIEN PIC_ID <PK><FK> GAL_ID <PK><FK> 0..* Object-Relational Impedance Mismatch 0..* Galerie <<TABLE>> GALERIEN ID <PK> TITEL ... Impedance Mismatch Datenbank Identität Granularität Vererbung Navigation Assoziationen OO-Software • Primärschlüssel • Objekt-Identität • Objekt-Gleichheit • Identity vs Equality • Erstellung eigener • Eigene Typen können • Vererbung nur durch • Vererbung ist Bestandteil • Navigation in jeder • Navigation wird explizit • Navigation über Joins • Join-Tabellen bei n:m • To-One Relationen (Pointer) • To-Many Relationen Datentypen sehr proprietär Datenmodell Richtung ohne Einschränkbarkeit möglich jederzeit erstellt werden von OO-Software deklariert • Gerichtete Navigation somit möglich (Collections) Java Persistenz Feature-Set Hibernate • JDBC ist Java SE Standard JPA 2.0 • iBATIS und Spring JDBC Template sind JDBC Frameworks • JPA 1, JPA 2 und Hibernate sind ORMapper JPA 1.0 iBATIS JDBC-Template JDBC Code Menge / Komplexität JDBC ist doch einfach Einfacher Select mit JDBC List<User> users = new ArrayList<User>(); Class.forName(”org.postgresql.Driver”); Connection con = DriverManager.getConnection(url, username, password); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(”SELECT * FROM USERS”); while(rs.hasNext()) { User u = new User(); u.setName(rs.getString(1)); users.add(u); } rs.close(); stmt.close(); con.close(); ! Exception Handling fehlt Einfacher Select mit JDBC try { List<User> users = new ArrayList<User>(); Class.forName(”org.postgresql.Driver”); Connection con = DriverManager.getConnection(url, username, password); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(”SELECT * FROM USERS”); while(rs.hasNext()) { User u = new User(); u.setName(rs.getString(1)); users.add(u); } rs.close(); stmt.close(); con.close(); } catch (SQLException e) { //Exception Handling } Resource Leaks „Einfacher“ Select mit JDBC Connection con = null; Statement stmt = null; ResultSet rs = null; try { List<User> users = new ArrayList<User>(); Class.forName(”org.postgresql.Driver”); con = DriverManager.getConnection(url, username, password); stmt = con.createStatement(); rs = stmt.executeQuery(”SELECT * FROM USERS”); while(rs.hasNext()) { User u = new User(); u.setName(rs.getString(1)); users.add(u); } rs.close(); stmt.close(); con.close(); } catch (SQLException e) { //Exception Handling } finally { if(rs != null) { try {rs.close();} catch (SQLException e) {...}; } if(stmt != null) { try {stmt.close();} catch (SQLException e) {...}; } if(con != null) { try {con.close();} catch (SQLException e) {...}; } } JDBC Frameworks Eigenschaften Pro Contra Beispiele • Adressieren Komplexität von JDBC • Bieten wiederverwendbare Komponenten • Einfache Typ Mappings (z.B. RowMapper) • Einfach zu benutzen • Wenige Fallstricke • Auch für extrem komplexe Legacy Datenbanken geeignet • Begrenztes Caching • Beschränkte Typ Mappings • Zahlreiche manuelle Aufrufe • Spring JDBCTemplate • iBATIS OR-Mapper Eigenschaften Pro Contra Beispiele • Adressieren Komplexität von JDBC • Komplette Typ Mappings • Ausgefeiltes Caching System • Transaktionales Write-Behind • Einfach zu benutzen • Caching • Code-Menge • Manchmal komplex • Schwächen bei schlecht entworfenen Legacy Datenbanken • Hibernate • EclipseLink • Open JPA Aufbau eines ORMappers Configuration Factory 2nd Level Cache Persistence Context 1st Level Cache Abbildung auf Datenbank Adresse Person Konto XML PERSONEN Mapping public class Person { private Adresse adresse; private Set<Konto> konten; } //... getter und setter Annotationen KONTEN Aufbau von JPA Persistence EntityManager Factory 2nd Level Cache EntityManager 1st Level Cache Aufbau von Hibernate Configuration SessionFactory 2nd Level Cache Session 1st Level Cache Hibernate‘s Module ORM EntityManager • Kern von Hibernate • OR-Mapping Engine • XML- und Annotation-Mappings • JPA Implementierung auf Basis von Core und Annotations Shards • Unterstützung für verteilte Datenbestände • Von Google Search • Mächtige Volltext-Suche • Basiert auf Lucene OGM • Hibernate für NoSQL Stores • DDL Generierung Bean Validation • Validierung • DDL Generierung Transaktionales Write-Behind begin commit Transaktion open Session saves: 1 saves: 1 updates: 1 close session.save(person) Logik session.update(konto) DATENBANK INSERT UPDATE Agenda Zeit Thema 08:00 - 09:00 Einleitung und theoretische Grundlagen 09:00 - 09:30 JPA im Überblick 09:45 - 10:30 Mapping von Klassen 10:30 - 11:15 Arbeiten mit Hibernate 11:15 - 11:45 Queries JPA im Überblick Aufbau von JPA Persistence EntityManager Factory 2nd Level Cache EntityManager 1st Level Cache Einfaches Mapping @Entity public class Person { @Id private long id; private String vorname; private String nachname; private long getId() { return id; } private void setId(long id) { this.id = id; } //... weitere getter und setter //... equals und hashCode } Person ID NUMBER <PK> VORNAME VARCHAR NACHNAME VARCHAR Konfiguration META-INF/persistence.xml <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="test"> <class>com.senacor.schulung.hibernate.domain.Person</class> <properties> <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" /> <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:hsql://localhost/testdb" /> <property name="javax.persistence.jdbc.user" value="sa" /> <property name="javax.persistence.jdbc.password" value="" /> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> </properties> </persistence-unit> </persistence> Initialisierung EntityManagerFactory Aus persistence.xml wird einmalig EntityManagerFactory erstellt EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory( "test" ); Arbeiten mit JPA 1. EntityManager erstellen 2. Transaktion starten 3. Operationen durchführen 4. Transaktion committen 5. EntityManager schließen EntityManager e = emf.createEntityManager() User u = new User(); u.setName("TestUser"); e.getTransaction().begin(); e.persist(u); e.getTransaction().commit(); e.close(); Agenda Zeit Thema 08:00 - 09:00 Einleitung und theoretische Grundlagen 09:00 - 09:30 JPA im Überblick 09:45 - 10:30 Mapping von Klassen 10:30 - 11:15 Arbeiten mit Hibernate 11:15 - 11:45 Queries Einfache Mappings • Domänenmodellierung • Entitäten • Attribute • Primärschlüssel • Value Objects • Assoziationen Identifikation von Entitäten und Value Objects • Ein gutes Domänenmodell unterscheidet zwischen Entitäten und Value Objects • Des Weiteren ist die Navigation im Modell klar gegliedert und folgt einheitlichen Richtlinien • Fokus des Modells sollte primär Fachlichkeit sein Entitäten besitzen eine konstante Identität und einen eigenen Lebenszyklus ★ Identität durch Attribute bestimmt ★ Kein eigener Lebenszyklus Value Objects Buchtip: Domain Driven Design von Eric Evans Addison Wesley Verlag ISBN: 0321125215 Mapping von Entitäten • Eine Entität muss mit @Entity gemappt werden • Eine Entität muss einen mit @Id annotierten Primärschlüssel haben • Eine Entität muss einen No-Arg Constructor haben Beispiel @Entity @Table(name=“PERSONEN“) public class Person { @Id private long id; private String vorname; private String nachname; private long getId() { return id; } private void setId(long id) { this.id = id; } //... weitere getter und setter //... equals und hashCode } PERSONEN ID NUMBER <PK> VORNAME VARCHAR NACHNAME VARCHAR PrimärschlüsselMapping @Id private long id; • Default: manuell gesetzter Primärschlüssel Generierungs Strategien Name Description increment Generiert long, int, short basierend auf Tabelle. Nicht in Cluster! identity Identity Columns in DB2, MySQL, MS SQL Server, Sybase, HSQL sequence Sequence in DB2, PostgreSQL, Oracle, SAP DB, McKoi, Interbase hilo HI/LO Algorithmus basierend auf Tabelle seqhil HI/LO Algorithmus basierend auf Sequence uuid 128-bit UUID Algorithmus, auf Netzwerk eingeschränkt guid GUID String in MS SQL Server und MySQL native Identity, Sequence oder hilo je nach Datenbank assigned ID wird von Applikation zugewiesen select Zieht ID, die durch Trigger vergeben wird foreign Zieht ID, die durch anderes Objekt vergeben wird sequence-identity Spezialisierte Sequence Generierung basierend auf JDBC 3 PrimärschlüsselMapping Beispiele @Entity @javax.persistence.SequenceGenerator ( name=“SEQ“, sequenceName=“seq_person“) public class Person { @Id @GeneratedValue( strategy=GenerationType.SEQUENCE, generator=“SEQ“) private long id; PrimärschlüsselMapping Beispiele @Entity @javax.persistence.TableGenerator ( name="TAB_GEN", table="GENERATOR_TABLE", pkColumnName = "key", valueColumnName = "hi", pkColumnValue = "PERS", allocationSize = "20") public class Person { @Id @GeneratedValue( strategy=GenerationType.TABLE, generator=“TAB_GEN“) private long id; Mapping von Attributen • Platzierung von Annotationen auf Attribut oder Getter-Ebene möglich • Platzierung entscheidet über Zugriff (via Reflection oder über Getter) • Default: jedes nicht annotierte Attribut ist persistent • Alle gängigen Typen werden unterstützt Mapping von Attributen • @Column für Spaltenkonfiguration • Sondermappings für Date und (B)LOB Support • @Transient für nicht-persistente Attribute Attribute-Mapping Beispiele @Entity public class Person { @Id @Column(name=“PERS_ID“) private long id; @Column(name=“PERS_VORNAME“, nullable=“false“) private String vorname; @Column(name=“PERS_NACHNAME“, nullable=“false“) private String nachname; @Temporal(value=TemporalType.DATE) @Column(name=“PERS_GEBURTSTAG) private Date geburtstag; @Transient private short alter; ... } Mapping von Value Objects • Beispiel: Person ist Entität mit konstanter ID, Adresse ist einfaches Objekt ohne konstante ID Person id : long vorname : String nachname : String Adresse strasse : String plz : String stadt : String Mapping von Value Objects • Notation Value Object ist in Java nicht vorgesehen • Value Object ist einfaches Attribut von Entität • Value Object hat keinen eigenen Lebenszyklus • Lebenszyklus wird von Entität gesteuert Value Object - Mapping @Embeddable public class Adresse { @Column(name=“ADR_STRASSE“) private String strasse; @Column(name=“ADR_PLZ“) private String plz; @Column(name=“ADR_STADT“) private String stadt; ... getter und setter PERSON ID NUMBER <PK> VORNAME VARCHAR } NACHNAME VARCHAR @Entity public class Person { @Id private long id; private String vorname; private String nachname; ADR_STRASSE VARCHAR @Embedded private Adresse anschrift ... } ADR_PLZ VARCHAR ADR_STADT VARCHAR Value Object - Mapping @Entity public class Person { ... @Embedded @AttributeOverrides ({ @AttributeOverride(name=“strasse“, column=@Column(name=“PERS_ADR_STRASSE“)), @AttributeOverride(name=“plz“, column=@Column(name=“PERS_ADR_PLZ“)), @AttributeOverride(name=“stadt“, column=@Column(name=“PERS_ADR_STADT“)) }) private Adresse anschrift ... } @Embeddable public class Adresse { @Column(name=“ADR_STRASSE“) private String strasse; @Column(name=“ADR_PLZ“) private String plz; @Column(name=“ADR_STADT“) private String stadt; ... getter und setter } Mapping von Relationen • JPA unterstützt folgende Relationen • One-To-One • One-To-Many • Many-To-Many • Unterscheidung zwischen uni- und bidirektionalen Relationen • Unterstützung für List, Map,Value Objects Relationen Teil 1 • Unidirektionale Relation geht nur in eine Richtung und ist daher einfach und empfehlenswert • JPA verwaltet bi-direktionale Relationen nicht von selbst (siehe fortgeschrittene Mappings) • Best Practice: Aggregate Aggregate gruppieren Entitäten <Entität> <Entität > <Root Entität> Selbstauskunft <Root Entität> <Entität > Kreditantrag <Entität> <Entität> <Entität> TilgungsDetail Darlehen Kunde Adresse Many-To-One Person Konto id : long vorname : String nachname : String nummer : String blz : String PERSON ID NUMBER <PK> VORNAME VARCHAR NACHNAME VARCHAR KONTO_ID NUMBER <FK> KONTO ID NUMBER <PK> NUMMER NUMBER BLZ VARCHAR Many-To-One @Entity public class Person { @Id private long id; private String vorname; private String nachname; @ManyToOne private Konto konto ... } @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; ... } Many-To-One @Entity public class Person { @Id private long id; private String vorname; private String nachname; @ManyToOne @JoinColumn(name=“PERS_KTO_ID“, nullable=false) private Konto konto ... } @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; ... } One-To-Many Person Konto id : long vorname : String nachname : String nummer : String blz : String PERSON ID NUMBER <PK> VORNAME VARCHAR NACHNAME VARCHAR KONTO ID NUMBER <PK> NUMMER NUMBER BLZ VARCHAR PERSON_ID NUMBER <FK> One-To-Many @Entity public class Person { @Id private long id; private String vorname; private String nachname; Ungeordnete Collection @OneToMany private Set<Konto> konten = new HashSet<Konto>(); ... } @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; ... } One-To-Many @Entity public class Person { @Id private long id; private String vorname; private String nachname; @OneToMany @JoinColumn(name=“KTO_PERSON_ID“) private Set<Konto> konten = new HashSet<Konto> ... } @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; ... } One-To-Many mit List @Entity public class Person { @Id private long id; private String vorname; private String nachname; Spalte wird benötigt, die Reihenfolge persistiert @OneToMany @JoinColumn(name=“KTO_PERSON_ID“) @IndexColumn(name=“KTO_ORDER“) private List<Konto> konten = new ArrayList<Konto>(); ... } KONTO ID NUMBER <PK> NUMMER NUMBER BLZ VARCHAR KTO_PERSON_ID NUMBER <FK> KTO_ORDER NUMBER One-To-One Shared Primary Key Person Konto id : long vorname : String nachname : String nummer : String blz : String PERSON KONTO ID NUMBER <PK> ID NUMBER <PK><FK> VORNAME VARCHAR NUMMER NUMBER NACHNAME VARCHAR BLZ VARCHAR One-To-One Shared Primary Key @Entity public class Person { @Id private long id; ... @OneToOne @PrimaryKeyJoinColumn private Konto konto ... } @Entity public class Konto { @Id @GeneratedValue(generator = "myForeignGenerator") @org.hibernate.annotations.GenericGenerator( name = "myForeignGenerator", strategy = "foreign", parameters = @Parameter(name = "property", value = "owner") ) private long id; private Person owner; ... } One-To-One Unique Foreign Key Person Konto id : long vorname : String nachname : String nummer : String blz : String PERSON ID NUMBER <PK> VORNAME VARCHAR NACHNAME VARCHAR KONTO_ID NUMBER <FK><UNIQUE> KONTO ID NUMBER <PK> NUMMER NUMBER BLZ VARCHAR One-To-One Unique Foreign Key @Entity public class Person { @Id private long id; private String vorname; private String nachname; @OneToOne @JoinColumn(name=“KONTO_ID“, nullable=false) private Konto konto ... } @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; ... } Bidirektionale Relationen • JPA verwaltet bi-direktionale Relationen nicht von selbst • • Im Code muss Konsistenz garantiert werden! • • Sonst: doppeltes Fremdschlüssel Mapping Ein Ende ist invers, d.h. JPA ignoriert diesen Teil bei Verwaltung und Mapping verweist auf das nicht-inverse Ende Tip: Bidirektionale Mappings nach Möglichkeit vermeiden Many-To-One Bidirektional Person Konto id : long vorname : String nachname : String nummer : String blz : String PERSON ID NUMBER <PK> VORNAME VARCHAR NACHNAME VARCHAR KONTO_ID NUMBER <FK> KONTO ID NUMBER <PK> NUMMER NUMBER BLZ VARCHAR Many-To-One / One-To-Many Bidirektional @Entity public class Person { @Id private long id; private String vorname; private String nachname; @ManyToOne @JoinColumn(name=“PERS_KTO_ID“, nullable=false) private Konto konto ... } @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; @OneToMany(mappedBy=“konto“) private Set<Person> eigentuemer = new HashSet<Person>(); ... } Verwaltung von Bidirektionalen Relationen @Entity public class Konto { @Id private long id; private Integer nummer; private String blz; @OneToMany(mappedBy=“konto“) private Set<Person> eigentuemer = new HashSet<Person>(); public void addEigentuemer(Person p) { if (p == null) { throw new IllegalArgumentException(“p is null“); } if(p.getKonto() != null) { p.getKonto().getEigentuemer().remove(p); } getEigentuemer().add(p); p.setKonto(this); } ... } Many-To-Many Person id : long vorname : String nachname : String PERSON Team name : String PERS_TEAM TEAM ID NUMBER <PK> PERS_ID NUMBER <FK> ID NUMBER <PK> VORNAME VARCHAR TEAM_ID NUMBER <FK> NAME VARCHAR NACHNAME VARCHAR Many-To-Many @Entity public class Person { @Id private long id; ... @ManyToMany @JoinTable(name=“PERS_TEAM“, joinColumns = @JoinColumn(name=“PERS_ID“), inverseJoinColumns = @JoinColumn(name=“TEAM_ID“) ) private Set<Team> teams = new HashSet<Team>(); ... } @Entity public class Team { @Id private long id; private String name; @ManyToMany(mappedBy=“teams“) private Set<Person> personen = new HashSet<Person>(); ... } Lebenszyklus und transitive Persistenz • Entitäten haben einen eigenständigen Lebenszyklus • TransientObjectException bei folgendem Code ohne transitive Persistenz Konto k = new Konto(); Person p = new Person(); ... k.setPerson(p); session.save(k); Cascade konfiguriert transitive Persistenz • JPA unterstützt folgende Cascades • • • • • • ALL PERSIST MERGE REMOVE REFRESH DETACH Hibernate hat andere Semantik! Cascading @Entity public class Person { @Id private long id; private String vorname; private String nachname; @ManyToOne(cascade=CascadeType.ALL) private Konto konto ... } Sonderfall: Orphan Deletion • • Nur für Collections • Zwei Konfigurations Möglichkeiten: Löscht eine Entität wenn sie aus einer Collection entfernt wird • • @OneToMany / ManyToMany (orphanRemoval=true) @org.hibernate.annotations.Cascade( CascadeType.DELETE_ORPHAN) Agenda Zeit Thema 08:00 - 09:00 Einleitung und theoretische Grundlagen 09:00 - 09:30 JPA im Überblick 09:45 - 10:30 Mapping von Klassen 10:30 - 11:15 Arbeiten mit Hibernate 11:15 - 11:45 Queries Arbeiten mit JPA und Hibernate • Lebenszyklus von Entitäten • EntityManager / Session API Lebenszyklus von Entitäten new() Transient gc save() / persist() saveOrUpdate() merge() Session .get() .load() .getReference() .find() Query .list() .uniqueResult() .iterate() .scroll() .getResultList() .getSingleResult() Persistent evict() clear() close() Detached delete() remove() Removed update() saveOrUpdate() merge() gc gc Lebenszyklus von Entitäten Zustand Von Hibernate „Verwaltet“ Datenbank ID transient nein nein persistent ja ja detached nein ja Speichern • Ein Objekt wird „persistent“ • session.save() gibt die generierte ID sofort zurück (return Serializable) • em.persist() gibt keine ID zurück (return void) Löschen • em.remove() und session.delete() löschen eine Entity und setzen den Status „removed“ • remove() ist JPA API • Löschen grosser Datenbestände über HQL • hibernate.use_identifier_rollback = true setzt den ID Wert zurück (Status = „transient“) Laden • get() und load() laden persistente Entities • find() und getReference() sind JPA API existiert existiert nicht Zugriff get() Entity null Entity / null find() Entity null Entity / null load() Proxy Proxy Entity / Exception getReference() Proxy Proxy Entity / Exception Updates • Default: alle Spalten sind im Update enthalten • dynamic-update=“true“ bewirkt dynamische SQL Generierung • Eigene Dirty Checking Implementierung kann in Interceptor (findDirty())implementiert werden Reattaching • session.update() löst immer SQL update aus • select-before-update konfiguriert dirty check mittels SELECT (ineffizient, aber bei „on update“ Triggern interesssant) • lock() löst kein update aus, sondern führt nur Reattach durch Reattaching • merge() verhindert NonUniqueObjectException • Das übergebene Objekt ist nicht attached, sondern das zurückgegebene • Kopiert alle Properties und Collections • Transiente Objekte werden gespeichert Reattaching merge saveOrUpdate Objekt mit gleicher ID bereits in Session Objekt in Session wird überschreiben NonUniqueObjectException Objekt ist Transient INSERT INSERT Zustand übergebenes Objekt Transient / Detached Persistent Zustand Rückgabe Objekt Persistent (returns void) Bestandteil von JPA ja nein Agenda Zeit Thema 08:00 - 09:00 Einleitung und theoretische Grundlagen 09:00 - 09:30 JPA im Überblick 09:45 - 10:30 Mapping von Klassen 10:30 - 11:15 Arbeiten mit Hibernate 11:15 - 11:45 Queries Arbeiten mit Queries Query q = em.createQuery(“from Person p where p.nachname = :nachname “) .setParameter(“nachname“, “Müller“) .setFirstResult(20) .setMaxResults(10); List ergebnisse = q.getResultList(); Query q = em.createQuery(“from Person p where p.username = :login “) .setString(“login“, “meinlogin“) Person p = (Person)q.getSingleResult(); Criteria c = session.createCriteria(Person.class); c.setFirstResult(20); c.setMaxResults(19); List ergebnisse = q.list(); Named Queries @NamedQueries({ @NamedQuery( name=“findPersonByUsername“, query=“from Person p where p.username = :login“ ) }) @Entity public class Person { @Id @GeneratedValue @Column(name=“PERS_ID“) private long id; @Column(name=“PERS_USERNAME“) private String username; .... } Query q = em.createNamedQuery(“findPersonByUsername“) .setParameter(“login“, username); List ergebnisse = q.getResultList(); JPA-QL • Textuelle Abfrage Sprache • Ähnlich zu SQL • Geht gegen Objekte und nicht gegen Tabellen • Voll Objektorientiert • Starkte Verwandschaft zu HQL • Query Objekt wird über EntityManager bezogen Einfache JPQL Beispiele from Person from Person as p select p from Person as p Diese Abfragen machen alle das selbe select p from Person p SQL: SELECT * FROM PERSONEN JPQL ist polymorph from Zahlungsart from LastschriftEinzug Zahlungsart Lastschrift from Kreditkarte Kreditkarte from java.lang.Object from java.io.Serializable Einschränkungen mit where from Person where username = :login from Person p where p.adresse.strasse = :strasse from Person p where p.email like ‘%@senacor.%‘ and p.adresse.stadt = ‘Nürnberg‘ and p.lastLogin < :vorgestern Sortierung mit order by from Person p order by p.lastname asc from Person p order by p.lastname asc, p.firstname asc Selektion select p.lastname, p.firstname from Person p • Gibt Object Array zurück • Rückgabewerte sind keine Entitäten! Aggregation und Gruppierung select p.adresse.stadt, count(p) from Person p group by p.adresse.stadt select p.adresse.stadt, count(p) from Person p group by p.adresse.stadt having p.adresse.stadt like ‘Bad %‘ Inner und Outer Join PERS_ID PERS_NAME 1 ... KTO_id... KTO_NR Michael 1 1234 1 Michael 2 5678 2 Tina 3 645234 2 Tina 4 98798 2 Tina 5 76532 PERS_ID PERS_NAME KTO_id... KTO_NR 1 Michael 1 1234 1 Michael 2 5678 2 Tina 3 645234 2 Tina 4 98798 2 Tina 5 76532 3 Sepp null null 4 Melanie null null ... SELECT p.*, k.* FROM Person p INNER JOIN Konto k on p.ID = k.PERS_ID SELECT p.*, k.* FROM Person p LEFT OUTER JOIN Konto k on p.ID = k.PERS_ID Joins in JPQL • Implizite Navigation from Person p where p.adresse.stadt = ‘ Regensburg‘ • Inner Join from Person p join p.konten k where k.balance > 0 • Fetch Join from Person p left join fetch p.konten QueryDSL Einführung • Typ-sicher durch Annotation Processor • Unterstützung für SQL, Hibernate / JPA und MongoDB, JDO, Lucene • Volle Code Completion • Fluent-API Maven <project> <build> <plugins> ... <plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.0.6</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin> ... </plugins> </build> </project> Formulierung von Abfragen HibernateQuery query = new HibernateQuery(session); JPAQuery query = new JPAQuery(entityManager); Einschränkungen mit Restrictions QPerson person = QPerson.person; Person p = query.from(person) .where(person.username.eq(“ploed“)) .uniqueResult(person); QPerson person = QPerson.person; List<Person> p = query.from(person) .where(person.alter.between(“10“, “14“), person.lastname.like(“P%“)) .list(person); Sortierung mit orderBy QPerson person = QPerson.person; List<Person> p = query.from(person) .where(person.alter.between(“10“, “14“), person.lastname.like(“P%“)) .orderBy(person.lastname.asc()) .list(person); Gruppierung mit Projections QPerson person = QPerson.person; JPAQuery query = new JPAQuery(entityManager); List<Object[]> result = query.from(person) .groupBy(person.adresse.stadt) .listDistinct(person.adresse.stadt, person.adresse.stadt.count(); Joins QPerson person = QPerson.person; JPAQuery query = new JPAQuery(entityManager); List<Person> l = query.from(person) .innerJoin(person.fotos) .orderBy(person.username.asc()) .distinct().list(person);