JPA : Entity Manager

As you read in the guide for EntityManagerFactory, to control the persistence of your objects you will require at least one EntityManagerFactory. Once you have obtained this object you then use this to obtain an EntityManager. An EntityManager provides access to the operations for persistence of your objects. This short guide will demonstrate some of the more common operations.

Important : An EntityManagerFactory is designed to be thread-safe. An EntityManager is not

You obtain an EntityManager from an EntityManagerFactory as follows

EntityManager em = emf.createEntityManager();

In the case of using container-managed JavaEE, you would instead obtain the EntityManager by injection

@PersistenceContext(unitName="myPU")
EntityManager em;

In general you will be performing all operations on a EntityManager within a transaction, whether your transactions are controlled by your JavaEE container, by a framework such as Spring, or by locally defined transactions. In the examples below we will omit the transaction demarcation for clarity.


Persisting an Object

The main thing that you will want to do with the data layer of a JPA-enabled application is persist your objects into the datastore. As we mentioned earlier, a EntityManagerFactory represents the datastore where the objects will be persisted. So you create a normal Java object in your application, and you then persist this as follows

em.persist(obj);

This will result in the object being persisted into the datastore, though clearly it will not be persistent until you commit the transaction. The LifecycleState of the object changes from Transient to PersistentClean (after persist()), to Hollow (at commit).

Persisting multiple Objects in one call

When you want to persist multiple objects with standard JPA you have to call persist multiple times. Fortunately DataNucleus extends this to take in a Collection or an array of entities, so you can do

em.persist(coll);

As above, the objects are persisted to the datastore. The LifecycleState of the objects change from Transient to PersistentClean (after persist()), to Hollow (at commit).

Finding an object by its identity

Once you have persisted an object, it has an "identity". This is a unique way of identifying it. When you specify the persistence for the class you specified an id class so you can create the identity from that. So what ? Well the identity can be used to retrieve the object again at some other part in your application. So you pass the identity into your application, and the user clicks on some button on a web page and that button corresponds to a particular object identity. You can then go back to your data layer and retrieve the object as follows

Object obj = em.find(cls, id);

where cls is the class of the object you want to find, and id is the identity. Note that the first argument could be a base class and the real object could be an instance of a subclass of that. Note that the second argument is either the value of the single primary-key field (when it has only one primary key field), or is the value of the object-id-class (when it has multiple primary key fields).


Deleting an Object

When you need to delete an object that you had previous persisted, deleting it is simple. Firstly you need to get the object itself, and then delete it as follows

Object obj = em.find(cls, id);  // Retrieves the object to delete
em.remove(obj);

Deleting multiple Objects

When you want to delete multiple objects with standard JPA you have to call remove multiple times. Fortunately DataNucleus extends this to take in a Collection or an array of entities, so you can do

Collection objsToRemove = new HashSet();
objsToRemove.add(obj1);
objsToRemove.add(obj2);
em.remove(objsToRemove);

Modifying a persisted Object

To modify a previously persisted object you take the object and update it in your code. When you are ready to persist the changes you do the following

Object updatedObj = em.merge(obj)

Modifying multiple persisted Objects

When you want to attach multiple modified objects with standard JPA you have to call merge multiple times. Fortunately DataNucleus extends this to take in a Collection or an array of entities, so you can do

Object updatedObj = em.merge(coll)

Refreshing a persisted Object

When you think that the datastore has more up-to-date values than the current values in a retrieved persisted object you can refresh the values in the object by doing the following

em.refresh(obj)

This will do the following

  • Refresh all fields that are to be eagerly fetched from the datastore
  • Unload all loaded fields that are to be lazily fetched.

If the object had any changes they will be thrown away by this step, and replaced by the latest datastore values.


Getting EntityManager for an object

JPA doesn't provide a method for getting the EntityManager of an object as such. Fortunately DataNucleus provides the following

EntityManager em = NucleusJPAHelper.getEntityManager(obj);

Level 1 Cache

Each EntityManager maintains a cache of the objects that it has encountered (or have been "enlisted") during its lifetime. This is termed the Level 1 Cache. It is enabled by default and you should only ever disable it if you really know what you are doing. There are inbuilt types for the Level 1 (L1) Cache available for selection. DataNucleus supports the following types of L1 Cache :-

  • weak - uses a weak reference backing map. If JVM garbage collection clears the reference, then the object is removed from the cache.
  • soft - uses a soft reference backing map. If the map entry value object is not being actively used, then garbage collection may garbage collect the reference, in which case the object is removed from the cache.
  • strong - uses a normal HashMap backing. With this option all references are strong meaning that objects stay in the cache until they are explicitly removed by calling remove() on the cache.

You can specify the type of L1 Cache by providing the persistence property datanucleus.cache.level1.type. You set this to the value of the type required. If you want to remove all objects from the L1 cache programmatically you should use em.clear() but bear in mind the other things that this will impact on.

Objects are placed in the L1 Cache (and updated there) during the course of the transaction. This provides rapid access to the objects in use in the users application and is used to guarantee that there is only one object with a particular identity at any one time for that EntityManager. When the EM is closed the cache is cleared.

The L1 cache is a DataNucleus plugin point allowing you to provide your own cache where you require it.