Difference between revisions of "EclipseLink/Examples/JPA/EMAPI"

From Eclipsepedia

Jump to: navigation, search
(EntityManager find())
(JPA 1.0: Previous version have not behaved correctly when called more than once and did some updates.)
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 +
[[Category:EclipseLink/Example/JPA|EMAPI]]
 +
 
This document demonstrates EclipseLink’s support for the JPA specification, specifically the usage of the EntityManager API
 
This document demonstrates EclipseLink’s support for the JPA specification, specifically the usage of the EntityManager API
  
Line 7: Line 9:
 
* Annotations are used for O-R Mapping  
 
* Annotations are used for O-R Mapping  
  
Within JPA the javax.persistence.EntityManager is the application access point for persisting EntityBeans to and loading them from the database. These data operations are achieved through the API covered below.  
+
Within JPA the javax.persistence.EntityManager is the application access point for persisting EntityBeans to and loading them from the database. These data operations are achieved through the API covered below.
 +
 
 +
For a complete list of the EntityManager API see, [http://www.eclipse.org/eclipselink/api/1.2/javax/persistence/EntityManager.html EntityManager].
 +
 
 +
For a list of EclipseLink's extensions to the EntityManager API see, [http://www.eclipse.org/eclipselink/api/1.2/org/eclipse/persistence/jpa/JpaEntityManager.html JpaEntityManager]
  
 
== Obtaining an EntityManager in a SessionBean ==
 
== Obtaining an EntityManager in a SessionBean ==
Line 142: Line 148:
 
If you would like to control the way a flush() call executes, you may call setFlushMode() before hand. The flush modes are as follows:
 
If you would like to control the way a flush() call executes, you may call setFlushMode() before hand. The flush modes are as follows:
  
* COMMIT - Flushing must occur only at transaction commit.
+
* COMMIT - Flushing occurs only at transaction commit, or when flush() is called.
* AUTO - (Default) Flushing to occur at query execution.
+
* AUTO - (Default) Flushing occurs before any query execution.
  
 
<source lang="java">
 
<source lang="java">
Line 150: Line 156:
 
     ...
 
     ...
 
     public void disemployEmployee(Integer employeeId, Date endDate) {
 
     public void disemployEmployee(Integer employeeId, Date endDate) {
 +
        em.setFlushMode(COMMIT);  // Avoids find causing flush.
 
         Employee employee = (Employee)em.find("Employee", employeeId);
 
         Employee employee = (Employee)em.find("Employee", employeeId);
 
         employee.getPeriod().setEndDate(endDate);
 
         employee.getPeriod().setEndDate(endDate);
         em.setFlushMode(COMMIT);
+
         em.flush(); // Causes flush.
        em.flush();
+
 
     }
 
     }
 
     ...
 
     ...
Line 261: Line 267:
  
  
== Getting a DataSource from an EntityManager ==
+
== Getting a JDBC Connection from an EntityManager ==
 
Normally you let the JPA container manage all interaction with the datasource for your application.  However, if you do require access to the datasource or connection in use by your JPA application, the following code should help you.
 
Normally you let the JPA container manage all interaction with the datasource for your application.  However, if you do require access to the datasource or connection in use by your JPA application, the following code should help you.
  
The following line of code will get the current JTA or non-JTA datasource in use by an entityManager in a container managed JPA application using a JTA transaction-type.
+
If you are using a JTA DataSource for your JPA persistence unit, then you can just access the JDBC Connection from the JavaEE containers DataSource.  Just inject or lookup the JTA DataSource from JNDI and get a Connection from it.  As long as you are within a JTA transactional context, the JDBC Connection will be the same Connection used by JPA.
 +
 
 +
If you are not using JTA, but are still using a non-JTA DataSource, then you can still access the DataSource from the container if you want a new JDBC Connection.  If you want the same JDBC Connection as used by JPA, then you can access this using the JpaEntityManager interface.  This will work with both a DataSource or a direct DriverManager JDBC Connection in JavaSE.
 +
 
 +
This is done differently in JPA 2.0 and JPA 1.0.
 +
 
 +
You should be in a JPA transaction to access the Connection.  Otherwise, you will be responsible for releasing the connection.
  
=== DataSource from a Container Managed EntityManager ===
+
===JPA 2.0===
 
<source lang="java">
 
<source lang="java">
DataSource aDataSource = ((JNDIConnector)((DatabaseLogin)
+
entityManager.getTransaction().begin();
  ((UnitOfWorkImpl)((JpaEntityManager)
+
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
    entityManager.getDelegate())
+
...
      .getActiveSession()
+
entityManager.getTransaction().commit();
        .acquireUnitOfWork())
+
          .getProject()
+
            .getDatasourceLogin())
+
              .getConnector())
+
                .getDataSource();
+
 
</source>
 
</source>
=== Connection from an SE EntityManager ===
 
The following code will return a null connection or cause a (non-narrow()) $Proxy ClassCastException if run on a container managed EntityManager, it is supplied for SE applications or one using the JavaSECMPInitializer or a RESOURCE_LOCAL transaction-type.
 
  
 +
===JPA 1.0===
 
<source lang="java">
 
<source lang="java">
Connection aConnection = ((UnitOfWorkImpl)((JpaEntityManager)entityManager)
+
entityManager.getTransaction().begin();
  .getActiveSession()
+
JpaEntityManager jpaEntityManager = (JpaEntityManager) entityManager.getDelegate();
    .acquireUnitOfWork())
+
AbstractSession session = (AbstractSession) jpaEntityManager.getActiveSession();
      .getAccessor()
+
UnitOfWork unitOfWork = (UnitOfWork) jpaEntityManager.getActiveSession();
        .getConnection();
+
final Accessor accessor;
 +
if (session.isInTransaction() || session.isExclusiveIsolatedClientSession()) {
 +
  accessor = session.getAccessor();
 +
} else {
 +
  unitOfWork.beginEarlyTransaction();
 +
  accessor = session.getAccessor();
 +
  accessor.incrementCallCount(unitOfWork.getParent());
 +
  accessor.decrementCallCount();
 +
}
 +
java.sql.Connection connection = accessor.getConnection();
 +
...
 +
entityManager.getTransaction().commit();
 
</source>
 
</source>

Latest revision as of 05:36, 20 November 2013


This document demonstrates EclipseLink’s support for the JPA specification, specifically the usage of the EntityManager API

JPA greatly simplifies the development of EJBs, removing many complex development tasks. For example, creating a simple CMP entity EJB using EJB 2.1 requires a bean class and at least two interfaces, as well as a deployment descriptor. The remote (or local) and home interfaces had to extend javax.ejb.EJBObject and javax.ejb.EJBHome interfaces respectively, and the bean class had to implement the javax.ejb.EntityBean interface. However, in JPA, development is greatly simplified due to the following specifications:

  • The bean class can be a plain java class (POJO)
  • No interfaces are required for an entity bean
  • Annotations are used for O-R Mapping

Within JPA the javax.persistence.EntityManager is the application access point for persisting EntityBeans to and loading them from the database. These data operations are achieved through the API covered below.

For a complete list of the EntityManager API see, EntityManager.

For a list of EclipseLink's extensions to the EntityManager API see, JpaEntityManager

Contents

[edit] Obtaining an EntityManager in a SessionBean

Before an EntityManager can be used it must be obtained from the container. The EntityManager can be set into your SessionBean by the Container though the @PersistenceUnit annotation.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
 
    @PersistenceUnit
    protected EntityManager em;
    ...
    public EntityManager getEntityManger(){
        return this.em;
    }
    ...

Whenever the SessionBean is accessed an EntityManger will be available in the em attribute. The EntityManager can also be retrieved through a JNDI lookup.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    protected EntityManager em;
    ...
    public EntityManager getEntityManager() {
        if (em == null}
            try{
                em = (EntityManager)(new InitialContext()).lookup("java:comp/ejb/EntityManager");
            } catch (Exception e){};
        }
        return em;
    }
    ...

