Web Application Engineering

Object Relational Mapping

Cristian Lucchesi

Istituto di Informatica e Telematica - CNR

ORM

Wikipedia
[OR Mapping - Object-Relational mapping is the process of the transformation of the data between the class objects and databases. Applications can depend on an OR-M like tool that greatly simplifies this work instead of manually coding the transformation process.]

Introduzione a Java Persistence API

Persistence Entities

Pojo

public class BlogEntry {
  private String id;
  private String title;
  private String excerpt;
  private String body;
  private Date date;
  ...
	
  // Getters e Setters vanno qui
}
  
la classe corrisponde ad un oggetto BlogEntry con attributi: it, titolo, excerpt, ...

Entity

@Entity
public class BlogEntry {
  private String id;
  private String title;
  private String excerpt;
  private String body;
  private Date date;
  ...
}
  

@Table

@Entity
@Table(name="blog_entries")
public class BlogEntry {
  ...
}
  

@Column

  @Column(name="id", nullable=false)
  private String id;
  
  @Column(name="title", nullable=false, length=70)
  private String title;
  
  @Column(name="excerpt", nullable=false, length=200)
  private String excerpt;
  ...
  
altri attributi impostabili sulla colonna sono: unique, insertable, updatable, precision, scale

@Id

@Id
private String id;
  

Auto-generation of Primary Keys

A primary key for an entity which is usually annotated with @Id annotation can be given a value manually or we can depend on the persistence provider for the same.
For this we have to use the @GeneratedValue annotation.
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private String imeiNo;
  
Since the imeiNo is going to be the primary key for the mobile object, we have decorated the field with @GeneratedValue annotation, which delegates the burden of creating values from developers to the persistence engine. Also, there are 4 different methods (or strategies) for primary key generation, which are AUTO, IDENTITY, SEQUENCE and TABLE. The simplest one is the automatic primary key generation strategy which is represented as GenerationType.AUTO.

