Manipulating Entities with EntityManager
The EntityManager figures out how to persist the domain by looking at the ORM configuration, it performs the CRUD (create, read, update and delete) operations on the domain objects in this section you will learn about the EntityManager interface, the lifecycle of entities, the concept of persistence context and how to obtain an instance of EntityManager.
The EntityManager API manages the lifecycle of entities, it is the bridge between the OO and the relational worlds, as seen in the picture below
The EntityManager performs the following
Below is a table detailing the most common methods used of the EntityManager API
Method Signature |
Description |
public void persist(Object entity); | Saves (persists) an entity into the database, also 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 |
public void flush(); | Synchronizes the state of entities in the EntityManager's persistence context with the database |
public mode setFlushMode(FlushModeType flushMode); | Change the flush mode of the EntityManager's persistence context. The flush mode may either be AUTO or COMMIT. The default flush mode is AUTO, meaning that the EntityManager tries to automatically synch the entities with the database. |
public FlushTypeMode getFlushMode(); | Retrieves the current flush mode |
public void refresh(Object entity); | Refreshes (resets) the entity from the database |
public Query createQuery(String name); | Creates a dynamic query using a JPQL statement |
public Query createNamedQuery(String name); | Creates a query instance based on a named query on the entity instance. |
public Query createNativeQuery(String sqlString); public Query createNativeQuery(String sqlString, Class result); public Query createNativeQuery(String sqlString, String resultSetMapping); |
Creates a dynamic query using a native SQL statement |
public void close(); | Closes an application-managed EntityManager |
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 |
Entities are not loaded or managed by the container like session or MDB's, the EntityManager's task is to manage the entity and for the shortest time possible. Entities can be in a number of different states
An entities state is managed by the EntityManager, it makes sure it is in sync with the database. When the EntityManager starts to manage an entity, it synchronizes the entities state with the database, it also ensures that any changes are reflected in the database, it accomplishes this by holding an object reference to the managed entity and periodically checking for data freshness, if it finds that the entity has changed it synchronizes with the database. When the EntityManager can no longer reach the entity it is said to be detached ( it can be moved out of scope by either being removed, serialized or cloned), an entity will also be detached immediately when no transaction is associated with it.
An entity becomes attached when you use persist, merge, refresh or find, the state of the entity determines which method you will use (see above picture).
The merge and refresh methods are intended for entities that have been retrieved from the database and are in detached state, merge updates the database with the data held in the entity, and refresh does the opposite. You can detach an entity to upload it to the web tier, the web tier can then update it and then send it back, we then reattach it and it updates the database.
The persistence context plays a vital role in the internal functionality of the EntityManager, it is the persistence context that manages the lifecycle of an entity, in simple terms a persistence context is a self-contained collection of entities managed by an EntityManager during a given persistence scope. There are two different scopes
Transaction-scoped EntityManager | If a persistence context is under transaction scope, entities attached during a transaction are automatically detached when the transaction ends. All persistence operations must occur inside the transaction, once the transaction is either committed or rolled back all entities are detached after they have been synchronized with the database. |
Extended EntityManager | An Extended EntityManager can only be used with stateful session beans and lasts as long as the bean instance is alive. Until the EntityManager is closed by the session bean being destroyed the entities will remain managed, unless explicitly removed. |
Now for an example, I have only highlighted the necessary parts to give to you an idea
Entity example | @Stateless public class ItemManagerBean implements ItemManager { // Inject the EntityManager instance @PersistenceContext(unitName="actionBazzar") private EntityManager entityManager; // No-arg constructor for creating ItemManagerBean instances by the container public ItemManagerBean() {} public Item addItem(String title, String description, byte[] picture, double initialPrice, long sellerId { Item item = new Item(); item.setTitle(title) ... // Retrieves entity using primary key Seller seller = entityManager.find(Seller.class, sellerId); item.setSeller(seller); // Persist entity instance entityManager.persist(item); return item; } public Item updateItem(Item item) { // Merges changes to the database entityManager.merge(item); } public Item undoItemChanges(Item item) { // Refreshes entity from the database entityManager.refresh(entityManager.merge(item)); return item; } public void deleteItem(Item item) { // Removes entity from the database entityManager.remove(entityManager.merge(item)); } } |
Creating EntityManager Instances
To manage entities you must obtain an instance of an EntityManager, to do this you use the @PersistenceContext annotation. There are additional steps that must be completed in your container, I have these steps in another subject JBoss Enterprise Applications and an example in EJB 3 Integration.The container will take care of opening, closing and looking up the EntityManager behind the scenes. JPA fully supports creating application-managed EntityManagers that you can create, use and release, including controlling how the EntityManager handles transactions, this is useful if you want to use the JPA outside the container.
The @PersistenceContext definition is below
@PersistenceContext definition | @Target({TYPE, METHOD, FIELD}) Notes: name - specifies the JNDI name of the persistence context |
Here is an example of changing the scope, remember you cannot use the extended scope for stateless session beans or MDBs. It is primary used for managing state across method calls hence why is is only used with stateful beans.
EntityManager Scoping | @PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager entityManager; |
One important point to remember is that the EntityManager is not thread safe, so do not go injecting them frivolously and do not inject them into a web component, yes there are ways around this (use SingleThreadModel) but don't go that route.
To access the EntityManager outside the container (you could be using Tomcat), you have to write code to control every aspect of the EntityManagers lifecycle.
@PersistenceUnit definition | @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface PersistenceUnit { String name() default ""; String unitName() default ""; } |
EntityManager outside the container | @Stateless public class ItemManagerBean implements ItemManager { @PersistenceUnit private EntityManagerFactory entityManagerFactory; private EntityManager entityManager; public ItemManagerBean() {} @PostConstruct public void initialize() { entityManager = entityManagerFactory.createEntityManager(); } ... public Item updateItem(Item item) { entityManager.joinTransaction(); entityManager.merge(item); return item; } ... @PreDestroy public void cleanup() { if (entityManager.isOpen()) { entityManager.close(); } } ... } |
The picture below shows the relationship between the various classes I mentioned above.
In Java SE environments the JTA is not available, so you must use resource-local transactions, the EntityTransaction interface can be used for this purpose.
Application-managed EntityManager | // Get the EntityManagerFactory instance EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("actionBazaar"); // Create the EntityManager EntityManager entityManager = entityManagerFactory.createEntityManager(); try { // Ceates a transaction EntityTransaction entityTransaction = entityManager.getTransaction(); // Begin the transaction entityTransaction.begin(); // Merges the item (update the database) entityManager.persist(item); // Commits the transaction entityTransaction.commit(); } finally { // Close all the resources entityManager.close(); entityManagerFactory.close(); } |
If you are using Tomcat or Jetty some persistence providers recommended that you use the ThreadLocal pattern, it associates a single instance of the EntityManager with a particular request. You bind the EntityManager to a thread-local variable and set the EntityManager instance to the associated thread. Check your persistence provider's documentation if you require to use the ThreadLocal pattern.
ThreadLocal pattern | private static EntityManagerFactory emf; return emf; public static EntityManager getEntityManager() { |
Managing Persistence Operations
A new entity is normally instantiated, and then all data that needs to go into the database is populated into the entity, this is normally passed in by the user. A persist method is then invoked to save the entity into the database. The persist method is intended to create new entity records in the database table not update existing ones, so make sure that the primary key used in not already there otherwise the persistence provider will throw a javax.persistence.PersistenceException. The persist method also causes the entity to become managed as soon as the method returns, the SQL statement generated is not immediately executed, in a transaction-scoped EntityManager this probably won't execute until the closing transaction is about to commit. So be careful not to rely on when the statement gets executed, but you can be sure that it will executed before the transaction completes. You can however manually flush the EntityManager at any time after the persist method, I will discuss this later.
By default the JPA does not persist related entities, in order to persist related entities we use the cascade attribute, you can set the cascade attribute to all, merge, persist, refresh or remove. The EntityManager will figure out all related entities, and will update, remove, etc accordingly.
persisting related entities | public class User { @oneToOne(cascade=CascadeType.PERSIST) public void setBillingInfo(BillingInfo billing) { Note: you can also use CascadeType.MERGE CascadeType.REFRESH CascadeType.REMOVE (use for one-to-many and one-to-one) CascadeType.ALL |
JPA supports several ways to retrieve entity instances from the database, the best way is using the primary key with the find method. The first argument specifies the Java type of the entity to be retrieved, the second is the identity value (in this case the primary key value).
find method | Seller seller = entityManager.find(Seller.class, sellerId); |
The identity value can either be a simple Java type identified by the @Id annotation or a composite primary key class specified through the @EmbeddedId or @IdClass annotation.
Find by primary key using composite keys | SellerPK sellerKey = new SellerPK(); sellerKey.setFirstName(firstName); sellerKey.setLastName(lastName); Seller seller = entityManager.find(Seller.class, sellerKey); |
If the find method does not find any matches, the Entity-Manager will return null or an empty entity and your application should handle this. To improve performance the find method uses the Entity-Managers caching capability, if your persistence provider supports caching.
The find method will retrieve all of the entity fields when it is invoked (by default), sometimes this may not be desirable, Fetch Modes allow you to change this behavior to optimize the performance.
The Entity-Manager has two fetch modes, they are apply when entities are related to one another
Eager fetching | Also know as eager loading, this is the default option, the entity-manager will attempt to retrieve all of the entity field data when the find method is invoked, this could include BLOBs or CLOBs, which means that it could have an performance impact. Because BLOBs and CLOBs have a heavy impact on I/O operations these should only be loaded when necessary. Other eager mechanisms are
|
Lazy fetching | Lazy fetching supports more than one mechanism
When lasy fetching the EntityManager would use separateSELECT statements and thus is less efficient than eager loading, when using entity relationships. |
Manually specifying | @ManyToOne(fetch=FetchType.LAZY) |
It is difficult to keep entities attached at all times, this is because entities will need to be detached and serialized at the web tier, where the entity is changed, which is outside the scope of the EntityManager, when using SLSB there is no guarantee that the same bean instance will be used, which means the entity will become detached, also entities become detached when transactions end. At some point you will want to reattach the entity to a persistence context to synchronize it with the database, the EntityManager's merge method is used for this. When using the merge method the entity may or may not be synchronized immediately, but it is guaranteed to be synchronized with the database sooner or later. If the entity has been removed then you will receive an IllegalArguementException error. By default entities that are associated with the entity being merged are not merged as well, you can control this using the cascade element of the @OneToOne, @OneToMany, @ManayToOne and @ManyToMany annotations. If the element is set to ALL or MERGE the related entities are merged.
merge method | entityManager.merge(item); Note: this should be called from a transactional context |
cascade element | @ManyToOne(cascade=CascadeType.MERGE) |
To remove an entity you use the remove method, remember this is not the same as detaching an entity. The remove method can only remove attached entities, again the remove may or may not happen immediately but is guaranteed to be issued at some point. Just 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. Be careful as the cascade option is dangerous, there could be many relationships through the entities.
remove method | entityManager.remove(entityManager.merge(item)); Note: the entity must be attached and should be called from a transactional context |
cascade element | @OneToOne(cascade=CascadeType.REMOVE) |
Another one of the EntityManager methods is the the flush method, remember that persist, merge and remove operations may or may not happen immediately, the reason for this is to batch work and then execute it thus saving on the number of database calls which increases performance. By default the database flush mode is set to AUTO, this means that the EntityManager performs a flush operation when needed, this normally occurs at the end of a transaction or when the persistence context is closed for application managed or extended-scope EntityManagers.
You can set the EntityManager flush mode to COMMIT, thus the persistence provider will only synchronize with the database when the transaction commits, however it will be your responsibility to synchronize entity state with the database before executing a query, if you don't do this you could end up with an inconsistent application. You can also explicitly flush the EntityManager by using the flush method.
flush mode | entityManager.setFlushMode(FlushModeType.COMMIT); |
flush method | entityManager.flush(); |
The last topic to discuss is the refresh method, this operation repopulates the entity data from the database overriding any changes in the entity.
refresh method | entityManager.refresh(entityManager.merge(item); entityManager.refresh(item); Note: the refresh method only works on managed entities |
Common usage | entityManager.persist(item); return item; |
Entities have similar lifecycle callbacks to session and message-driven beans like PostContruct and PreDestroy, you can receive callbacks for lifecycle events like persist, load, update and remove, you could invoke an EJB, or use JMS. Most of the time you will use callbacks for logging, validating data, auditing, sending notifications or generating data after an entity has been loaded, these callbacks are the database triggers of the JPA world. All entity listeners are stateless in nature and you cannot assume that there is a one-to-one relationship between an entity instance and a listener instance.
Lifecycle Method | When it is performed |
PrePersist | Before the EntityManager persists an entity instance |
PostPersist | After an entity has been persisted |
PostLoad | After an entity has been loaded by a query, find, or refresh operation |
PreUpdate | Before a database update occurs to synchronize an entity instance |
PostUpdate | After a database update occurs to synchronize an entity instance |
PreRemove | Before EntityManager removes an entity |
PostRemove | After an entity has been removed |
Now for an example of a entity listener
Listener | public class ItemMonitor { ... public ItemMonitor() {} @PrePersist @PreUpdate public void monitorItem(Item item) { if (item.getInitialBidAmount() > ItemMonitor.MONTORING_THRESHOLD) { notificationManager.sendItemPriceEmailAlert(item); } } } |
Entity | @Entity @EntityListeners(actionbazaar.persistence.ItemMonitor.class) public class Item implements Serializable { ... |
Sometimes you may want a default listener (for crosscutting) here you create your listener than you have to use the persistence file to implement it, there is no annotations to perform this
Listener | public class ActionBazaarAuditor { .. @PrePersist @PostPersist ... @PostRemove public void logOperation(Object object) { Logger.log("Perform Persistence Operation on: " + object.getName() ); } } |
persistence.xml | <persistence-unit name="actionBazaar"> ... <default-entity-listeners>actionbazaar.persistence.ActionBazzarAuditor.class</default-entity-listeners> ... </persistence-unit> |
Listeners follow OO inheritance rules, thus the picture below details the listener execution order. If there is more than one listener in a particular level, then the execution order is determined by the order in which they are listed in the annotation or deployment descriptor.
You can exclude default and super class listeners if you so wish
exclude listeners | @Entity @ExcludeDefaultListeners @ExcludeSuperClassListeners @EntityListeners(actionbazaar.persistence.ItemMonitor.class) ... |
Here are some best practices, I am not going to go into too much details and therefore list them below, I will be updating this list as I become more experienced.