Note that the @PersistenceUnit annotation is not used.

[edit] EntityManager persist()

The EntityManager persist(Object entity) API is used to mark a new instance for insert into the Database. It must only be called on new Entities. The value returned from the persist(Object entity) call is the same instance as was passed in.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public void createEmployee(String fName, String lName) {
        Employee employee  = new Employee();
        employee.setFirstName(fName);
        employee.setLastName(lName);
        em.persist(employee);
    }
    ...

[edit] EntityManager merge()

To integrate a bean read in a different transaction or provided by the client into the current transaction use the merge(Object entity) API. The result will be an instance of the provided entity. The state of that entity will also be available in the returned instance.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public void updateAddress(Address addressExample) {
        em.merge(addressExample);
    }
    ...

[edit] EntityManager remove()

To delete a bean from the database use the remove(Object entityBean) API.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public void removeEmployee(Integer employeeId) {
        Employee employee = (Employee)em.find(Employee.class, employeeId);
        ...
        em.remove(employee);
    }
    ...

[edit] EntityManager find()

When a primary key query is required the find(Class entityClass, Object primaryKey) API can be used.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public Employee findEmployee(Integer employeeId) {
        return (Employee) em.find(Employee.class, employeeId);
    }
    ...

[edit] EntityManager getReference()

To retrieve an instance from the database using a primary key. This method may throw an EntityNotFoundException if the requested instance does not exist in the database.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public Employee getEmployee(Integer employeeId) {
        return (Employee)em.getReference(Employee.class, employeeId);
    }
    ...

[edit] EntityManager flush()

The EntityManager flush() API is used to send updates to the database within a transaction, subsequent queries within the same transaction will return the updated data. This is useful if a particular transaction spans multiple operations or pages, similar to a "wizard".

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public void disemployEmployee(Integer employeeId, Date endDate) {
        Employee employee = (Employee)em.find("Employee", employeeId);
        employee.getPeriod().setEndDate(endDate);
        em.flush();
    }
    ...

[edit] EntityManager setFlushMode()

