Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/Examples/JPA/EMAPI
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
- 1 Obtaining an EntityManager in a SessionBean
- 2 EntityManager persist()
- 3 EntityManager merge()
- 4 EntityManager remove()
- 5 EntityManager find()
- 6 EntityManager getReference()
- 7 EntityManager flush()
- 8 EntityManager setFlushMode()
- 9 EntityManager lock()
- 10 EntityManager refresh()
- 11 EntityManager clear()
- 12 EntityManager contains()
- 13 EntityManager createQuery()
- 14 EntityManager createQuery(Expression)
- 15 Getting a JDBC Connection from an EntityManager
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.
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); } ...
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); } ...
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); } ...
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); } ...
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); } ...
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(); } ...
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. } ...
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"); } ...
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); } ...
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(); } ...
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); } ...
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; } ...
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; } ...
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.
JPA 2.0
entityManager.getTransaction().begin(); java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class); ... entityManager.getTransaction().commit();
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();