Introducing EntityManager
It manages the lifecycle of entities.
EntityManager
interface
In a sense, the EntityManager
is the bridge between the OO (object-oriented) and relational worlds.
EntityManager
is used to perform CRUD operations. Here are the most commonly used methods of the EntityManager
interface.
Method signature | Description |
---|---|
public void persist(Object entity); | Saves (persists) an entity into the database and makes the entity managed. |
public <T> T merge(T entity); | Merges an entity to the EntityManager ’s persistence context and returns the merged entity. |
public void remove(Object entity); | Removes an entity from the database. |
public <T> T find(Class<T> entityClass, Object primaryKey); | Finds an entity instance by its primary key. This method is overloaded and comes in different forms. |
public void flush(); | Synchronizes the state of entities in the EntityManager ’s persistence context with the database. |
public void setFlushMode(FlushModeType flushMode); | Changes the flush mode of the EntityManager ’s persistence context. The flush mode may be AUTO or COMMIT . The default flush mode is AUTO , meaning that the EntityManager tries to automatically sync the entities with the database. |
public FlushModeType getFlushMode(); | Retrieves the current flush mode. |
public void refresh(Object entity); | Refreshes (resets) the entities from the database. This method is overloaded and comes in different forms. |
public Query createQuery(String jpqlString); | Creates a dynamic query using a JPQL statement. This method is overloaded and comes in different forms. |
public Query createNamedQuery(String name); | Creates a query instance based on a named query on the entity instance. This method is overloaded and comes in different forms. |
public Query createNativeQuery(String sqlString); | Creates a dynamic query using a native SQL statement. This method is overloaded and comes in different forms. |
public StoredProcedureQuery createStoredProcedureQuery(String procedureName); | Creates a StoredProcedureQuery for executing a stored procedure. This method is overloaded and comes in different forms. |
public void close(); | Closes an application-managed EntityManager . |
public void clear(); | Detached all managed entities from the persistence context. All changes made to entities not committed will be lost. |
public boolean isOpen(); | Checks whether an EntityManager is open. |
public EntityTransaction getTransaction(); | Retrieves a transaction object that can be used to manually start or end a transaction. |
public void joinTransaction(); | Asks an EntityManager to join an existing JTA transaction. |
Lifecycle of an entity
Managed entities
Bid bid = new Bid();
manager.persist(bid);
Detached entities
manager.remove(bid);
Persistence context, scopes, and the EntityManager
The EntityManager
delegates the task of managing the entity state to the currently available persistence context.
In a very simple sense, a persistence context is a self-contained collection of entities managed by an EntityManager
during a given persistence scope. The persistence scope is the duration of time a given set of entities remains managed.
There are two different types of persistence scopes: transaction
and extended
.
Transaction-scoped EntityManager
An EntityManager
associated with a transaction-scoped persistence context is known as a transaction-scoped EntityManager
. If a persistence context is under transaction scope, entities attached during a transaction are automatically detached when the transaction ends.
Extended-scope EntityManager
The lifespan of the extended EntityManager
lasts across multiple transactions. An extended-scoped EntityManager
can only be used with stateful session beans and lasts as long as the bean instance is alive.
Once an entity is attached in any given transaction, it’s managed for all transactions in the lifetime of the persistence context
Using EntityManager
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface PersistenceContext {
public String name() default "";
public String unitName() default "";
public PersistenceContextType type() default PersistenceContextType.TRANSACTION;
public PersistenceProperty[] properties() default {};
}
name
: specifies the JNDI name of the persistence context. This element is used in the unlikely case that you must explicitly mention the JNDI name for a given container implementation to be able to look up anEntityManager
. In most situations, leaving this element empty is fine, except when you use@PersistenceContext
at the class level to establish a reference to the persistence context.unitName
: element specifies the name of the persistence unit. A persistence unit is a grouping of entities used in an application. The idea is useful when you have a large Java EE application and would like to separate it into several logical areas (think Java packages).
Persistence units can’t be set up using code; you must configure them through the persistence.xml
deployment descriptor.
@PersistenceContext(unitName="admin")
EntityManager entityManager
You aren’t allowed to use extended persistence scope for stateless session beans or MDBs. The reason is pretty obvious, because the purpose of the extended scope is to extend across method invocations on a bean, even if each method invocation is a separate transaction. Because neither stateless session beans nor MDBs are supposed to implement such functionality, it makes no sense to support extended scope for these bean types. On the other hand, extended persistence scope is ideal for stateful session beans. An underlying EntityManager
with extended scope could be used to cache and maintain the application domain across an arbitrary number of method invocations from the client. More importantly, this can be done without giving up on method-level transaction granularity (most likely using CMT).
EntityManager
and Thread-Safety
EntityManagers
aren’t thread-safe and shouldn’t be used in situations where more than one thread may access them. This means that it’s dangerous to use EntityManager
s in servlets or JSP pages. A servlet is instantiated once and handles multiple requests concurrently.
**It’s best to use EntityManager
s from within EJBs. **
If you must access the EntityManager
directly, you can use the following code snippet or access the EntityManagerFactory
:
@PersistenceContext(name="pu/actionBazaar" unitName="ActionBazaar")
public class ItemServlet extends HttpServlet {
@Resource
private UserTransaction ut;
public void service(HttpSerlvetRequest req, HttpServletResponse resp)
throws ServletException, IOException {
Context ctx = new InitialContext();
EntityManager em = (EntityManager)ctx.lookup("java:comp/env/pu/actionBazaar");
...
ut.begin();
em.persist(item);
ut.commit();
...
}
}
The other alternative is to use an application-managed EntityManager with a JTA transaction. It’s worth noting that EntityManagerFactory
is thread-safe.
Injecting the EntityManagerFactory
Using the EntityManagerFactory
, you can get EntityManager
instances that you can fully control.
The EntityManagerFactory
annotation is defined as follows:
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface PersistenceUnit {
String name() default "";
String unitName() default "";
}
The name
and unitName
elements serve exactly the same purpose as they do for the @PersistenceContext
annotation. Whereas the name
element can be used to point to the JNDI name of the EntityManagerFactory
, the unitName
element is used to specify the name of the underlying persistence unit.
Persistence operations
Persisting entities
Retrieving entities by key
- By primary key
Seller seller = entityManager.find(Seller.class, sellerId);
- Find by primary key using composite keys
SellerPK sellerKey = new SellerPK();
sellerKey.setFirstName(firstName);
sellerKey.setLastName(lastName);
Seller seller = entityManager.find(Seller.class, sellerKey);
Entity fetch mode
JPA has more than one mechanism to support lazy fetching. Specifying column fetch-mode using the @Basic
annotation is the easiest one to understand. For example, you can set the fetch mode for the picture
property on the Item
entity to be lazy as follows:
@Column(name="PICTURE")
@Lob
@Basic(fetch=FetchType.LAZY)
public byte[] getPicture() {
return picture;
}
A SELECT
statement generated by the find method to retrieve Item
entities won’t load data from the ITEMS.PICTURE
column into the picture
field. Instead, the picture
data will be automatically loaded from the database when the property is first accessed through the getPicture
method.
Loading related entities
EntityManager
’s find method must retrieve all entities related to the one returned by the method.
When the find
method returns an instance of an Item
, it also automatically retrieves the Seller
, Bid
, and Category
entities associated with the instance and populates them into their respective Item
entity properties.
By default, some of the relationship types are retrieved lazily
and some are loaded eagerly
.
The Seller
associated with an Item is retrieved eagerly, because the fetch mode for the @ManyToOne
annotation is defaulted to EAGER
.
As the listing shows, an eager
fetch means that the most natural way of retrieving the Item
entity would be through a single SELECT
statement using a JOIN
between the ITEMS
and SELLERS
tables. It’s important to note the fact that the JOIN
will result in a single row, containing columns from both the SELLERS
and ITEMS
tables.
Pretty much the same thing applies to the @OneToOne
annotation.
lazy
versus eager
loading of related entities
In contrast, the @OneToMany
and @ManyToMany
annotations are defaulted to lazy
loading.
Behavior of loading an associated entity is different for each kind of association by default. You can change the loading behavior by specifying the fetch
element with the relationship.
Relationship type | Default fetch behavior | Number of entities retrieved |
---|---|---|
One-to-one | EAGER | Single entity retrieved |
One-to-many | LAZY | Collection of entities retrieved |
Many-to-one | EAGER | Single entity retrieved |
Many-to-many | LAZY | Collection of entities retrieved |
Here’s an example of explicitly specifying the fetch mode for a relationship
@ManyToOne(fetch=FetchType.LAZY)
public Seller getSeller() {
return seller;
}
Updating entities
Recall that the EntityManager
makes sure that changes made to attached entities are always saved into the database behind the scenes
. This means that for the most part, the application doesn’t need to worry about manually calling any methods to update the entity.
After each method is invoked, the EntityManager
ensures that the changes are persisted to the database. Typically, all changes are persisted when the transaction ends and the EntityManager
attempts to commit all the changes. But you can force persistence at any time by using the EntityManager
flush
method. When flush
is called, it applies to all entities managed in the persistence context. Flushing is controlled by the FlushModeType
.
FlushTypeMode.AUTO
means to persist to the database at query execution time.FlushTypeMode.COMMIT
will persist to the database when the transaction ends and is committed.
Detachment and merge operations
This means that entities returned by the session bean to its clients are always detached, just like the newly created Item entity returned by the ItemManager
’s add- Item method:
public Item addItem(String title, String description,
byte[] picture, double initialPrice, long sellerId) {
Item item = new Item();
item.setTitle(title);
...
entityManager.persist(item);
return item;
}
- An entity instance can be detached and serialized to a separate tier where the client makes changes to the entity and sends it back to the server. The server can use a merge operation to attach the entity to the persistence context.
public Item updateItem(Item item) {
entityManager.merge(item);
return item;
}
As soon as the updateItem
method returns, the database is updated with the data from the Item
entity. The merge
method must only be used for an entity that exists in the database. An attempt to merge a nonexistent entity will result in an IllegalArgumentException
. The same is true if the EntityManager
detects that the entity you’re trying to merge has already been deleted through the remove
method, even if the DELETE
statement hasn’t been issued yet.
Merging relationships
By default, entities associated with the entity being merged aren’t merged as well. For example, the Seller
, Bid
, and Category
entities related to the Item
aren’t merged when the Item
is merged in the previous code snippet.
But this behavior can be controlled using the cascade
element of the @OneToOne
, @OneToMany
, @ManyToOne
, and @ManyToMany
annotations. The cascade
element is added to the annotation on the owning side of the relationship.
If the cascade
element is set to either ALL
or MERGE
, the related entities are merged. For example, the following code will cause the Seller
entity related to the Item
to be merged because the cascade
element is set to MERGE
:
public class Item {
@ManyToOne(cascade=CascadeType.MERGE)
public Seller getSeller() {
Note that as in most of the EntityManager
’s methods, the merge
method must be called from a transactional context or it’ll throw a TransactionRequiredException
.
Detached entities and the DTO anti-pattern
If you’ve spent even a moderate amount of time using EJB 2, you’re probably thoroughly familiar with the data transfer object (DTO) anti-pattern. In a sense, the DTO anti-pattern was necessary because of entity beans. The fact that EJB 3 detached entities are nothing but POJOs makes the DTO anti-pattern less of a necessity of life. Instead of having to create separate DTOs from domain data just to pass back and forth between the business and presentation layers, you may simply pass detached entities. This is exactly the model followed in this chapter.
But if your entities contain behavior, you might be better off using the DTO pattern anyway, to safeguard business logic from inappropriate usage outside a transactional context. In any case, if you decide to use detached entities as a substitute for DTOs, you should make sure they’re marked java.io.Serializable
.
Deleting entites
An important detail to notice about the deleteItem
method (repeated next) is that the Item
to be deleted was first attached to the EntityManager
using the merge
method:
public void deleteItem(Item item) {
entityManager.remove(entityManager.merge(item));
}
This is because the remove
method can only delete currently attached entities and the Item
entity being passed to the deleteItem
method isn’t managed. If a detached entity is passed to the remove
method, it throws an IllegalArgumentException
. Before the deleteItem
method returns, the Item
record will be deleted from the database using a DELETE
statement like this:
DELETE FROM ITEMS WHERE item_id = 1
Just as with the persist
and merge
methods, the DELETE
statement isn’t necessarily issued immediately but is guaranteed to be issued at some point. Meanwhile, the EntityManager
marks the entity as removed so that no further changes to it are synchronized (as we noted in the previous section).
Cascading remove operations
Just as with merging and persisting entities, you must set the cascade
element of a relationship annotation to either ALL
or REMOVE
for related entities to be removed with the one passed to the remove
method.
@Entity
public class Bidder {
@OneToOne(cascade=CascadeType.REMOVE)
public BillingInfo setBillingInfo() {
In general, you might find that the only relationship types where cascading removal makes sense are one-to-one and one-to-many. You should be careful when using the cascade delete because the related entity you’re cascading the delete to may be related to other entities you don’t know about.
Handling relationships
If your intent was really to cascade-delete the Items
associated with the Seller
, you should iterate over all instances of Category
that reference the deleted Items
and remove the relationships first, as follows:
List<Category> categories = getAllCategories();
List<Item> items = seller.getItems();
for (Item item: items) {
for (Category category: categories) {
category.getItems().remove(item);
}
}
entityManager.remove(seller);
Entity queries
javax.persistence.Query
—Represents either a JPQL or native SQL query. If a query is typed, ajavax.persistence.TypeQuery<T>
is returned, thus eliminating the need to cast results.javax.persistence.StoredProcedureQuery
—Represents a query that invokes a stored procedure.javax.persistence.criteria.CriteriaQuery
—Represents a query that’s constructed using the meta-model.
In addition to these types of queries, you also have dynamic and named queries.
- Dynamic queries are queries created in code—the query is passed off to the
EntityManager
. - A named query is a query that’s either configured in an annotation or pulled from an ORM XML configuration file.
Dynamic queries
Dynamic queries are created on the fly and embedded within the code. Dynamic queries are typically only used once—that is, the query isn’t shared among multiple components in an application.
@PersistenceContext
private EntityManager entityManager;
public List<Category> findAllCategories() {
TypedQuery<Category> query = entityManager.createQuery(
"SELECT c FROM Category c",Category.class);
return query.getResultList();
}
You can also use dynamic queries to execute both native SQL and stored procedure queries as well— with createNativeQuery
and createStoredProcedureQuery
, respectively.
Named queries
- A named query must be created before it can be used.
- It’s defined in the entity using annotations or in the XML file defining O/R mapping metadata.
- A named query is accessed by its name, thus enabling its use across multiple components in an application.
- A named query is defined using the
@javax.persistence.NamedQuery
annotation
@Entity
@NamedQuery(
name = "findAllCategories",
query = "SELECT c FROM Category c WHERE c.categoryName
LIKE :categoryName ")
public class Category implements Serializable {
public List<Category> findAllCategories() {
TypedQuery<Category> query =
entityManager.createNamedQuery("findAllCategories",Category.class);
}
}
@Entity
@NamedQueries({
@NamedQuery(
name = "findCategoryByName",
query = "SELECT c FROM Category c WHERE c.categoryName
LIKE :categoryName order by c.categoryId"
),
@NamedQuery(
name = "findCategoryByUser",
query = "SELECT c FROM Category c JOIN c.user u
WHERE u.userId = ?1"
)})
@Table(name = "CATEGORIES")
public class Category implements Serializable {
...
}