BlogEntry Entity

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class BlogEntry {

  @Id 
  @Column(name="id", nullable=false)
  private String id;
  
  @Column(name="title", nullable=false, length=70)
  private String title;
  
  @Column(name="excerpt", nullable=false, length=200)
  private String excerpt;
  
  @Column(name="body", nullable=false, length=1400)
  private String body;
  
  @Column(name="date", nullable=false)
  private Date date;
  
  //Getter, setter e altri metodi di seguito...
  

EntityManager

EntityManager - J2SE

EntityManagerFactory entityManagerFactory =  
    Persistence.createEntityManagerFactory("PersistentUnitName");

EntityManager eManager = entityManagerFactory.createEntityManager();
    
L'EntityManagerFactory può essere configurato con l'aiuto della Persistent Unit di cui parleremo più avanti.

EntityManager - J2EE

import javax.persistence.EntityManager;

...

  @PersistenceContext
  private EntityManager entityManager;
  

EntityManager.persist()

...
BlogEntry blogEntry = new BlogEntry();
blogEntry.setId("waeSeminar")
blogEntry.setTitle("Seminario di Web application Engineering")
// Update delle varie proprietà
entityManager.persist(blogEntry);
...
  

EntityManager.find()

	BlogEntry entry = entityManager.find(BlogEntry.class, "waeSeminar");
	If (entry != null){ // entry object may or may not be null.
	    // Process the object.
	}
l'oggetto restituito dall'EntityManager diventa così direttamente utilizzabile

EntityManager.getReference()

BlogEntry entry = entityManager.getReference(BlogEntry.class, "waeSeminar");
// entry object may not contain the actual state values for title, excerpt,
// body and date, the states may be loaded during the first access.

String title = entry.getTitle();
// The persistence engine may fetch the title value for the entry here
// at this particular point of time.
...
  

EntityManager.remove()

BlogEntry entry = entityManager.getReference(BlogEntry.class, "waeSeminar");
entityManager.remove(entry)
  

Entity e Transazioni

All entities have the property of transactionability and their CRUD operations will take place within a transactional context. Transactions can be broadly classified into two types based on who actually owns (or manages) the transaction. They are JTA and Resource-local transaction. In the case of a J2EE Application, the default transaction type is JTA (Java Transaction API), unless explicitly specified. A method can be simply annotated with @RequiresNew (or @Requires) in which case a new transaction will always started by the container and the transaction completes as soon as the method ends. Whereas in a J2SE application, the transaction-type defaults to Resource-local, which means that the developer (or the application code) has to explicitly start and end a transaction. It means that the user has to explicitly start a transaction with the help of EntityManager object, and then have to commit it, if everything goes normal, else have to roll-back the transaction if some error occurs.

EntityManager.merge()

The EntityManager.merge(entityObject) method will make a detached entity to get associated with the current persistence context of the EntityManager.
// Transaction has begin.
...
BlogEntry entry = entityManager.find(BlogEntry.class, "waeSeminar");
...
// Transaction ends.
entry.setTitle("Web Application Engineering - Tutorial") // Updating the entry object.
entityManager.merge(entry);

  
In the above piece of code, a mobile entity object is located with the EntityManager.find() method. This entity is now in a managed state. Assume that the transaction ends after some point of time and also the persistence context is a transaction-scoped persistence context. As soon as the transaction completes, the persistence context will go off, (since the persistence context is a transaction-scoped persistence context). So the entity object becomes detached from the persistence context. After this any modifications that are made to the mobile object won't be knowledgeable to the EntityManager as the mobile object has already been detached from it. Now, calling the merge() method, will make the mobile object becomes managed, and all the recent changes that are made to it will become visible to the current persistence context of the EntityManager.

Flushing and Refreshing

BlogEntry entry = ....
entry.setExcerpt("Seminario ..."); // Aggiorna lo stato dello oggetto
....
entityManager.flush(); // Sincronizza il database con i valori
                       // presi dall'oggetto "entry"
  
BlogEntry entry = ...
entry.setBody("Il web..."); // Lo stato dell'oggetto "entry" è aggiornato
...
entityManager.refresh();  // l'entity object viene aggiornato
                          // con i valori presi dal db
                          // Tutti i cambiamenti fatti sono persi
  

Query API

Static Query

@NamedQuery(name="BlogEntry.findAll" query="SELECT be FROM BlogEntry be")
@Entity
class BlogEntry {
}
Query findAllQuery = entityManager.createNamedQuery("BlogEntry.findAll");
// Execute the query.
  

Note that the code is referring the query by its name ("BlogEntry.findAll") by calling the createNamedQuery() method. Also, since these named queries are statically defined, during deployment time itself, the persistent engine may parse and translate the JPQL into native SQL, cache them, thereby yielding better performance.

Multiple named queries can be logically defined with the help of @NamedQueries, like this,
@NamedQueries( {
  @NamedQuery(name="BlogEntry.selectAllQuery" query="SELECT be FROM BlogEntry"),
  @NamedQuery(name="BlogEntry.deleteAllQuery" query="DELETE FROM BlogEntry")
} )
    
[The @NamedQueries is very similar like @NamedQuery, but can accept multiple @NamedQuery objects, The definition of the @NamedQueries looks like this,

Dynamic Queries

String queryString = ... // Obtained during run-time.
Query dynaQuery = entityManager.createQuery(queryString);
  

Single result

Query query = entityManager.createQuery(
  "SELECT be FROM BlogEntry be " + 
  "WHERE be.title = "Web Application Engineering");
BlogEntry entry = query.getSingleResult();
  

Multiple Results

Query query = entityManager.createQuery("SELECT be FROM BlogEntry");
List<BlogEntry> entries =
(List<BlogEntry>query.getResultList();

Lavorare con i parametri

String selectQuery =
  "SELECT be FROM BlogEntry WHERE be.title = ?1 and be.excerpt = ?2;
Query selectQuery = entityManager.createQuery(selectQuery);
...
selectQuery.setParameter(1, title);
selectQuery.setParameter(2, excerpt);
  
durante l'esecuzione della query ?1 e ?2 saranno rimpiazzati dai valori specificati dalle stringhe title e exceprt

Parametri con nome

String query = "SELECT be FROM BlogEntry WHERE be.title = :title " +
                " AND be.date = :fromDate"
Query selectQuery = entityManager.createQuery(query);

selectQuery.setParameter("title", title);
selectQuery.setParameter("fromDate", myDate);
  

Named parameter e @NamedQuery

@NamedQuery(name ="BlogEntry.findByTitle" 
  query = "SELECT be FROM BlogEntry WHERE be.title = :title
)
...

//E successivamente nel codice...
Query namedQuery = entityManager.createNamedQuery("BlogEntry.findByTitle");
namedQuery.setParameter("title", titleValue);
  

Paginare i risultati

private static int maxRecords = 25;
...
public List<BlogEntry> getPagedBlogEntries(int startPosition) {

    String queryString = "SELECT be FROM BlogEntry";

    Query query = entityManager.createQuery(queryString);
    query.setMaxResults(maxRecords);
    query.setFirstResult(startPosition);

    return entityManager.getResultList(queryString);
}
  

Flushing Query objects

JPA provides two modes of flushing query objects namely AUTO and COMMIT (which are defined in FlushModeType Enumeration).
If the AUTO mode is set on the Query object which is the default one, then any changes made to entity objects will be reflected the very next time when a select query is made. That means that the persistence engine must make sure to update all the states of the entity objects which will affect the results of a SELECT query.
The AUTO flush mode can be set explicitly on the Query by calling setFlushMode().
queryObject.setFlushMode(FlushModeType.AUTO);
  
When the flush mode is set to COMMIT, then the persistence engine, may only update all the state of the entities during the database COMMIT.
The dirty changes applied to entity objects and then querying for the results of the entities may be unspecified.
queryObject.setFlushMode(FlushModeType.COMMIT);
  
[The method setFlushMode(FlushModeType) is available in both EntityManager and Query interface.
If the EntityManager has been configured with setFlushMode(FlushModeType.AUTO) and the Query object has been called with setFlushMode(FlushModeType.COMMIT), then the preference will always goes to the Query object.]

Configurare la persistenza

//  Nel caso di J2SE
String persistentUnitName = "MyBlogPersistentUnit";
EntityManagerFactoryfactory  factory = 
  Persistence.createEntityManagerFactory(persistentUnitName);
 
EntityManager entityManager = factory.createEntityManager(); 
  
// Nel caso J2EE
@PersistenceContext(unitName = "MyBlogPersistentUnit")
private EntityManager entityManager;
  

Persistence.xml

Esempio di file persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<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_1_0.xsd" 
  version="1.0">

   <persistence-unit name="entityManager">
      <provider>org.hibernate.ejb.HibernatePersistence</provider>
      <jta-data-source>java:/blogDatasource</jta-data-source>
      <properties>
         <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
         <property name="jboss.entity.manager.factory.jndi.name" 
	           value="java:/blogEntityManagerFactory"/>
         <property name="hibernate.jdbc.charSet" value="utf-8"/>
      </properties>
   </persistence-unit>
</persistence>
  

Java DataSource

<?xml version="1.0" encoding="UTF-8"?>

<datasources>
    <local-tx-datasource>
        <jndi-name>blogDatasource</jndi-name>
        <connection-url>jdbc:hsqldb:.</connection-url>
        <driver-class>org.hsqldb.jdbcDriver</driver-class>
        <user-name>sa</user-name>
        <password></password>
    </local-tx-datasource>
</datasources>
</div>
  

Molteplicità nelle relazioni

esempio di ManyToOne

@Entity
@Table(name = "bookings", schema = "public")
public class Booking {

  ...
  private Customer customer;
  ...

  @ManyToOne
  @JoinColumn(name = "customer_id", nullable = false)
  @NotNull
  public Customer getCustomer() {
    return this.customers;
  }
  ....
}
  

Relazioni inverse

@Entity
@Table(name = "customers", schema = "public")
public class Customer {
  ...
  private List<Booking> bookings;
  ...

  @OneToMany(mappedBy = "customer")
  public List<Booking> getBookings() {
    return this.bookings;
  }
  ...
  

aiuto!

Riferimenti

The end

Grazie
per
l'attenzione