@Entity
public class Person {
private Account account;
}
@Entity
public class Account {
// Possessore
private Person owner;
}
Cristian Lucchesi
gli oggetti possono contenere uno o più riferimenti ad altri oggetti
questi riferimenti prendono il nome di relazioni
le relazioni hanno diverse caratteristiche
direzionalità, una relazione potrà essere:
unidirezionale se un oggetto fa riferimento ad un altro ma non avviene il contrario
bidirezionale se il riferimento tra gli oggetti è reciproco
@Entity
public class Person {
private Account account;
}
@Entity
public class Account {
// Possessore
private Person owner;
}
La Person ha una relazione con Account e viceversa.
@Entity
public class Person {
//... Altre proprietà
private Province bornIn;
}
@Entity
public class Province {
private String name;
}
Person ha una relazione con Province ma non viceversa.
altra caratteristica delle relazioni è l’opzionalità:
non è detto che la relazione debba esistere necessariamente.
se non è obbligatoria, la relazione si dice opzionale.
la relazione può sussistere verso uno o più oggetti, in entrambe le direzioni
tre tipi di relazioni in funzione della molteplicità
uno a uno, uno a molti, molti a molti
JPA permette di gestire le relazioni e di risparmiarci di scrivere molto codice
@Entity @Table(name="persons")
public class Person {
@OneToOne
private Account account;
}
@Entity @Table(name="accounts")
public class Account {
// Non c'è il campo Person
}
si utilizza l’annotazione @OneToOne
l’annotazione è solo nell’entità che possiede il riferimento (owning side)
la tabella persons avrà una colonna con la chiave esterna che si riferisce alla tabella accounts.
@Entity @Table(name="persons")
public class Person {
@OneToOne
private Account account;
}
@Entity @Table(name="accounts")
public class Account {
@OneToOne(mappedBy="account")
private Person person;
}
l’annotazione @OneToOne è su entrambe le entità (owning side e inverse side)
l’attributo mappedBy specifica il nome della proprietà java utilizzata nella @OneToOne che è owning side
il Persistence Runtime JPA (es. Hibernate) effettua aggiornamenti della relazione sul db solo quando si cambia l’owning side
@Entity @Table(name="persons")
public class Person {
@OneToOne
private Account account;
}
@Entity @Table(name="accounts")
public class Account {
@OneToOne(mappedBy="account")
private Person person;
}
...
Person p = entityManager.getReference(Person.class, 1L);
account.setPerson(p); // Non viene modificato il db
Account a = entityManager.getReference(Account.class, 1L);
person.setAccount(a); // viene aggiornato il db
Person p = entityManager.find(Person.class, 1L);
select
personwith0_.id as id1_3_1_, personwith0_.account_id as account_4_3_1_,
personwith0_.name as name2_3_1_, personwith0_.surname as surname3_3_1_,
account1_.id as id1_2_0_, account1_.username as username2_2_0_
from
persons personwith0_
left outer join
accounts account1_ on personwith0_.account_id=account1_.id
where
personwith0_.account_id=1
L’EntityManager si occupa di caricare gli oggetti collegati con le opportune join.
@JoinColumn permette di personalizzare la relazione:
name: (String default il nome della variabile)
cambia il nome della colonna sul db
insertable: (boolean default true)
se false la colonna viene omessa nella insert
updatable: (boolean default true)
se false la colonna viene omessa nella update
@JoinColumn permette di personalizzare la relazione:
nullable (boolean default true)
utilizzata per determinare il tipo di JOIN eseguita da jpa (INNER vs LEFT)
columnDefinition (String default "")
frammento di SQL da utilizzare per la DDL della colonna
@Entity @Table(name="persons")
public class Person {
@OneToOne
@JoinColumn(name="account_id", updatable = false)
private Account account;
}
viene indicato il nome della colonna da utilizzare per le join e che il campo non è aggiornabile
un’entità può essere associata a più di un oggetto dello stesso tipo
la relazione viene detta di tipo uno a molti.
analogamente alle relazioni uno ad uno può essere uni o bidirezionale
In questo caso le annotazioni utilizzate sono:
javax.persistence.OneToMany
javax.persistence.ManyToOne
@Entity @Table(name = "persons")
public class Person {
@OneToMany(mappedBy = "owner")
private List<Car> cars = new ArrayList();
}
@Entity @Table(name = "cars")
public class Car {
@ManyToOne
private Person owner;
}
una persona ha molte macchine @OneToMany
molte macchine hanno lo stesso (uno) proprietario @ManyToOne
@Entity @Table(name = "orders")
public class Order {
@Size(min = 1)
@OneToMany(mappedBy = "order")
private List<LineItem> lineItems = new ArrayList<>();
}
@Entity @Table(name = "order_line_items")
public class LineItem {
@NotNull
@ManyToOne(optional = false)
private Order order;
}
un ordine ha molte righe di dettaglio (almeno una)
più righe di dettaglio appartengono allo stesso ordine
una riga di dettaglio deve appartenere ad un ordine
Order order = entityManager.find(Order.class, 1L);
System.out.println("Ordine " + order);
for (LineItem lineItem : order.getLineItems()) {
System.out.println(
String.format("Riga %s. %s: %s euro",
lineItem.getId(), lineItem.getDescription(),lineItem.getAmount()));
}
select order0_.id as id1_4_0_ from orders order0_ where order0_.id=1
select lineitems0_.order_id as order_id4_4_0_, lineitems0_.id as id1_1_0_,
lineitems0_.id as id1_1_1_, lineitems0_.amount as amount2_1_1_,
lineitems0_.description as descript3_1_1_,
lineitems0_.order_id as order_id4_1_1_
from LineItem lineitems0_ where lineitems0_.order_id=1
non c’è bisogno di preoccuparsi delle join… ci pensa JPA
il possessore della relazione owning side è sempre con @ManyToMany
l'inverse side è sempre con @OneToMany che è quella con l’attributo mappedBy
il JPA Engine tiene traccia dei cambiamenti solo sull’owning side
(come avviene per le relazioni @OneToOne)
Order order = entityManager.find(Order.class, 1L);
order.getLineItems().add(lineItem); // Non viene modificato il db
LineItem li = entityManager.find(LineItem.class, 1L);
li.setOrder(order); // viene aggiornato il db
molte entità possono essere associate a molte altre entità dello stesso tipo
la relazione viene detta di tipo molti a molti
analogamente alle relazioni one-to-one e e uno-to-many può essere uni o bidirezionale
l’annotazione utilizzata è javax.persistence.ManyToMany
nelle many-to-many l'owning_side può essere a scelta su uno qualunque dei lati della relazione
@Entity @Table(name = "persons")
public class Person {
@ManyToMany(mappedBy = "owners")
private List<House> houses = new ArrayList();
}
@Entity @Table(name = "houses")
public class House {
@ManyToMany
private List<Person> owner = new ArrayList();
}
una persona può avere molte case
una casa può avere più proprietari
specifica la tabella di mapping dell’associazione.
è applicata al owning side dell’associazione
di solito è utilizzata nelle many-to-many e one-to-many unidirezionali
se non è presente il nome della join table è la concatenazione dei nomi delle tabelle in relazione tra di loro, separati da _ (underscore) e con l'owning size prima
@ManyToMany
@JoinTable(name = "persons_houses")
private List<Person> owner = new ArrayList();
@ManyToMany
@JoinTable(name = "persons_houses",
joinColumns= @JoinColumn(name="house_id"), (1)
inverseJoinColumns= @JoinColumn(name="person_id") (2)
)
private List<PersonWithAccount> owners = Lists.newArrayList();
1 | colonna del db relativa alla owning_side dlela relazione |
2 | colonna del db relativa alla inverse_side della relazione |
//...
entityManager.getTransaction().begin();
Order order = new Order();
LineItem lineItem = new LineItem();
lineItem.setOrder(order);
entityManager.persist(lineItem); (1)
entityManager.persist(order);
entityManager.getTransaction().commit();
1 | In questo punto l’order non è ancora salvato (non ha un id associabile in order_line_items) |
WARN: HHH000437: Attempting to save one or more entities that have a non-nullable association with an unsaved transient entity. The unsaved transient entity must be saved in an operation prior to saving these dependent entities.
quando un’operazione è effettuata su un’entity si può scatenare la stessa operazione sulle entità collegate
Cascade | Descrizione |
---|---|
PERSIST | quando l’oggetto è persisted, anche l’entità collegata è persisted |
DETACH | quando l’oggetto è detached, anche l’entità collegata è detached |
MERGE | quando l’oggetto è merged, anche l’entità collegata è merged |
Cascade | Descrizione |
---|---|
REFRESH | quando viene effettuta la refresh dell’oggetto anche l’entità collegata viene ricaricata |
REMOVE | quando viene rimosso l’oggetto anche l’entità collegata viene rimosa |
ALL | tutte le operazioni sono applicate agli oggetti correlati. Equivalente a cascade={DETACH, MERGE, PERSIST, REFRESH, REMOVE} |
Valido per tutte le relazioni @OneToOne, @OneToMany, @ManyToOne, ..
import javax.persistence.CascadeType;
//...
public class Order {
//...
@OneToMany(mappedBy = "order", cascade = { CascadeType.PERSIST, CascadeType.REMOVE)
private List<LineItem> lineItems = new ArrayList<>();
public class Order {
//..
@OneToMany(mappedBy = "order", cascade = CascadeType.PERSIST)
private List<LineItem> lineItems = new ArrayList<>();
}
//...
// utilizzo del Cascade
Order order = new Order();
LineItem lineItem = new LineItem();
lineItem.setOrder(order);
order.getLineItems().add(lineItem);
entityManager.persist(order);
L’oggetto LineItem viene salvato per effetto del
cascade = CascadeType.PERSIST
public class Order {
//..
@OneToMany(mappedBy = "order", cascade = { CascadeType.PERSIST, CascadeType.REMOVE )
private List<LineItem> lineItems = new ArrayList<>();
}
//...
// utilizzo del Cascade
Order order = entityManager.find(Order.class, 1L);
entityManager.remove(order);
Hibernate:
select * from orders where id = 1
Hibernate:
select * from order_line_items where order_id = 1
Hibernate:
delete from order_line_items where id = 1
Hibernate:
delete from orders where id = 1
nelle relazioni one-to-one e one-to-many quando un oggetto viene rimosso da una relazione è probabile che debba essere rimosso
questi oggetti sono considerati orphans e possono essere rimossi automaticamente utilizzando l’attributo orphanRemoval
per esempio se un Order ha molti LineItem ed un LineItem viene rimosso dall’associazione, la LineItem è considerata un orphan
se orphanRemoval è true, la LineItem sarà cancellata quando è rimossa l’associazione dall’ordine
l’attributo orphanRemoval in @OneToMany e @OneToOne accettata true/false ed il default è false
public class Order {
@OneToMany(mappedBy = "order", orphanRemoval = true)
public List<LineItems> getLineItems() { ... }
}
// Esempio di utilizzo
Order order = entityManager.getReference(Order.class, 11L)
order.getLineItems().clear(); (1)
1 | La lista viene svuotata, gli oggetti LineItem diventano orphan e vengono cancellati |
JPA fornisce due metodi per interrogare le Entity
Java Persistence query language (JPQL)
linguaggio semplice basasto su stringhe e simile a SQL
Criteria API
un API Java utlizzata per creare query typesafe
JPQL definisce le query per interrogare le Entity ed il loro stato di persistenza
le query sono portabili ed indipendenti dal data store (db)
il linguaggio utilizza le Entity e le loro relazioni come modello di astrazione del database
le operazioni e le espressioni sono basate sul modello astratto
lo scope delle query comprende le Entity gestite dalla stessa Persistent Unit
SELECT a FROM Article a ORDER BY a.date ASC
EntityManager::createQuery permette di creare query dinamiche definite direttamente nella business logic dell’applicazione
@PersistenceContext
EntityManager entityManager;
public List<Person> list(int maxResults) {
return entityManager
.createQuery(
"SELECT p FROM Person p", Person.class)
.setMaxResults(maxResults)
.getResultList();
}
Si possono definire query statiche con nome
Raccomandate perché le query usufruiscono del sistema di cache
@NamedQuery(name="ordered", query="SELECT e FROM BlogEntry e ORDER BY e.date ASC")
public class BlogEntry {
...
public List<BlogEntry> allEntriesOrdered() {
return entityManager.createNamedQuery("ordered", BlogEntry.class).getResultList();
}
sono parametri della query prefissati con due punti (:).
sono sostituiti nella query eseguita tramite il metodo
javax.persistence.Query.setParameter(String name, Object value)
@PersistenceContext
EntityManager entityManager;
public List<Person> byName(String firstname) {
return entityManager
.createQuery(
"SELECT p FROM Person p WHERE p.firstname = :firstname",
Person.class)
.setParameter("firstname", firstname)
.getResultList();
}
parametri individuati con il punto interrogativo (?) seguiti da un numero
sono sostituiti nella query eseguita tramite il metodo
javax.persistence.Query.Query.setParameter(integer position, Object value)
@PersistenceContext
EntityManager entityManager;
public List<Person> byName(String firstname) {
return entityManager
.createQuery(
"SELECT p FROM Person p WHERE p.firstname = ?1",
Person.class)
.setParameter(1, firstname)
.getResultList();
}
una select query ha sei elementi
SELECT, FROM, WHERE, GROUP BY, HAVING e ORDER BY
le clausole SELECT e FROM sono obbligatorie
WHERE, GROUP BY, HAVING, and ORDER sono opzionali
QL_statement ::= select_clause from_clause
[where_clause][groupby_clause][having_clause][orderby_clause]
SELECT definisce il tipo di oggetto o valore restituto
FROM definisce il contesto della query dichiarando uno o più variabili referenziabili nella parte SELECT e WHERE, può contenere
il nome astratto di un’entità
una collezione riferita in un’entity
un elemento single-valued di una relazione
WHERE è un’espressione condizionale che restringe gli oggetti restituiti dalla query
GROUP BY ragrruppa i risutalti della query in accordo ad un insieme di proprietà
HAVING è utilizzata con la GROUP BY per restringere i risultati in accordo a delle espressioni condizionali
definisce l’ordinamento dei risultati
SELECT p FROM Player p
tutti i giocatori
la clausola FROM dichiara un variabile identificativa chiamata p, omettendo la keyword opzionale AS
è l’abstract schema name dell’entity Player @Entity class Player {…}
SELECT DISTINCT p FROM Player p
WHERE p.position = :position AND p.name = :name
i giocatori con una determinata posizione e nome
position e name sono campi persistenti dell’entità Player.
nella WHERE si compare il valore dei campi nel db con quelli passati per parametro.
*DISTINCT" elimina eventuali duplicati
in JPQL un’espressione può attraversare o navigare le entità correlate tra di loro
questa funzionalità è la differenza principale tra JPQL e SQL
la nagivazione tra le entità correlate è molto semplificata rispetto all’SQL perché sfrutta la descrizione delle entity e delle sue relazioni
SELECT DISTINCT p
FROM Player p JOIN p.teams t
tutti i giocatori che appartengono almeno ad una squadra
p rappresenta l’entità Player, t l’entità Team correlata
p.teams naviga da un Player ai suoi Team correlati.
il punto (.) nell’espressione p.teams è l’operatore di navigazione
SELECT DISTINCT p
FROM Player p JOIN p.teams t
è equivalente a
SELECT DISTINCT p
FROM Player p
WHERE p.team IS NOT EMPTY
ed a
SELECT DISTINCT p
FROM Player p, IN (p.teams) t
SELECT DISTINCT p
FROM Player p JOIN p.teams
WHERE t.city = :city
tutti i giocatori che appartengono ad una squadra di una determinata città
ATTENZIONE: nelle WHERE non si possono navigare le collezioni, non si può scrivere WHERE t.teams.city = :city (illegal expression)
SELECT DISTINCT p
FROM Player p JOIN p.teams t
WHERE t.league.sport = :sport
dato che league non è una collezione ma relazione league può essere seguita per arrivare al campo sport collegato
SELECT p FROM Player p
WHERE p.name LIKE 'Mich%'
SELECT t FROM Team t
WHERE t.league IS NULL
SELECT p FROM Player p
WHERE p.teams IS EMPTY
SELECT DISTINCT p FROM Player p
WHERE p.salary BETWEEN :lowerSalary AND :higherSalary
SELECT DISTINCT p1
FROM Player p1, Player p2
WHERE p1.salary > p2.salary AND p2.name = :name
public List<Person> havingHouse(House house) {
return entityManager.createQuery(
"SELECT p FROM Person JOIN p.houses h"
+ " WHERE h = :house", Person.class)
.setParameter("house", house)
.getResultList();
}
Compara gli oggetti di tipo House relazionati a Person con l’oggetto House passato per parametro
Due entity dello stesso abstract schema type sono considerate uguali se e solo se le loro chiavi primarie hanno lo stesso valore
SELECT o
FROM Customer c JOIN c.orders o JOIN c.address a
WHERE a.state = 'CA'
ORDER BY o.quantity, o.totalcost
SELECT c.status, AVG(o.totalPrice)
FROM CustomerOrder o JOIN o.customer c
GROUP BY c.status HAVING c.status IN (1, 2, 3)
Stringhe: CONCAT, LENGTH, SUBSTRING, TRIM, LOWER, UPPER
Aritmetiche: ABS, MOD, SQRT, SIZE
Date/Time: CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP
Subquery: simili alle subquery SQL
Case Expression: CASE TYPE(p) WHEN…
….
https://docs.oracle.com/javaee/7/tutorial/persistence-querylanguage005.htm
Gli statemente Update e delete fornisco operazioni su un insieme di entity.
update_statement :: = update_clause [where_clause]
delete_statement :: = delete_clause [where_clause]
operano in accordo alle condizioni impostate nella WHERE
la clausola WHERE segue le stesse regole valide per la SELECT
UPDATE Player p
SET p.status = 'inactive'
WHERE p.lastPlayed < :inactiveThresholdDate
Imposta a inactive tutti i giocatori che non giocano da molto tempo
DELETE FROM Player p
WHERE p.status = 'inactive'
AND p.teams IS EMPTY
Cancella tutti i giocatori inattivi che non appartengono a nessun Team
sono una alternativa in JPA 2.x per a JPQL
le query sono dinamiche
si compongono a partire da oggetti Java standard
le query sono create in modo typesafe
il CriteriaBuidler
si ottiene utilizzando il metodo
EntityManager::getCriteriaBuilder
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<BlogEntry> criteriaQuery = builder.createQuery(BlogEntry.class);
Root<BlogEntry> be = criteriaQuery.from(BlogEntry.class);
ParameterExpression<String> title = builder.parameter(String.Class);
criteriaQuery.select(be).where(builder.equals(be.get("title"), title);
TypedQuery<BlogEntry> typedQuery = entityManager.createQuery(criteriaQuery);
typedQuery.setParameter(parameter, "il mio titolo");
typedQuery.getSingleResult(); // recupera il blogentry con "il mio titolo"
Criteria API e JPQL sono strettamente legate e con operatori analoghi nelle loro query.
gli sviluppatori familiari con la sintassi JPQL troverranno l’equivalente operazioni object-level nella Criteria API.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
TypedQuery<Pet> q = em.createQuery(cq);
List<Pet> allPets = q.getResultList();
è equivalente a
List<Pet> allPets =
entityManager.createQuery("SELECT p FROM Pet p", Pet.class).getResultList();
JPA2 prevede l’utilizzo di un Metamodel per ogni entity per garantire il typesafe delle query.
un Metamodel è una classe i cui attributi corrispondono ai campi persistenti ed alle relazioni dell’entity.
di solito la classe del Metamodel ha lo stesso il nome con un underscore (_) finale
@Entity
public class Pet {
@Id
protected Long id;
protected String name;
protected String color;
@ManyToOne
protected Set<Person> owners;
...
}
@Static Metamodel(Pet.class)
public class Pet_ {
public static volatile SingularAttribute<Pet, Long> id;
public static volatile SingularAttribute<Pet, String> name;
public static volatile SingularAttribute<Pet, String> color;
public static volatile SetAttribute<Pet, Person> owners;
}
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name), "Fido"));
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).isNull());
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(pet.get(Pet_.color).in("brown", "black"));
<plugin>
<groupId>org.bsc.maven</groupId>
<artifactId>maven-processor-plugin</artifactId>
<executions>
<execution>
<id>process</id>
<goals>
<goal>process</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<!-- source output directory -->
<outputDirectory>target/metamodel</outputDirectory>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>4.3.11.Final</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>target/metamodel</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
public List<Pet> byOwnerName(String ownerName) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);
cq.select(pet).where(cb.equal(owner.get(Owner_.name), ownerName));
return entityManager.createQuery(criteriaQuery.where()).getResultList();
}
I predicati sono componibili con: and, or, not
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.where(cb.equal(pet.get(Pet_.name), "Fido")
.and(cb.equal(pet.get(Pet_.color), "brown")));
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.select(pet);
cq.orderBy(cb.desc(pet.get(Pet_.birthday)));
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = pet.join(Pet_.owners).join(Owner_.address);
cq.select(pet);
cq.orderBy(cb.asc(address.get(Address_.postalCode)));
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);
cq.select(pet);
cq.orderBy(cb.asc(owner.get(Owner_.lastName)), owner.get(Owner_.firstName)));
CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);
Root<Pet> pet = cq.from(Pet.class);
cq.groupBy(pet.get(Pet_.color));
cq.having(cb.in(pet.get(Pet_.color)).value("brown").value("blonde"));
Restituisce la lista di Pet il cui Owner si chiama come il parametro ownerName passato
Vantaggi JPQL
le query JPQL sono poche righe, coincise e più leggibili
gli sviluppatori già familiari con SQL le imparano velocemente
le JPQL NamedQuery possono essere definite e riutilizzate facilmente
Svantaggi JPQL
JPQL query non sono typesafe
richiedono un cast quando si leggono i risultati dall’entityManager
sono sottoposte a problemi di type-casting non intercettabili a compile time
in caso di refactoring delle Entity non c’è nessun controllo sulle stringhe JPQL utilizzate
Vantaggi Criteria Query
permettono di definire le query a livello applicativo con oggetti riutlizzabili
hanno migliori performance perché non necessitano il parsing della query (String) ogni volta
sono typesafe e non richiedono type casting
sono un API Java e non richiedono di imparare un nuovo linguaggio
in caso di rifattorizzazione le query sono parzialmente rifattorizzate automaticamente
Svantaggi di Criteria Query
sono più prolisse delle query JPQL
richiedono la creazione di molti oggetti ed eseguire diversi metodi per sottomettere le query
Esistono varie implementazioni JPA che tipicamente sono utilizzati all’interno di application server:
Hibernate, in JBoss/RedHat
EclipseLink, Oracle
OpenJPA …
org.springframework.data.jpa.repository.Query
public interface ArticleDao extends PagingAndSortingRepository<Article, Integer> {
@Query("SELECT a FROM Article a WHERE a.author.surname = ?1")
List<Article> findByAuthorSurname(String surname);
}
hibernate.ddl-auto =update VS database evolution
Java Platform, Enterprise Edition: The Java EE Tutorial → Persistence