JPA provides developers with an ORM facility for managing relational data. JPA consists of 4 areas:
The Java Persistence API
The query language
The Criteria API
ORM metadata
JPA est une spécification d’ORM
Implémentations disponibles :
Hibernate
EclipseLink
OpenJPA
Le périmètre du standard JPA est très vaste.
Ce cours est uniquement une introduction à une partie de ce standard.
Java DataBase Connectivity
API d’accès aux bases de données SQL
Code indépendant du type de la base de données
Nécessite l’écriture de code SQL
Enregistrement du driver
Connection à la base de données
Exécution du code SQL
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost/test", "user", "password");
PreparedStatement stmt = connection.prepareStatement(
"SELECT * FROM PERSON WHERE ID = ?");
stmt.setString(1, 1234L);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
String name = rs.getString("NAME");
Date birthDate = rs.getDate("BIRTH_DATE");
Person person = new Person(name, birthDate);
}
if (conn != null) {
conn.close();
}
Bas niveau
Nécessite beaucoup de code répétitif
Gestion des exceptions
Libération des ressources
Gestion manuelle des verrous
Gestionnaire de pilotes
Source de données
Inadapté pour un nombre élevé de connections
Dépassement du nombre de connections côté DB
Refus de nouvelles connections après une certaine limite
Temps d’ouverture d’une nouvelle connection
Pour un nombre élevé de connections utilisées pour une courte durée
Pool de connections initialisées au démarrage du serveur d’applications
Récupérer une connection de la source de données
Utiliser la connection pour exécuter du code SQL
Remettre la connection à disposition du pool
Appel de la méthode close()
Paramètres : URL de la base, nombre initial de connections, nombre maximal, etc.
Dépendant du serveur d’applications
Mis à disposition dans l’arbre de ressources JNDI
Code explicite pour récupérer la ressource JNDI
Injection via @Resource
Configuration pour JPA
public class SomeRepository {
@Resource(name="java:comp/env.jdbc/mydb")
private DataSource dataSource;
...
}
An entity is a lightweight persistence domain object. An entity class represents a table in a relational database, and each entity instance corresponds to a row in that table.
Etre annotée avec @javax.persistence.Entity
Respecter le contrat d’un JavaBean
Constructeur public
sans argument
Accesseurs publics
Avoir un attribut/getter d’id
Tous les champs sont persistés
Sauf ceux annotés avec @javax.persistence.Transient
Types primitifs
Chaînes de caractères
Types Serializable
Enumérations
Entités
Collections des types ci-dessus
Un attribut spécifique annoté @Id
référence la clé primaire de l’entité
Annotation @javax.persistence.GeneratedValue
sur l’attribut annoté par @Id
GenerationType.IDENTITY
GenerationType.SEQUENCE
@SequenceGenerator
GenerationType.TABLE
@TableGenerator
@Entity
public class Book {
@Id @GeneratedValue(strategy=IDENTITY)
private long id;
private String isbn;
private String title;
private String description;
private Date publicationDate;
private Author author;
// Getters and setters
}
De manière consistante
Soit sur les attributs
Soit sur les getters
Par défaut :
nom de la table
nom de la colonne
@javax.persistence.Table(name = "PERSON")
@javax.persistence.Column(name = "FIRST_NAME")
Les annotations précédentes permettent également :
Spécification d’un schéma
Définition des contraintes (DDL)
@Entity
@Table(name="T_BOOK")
public class Book {
@Id @GeneratedValue(strategy=IDENTITY)
private long id;
private String isbn;
private String title;
private String description;
@Column(name="PUBLICATION_DATE")
private Date publicationDate;
private Author author;
// Getters and setters
}
The EntityManager API creates and removes persistent entity instances, finds entities by the entity’s primary key, and allows queries to be run on entities.
Gestion du cycle de vie
Recherche d’une entité par sa clé primaire
Synchronisation avec la base de données
Exécution de requêtes avec l’API Criteria
Exécution de requêtes JPQL (& SQL)
etc.
Initialisé par le serveur d’apps
Fichier persistence.xml
Situé dans WEB-INF/classes/META-INF
<persistence>
<persistence-unit name="PeopleManagement">
<!-- Non-default data source
<jta-data-source>jdbc/people</jta-data-source>
-->
<class>ch.frankel.Person</class>
<class>ch.frankel.Teacher</class>
<class>ch.frankel.Student</class>
</persistence-unit>
</persistence>
@Stateless
public class Service {
@PersistenceContext
private EntityManager em;
}
Uniquement pour des objets dont le cycle de vie est gérée par le serveur d’applications
A transaction symbolizes a unit of work performed within a DBMS against a database, and treated in a coherent and reliable way independent of other transactions.
Transaction nécessaire pour les opérations d’écriture
transaction.begin();
em.persist(new Person());
transaction.commit();
@Stateless
public class Service {
@Resource
private UserTransaction userTransaction;
}
@Stateless
public class Service {
@Transactional
public void doInTransaction() {
// Everything here will run in a transaction
}
}
Commit à la fin de la méthode pour une exécution normale
En cas d’exception runtime
, rollback
@Transactional(rollbackFor=MyException.class)
Permet les transactions distribuées
2-Phases Commit
Nécessite un pilote JDBC XA
An EJB component is a body of code that has fields and methods to implement modules of business logic.
@Stateless
public class Service {
// Business logic
}
@Stateless
public class AccountService {
@Transactional
public void transfer(Account source, Account target, Money amount) {
// Business logic
}
}
Permet les transactions distribuées
2-Phases Commit
Nécessite un pilote JDBC XA
The query language allows you to write portable queries that work regardless of the underlying data store. It uses the abstract persistence schemas of entities, including their relationships, for its data model and defines operators and expressions based on this data model.
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM PERSON " +
"WHERE FIRST_NAME LIKE ? AND LAST_NAME LIKE ?");
stmt.setString(1, firstName);
stmt.setString(2, lastName);
ResultSet rs = stmt.executeQuery();
List persons =
em.createQuery(
"SELECT p FROM Person p " +
"WHERE p.firstName LIKE :firstName "
"AND p.lastName LIKE :lastName")
.setParameter("firstName", firstName)
.setParameter("lastName", lastName)
.getResultList();
The Criteria API is used to define queries for entities and their persistent state by creating query-defining objects. Criteria queries are written using Java programming language APIs, are typesafe, and are portable
Typage faible
Paramètres optionnels :
Concaténation de chaînes
Person person = em.find(Person.class, 1234L);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
cq.from(Person.class);
TypedQuery<Person> q = em.createQuery(cq);
List<Person> allPersons = q.getResultList();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> person = cq.from(Person.class);
cq.where(cb.like(person.get("firstName"), firstName),
cb.like(person.get("lastName"), lastName));
TypedQuery<Person> q = em.createQuery(cq);
List<Person> persons = q.getResultList();
For each entity class in a particular package, a metamodel class is created with a trailing underscore and with attributes that correspond to the persistent fields or properties of the entity class.
Soit :
Manuelle
Via l’implémentation spécifique
Devrait être synchronisée avec chaque changement de code
Via un outil de build (Maven)
javac -classpath $TOMEE_HOME/lib/openjpa-3.1.0.jar:\
$TOMEE_HOME/lib/javaee-api-8.0-3.jar \
-Aopenjpa.metamodel=true \
-Aopenjpa.source=8 ch/hesge/javaee/Book.java
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> cq = cb.createQuery(Person.class);
Root<Person> person = cq.from(Person.class);
cq.where(cb.like(person.get(Person_.firstName), firstName),
cb.like(person.get(Person_.lastName), lastName));
TypedQuery<Person> q = em.createQuery(cq);
List<Person> persons = q.getResultList();