If you would like to control the way a flush() call executes, you may call setFlushMode() before hand. The flush modes are as follows:

  • COMMIT - Flushing occurs only at transaction commit, or when flush() is called.
  • AUTO - (Default) Flushing occurs before any query execution.
@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public void disemployEmployee(Integer employeeId, Date endDate) {
        em.setFlushMode(COMMIT);  // Avoids find causing flush.
        Employee employee = (Employee)em.find("Employee", employeeId);
        employee.getPeriod().setEndDate(endDate);
        em.flush();  // Causes flush.
    }
    ...

[edit] EntityManager lock()

Use the lock(Object entity, LockModeType lockMode) when you wish to lock an entity from outside changes within a persistence context. The lock mode type can either be at the READ or WRITE level.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
	public void undoUpdateEmployee(Integer employeeId){
            Employee employee = (Employee)em.find("Employee", employeeId);
            em.lock(employee, WRITE);
            employee.setFirstName("Bill");
	}
    ...

[edit] EntityManager refresh()

When the latest data is required or when changes need to be reverted in an Entity Bean the refresh(Object entity) API can be used.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
	public void undoUpdateEmployee(Integer employeeId){
            Employee employee = (Employee)em.find("Employee", employeeId);
            em.refresh(employee);
	}
    ...

[edit] EntityManager clear()

Clear the persistence context, causing all managed entities to become detached. Changes made to entities that have not been flushed to the database will not be persisted.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
	public void clearEmployees() {
            em.clear();
	}
    ...

[edit] EntityManager contains()

Check if the instance belongs to the current persistence context.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
	public boolean isActiveEmployee(Employee emp) {
            return em.contains(employee);
	}
    ...

[edit] EntityManager createQuery()

In order to perform queries based on more complex criteria queries can be created using the createQuery(string ejbqlString), createNamedQuery(String name) or createNativeQuery(String sqlString)

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public Collection findLargeProjectsWithBudgetLargerThan(double budget) {
        Collection projects = em.createNamedQuery("findWithBudgetLargerThan")
             .setParameter("amount", budget).getResultList();
        return projects;
    }
 
    public Project findProjectWithName(String name) {
        Project project = (Project)em.createQuery("SELECT OBJECT(project) FROM Project project WHERE project.name = :projectName")
                .setParameter("projectName", name).getSingleResult();
        return project;
    }
   ...

[edit] EntityManager createQuery(Expression)

If EclipseLink's java like expressions are preferred for the query criteria, that functionality is available though the createQuery(Expression expression, Class resultType) API on the EclipseLink specific EntityManager implementation org.eclipse.persistence.jpa.JpaEntityManager.

Use the org.eclipse.persistence.jpa.JpaHelper class to retrieve the EclipseLink JpaEntityManager.

@Stateless
public class EmployeeDemoSessionEJB implements EmployeeDemoSession {
    ...
    public Collection findProjectsWithName(String name) {
        ExpressionBuilder builder = new ExpressionBuilder();
        JpaEntityManager jpaEm = JpaHelper.getEntityManager(em);
        Query query = jpaEm.createQuery(builder.get("name").equals(builder.getParameter("projectName")), Project.class);
        query.setParameter("projectName", name);
        Collection projects = query.getResultList();
        return projects;
    }
    ...


[edit] Getting a JDBC Connection from an EntityManager

Normally you let the JPA container manage all interaction with the datasource for your application. However, if you do require access to the datasource or connection in use by your JPA application, the following code should help you.

If you are using a JTA DataSource for your JPA persistence unit, then you can just access the JDBC Connection from the JavaEE containers DataSource. Just inject or lookup the JTA DataSource from JNDI and get a Connection from it. As long as you are within a JTA transactional context, the JDBC Connection will be the same Connection used by JPA.

If you are not using JTA, but are still using a non-JTA DataSource, then you can still access the DataSource from the container if you want a new JDBC Connection. If you want the same JDBC Connection as used by JPA, then you can access this using the JpaEntityManager interface. This will work with both a DataSource or a direct DriverManager JDBC Connection in JavaSE.

This is done differently in JPA 2.0 and JPA 1.0.

You should be in a JPA transaction to access the Connection. Otherwise, you will be responsible for releasing the connection.

[edit] JPA 2.0

entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();

[edit] JPA 1.0

entityManager.getTransaction().begin();
JpaEntityManager jpaEntityManager = (JpaEntityManager) entityManager.getDelegate();
AbstractSession session = (AbstractSession) jpaEntityManager.getActiveSession();
UnitOfWork unitOfWork = (UnitOfWork) jpaEntityManager.getActiveSession();
final Accessor accessor;
if (session.isInTransaction() || session.isExclusiveIsolatedClientSession()) {
  accessor = session.getAccessor();
} else {
  unitOfWork.beginEarlyTransaction();
  accessor = session.getAccessor();
  accessor.incrementCallCount(unitOfWork.getParent());
  accessor.decrementCallCount();
}
java.sql.Connection connection = accessor.getConnection();
...
entityManager.getTransaction().commit();