Hibernate.orgCommunity Documentation

Chapter 3. Working with objects

3.1. Entity states
3.2. Making objects persistent
3.3. Loading an object
3.4. Querying objects
3.4.1. Executing queries
3.5. Modifying persistent objects
3.6. Detaching a object
3.7. Modifying detached objects
3.8. Automatic state detection
3.9. Deleting managed objects
3.10. Flush the persistence context
3.10.1. In a transaction
3.10.2. Outside a transaction
3.11. Transitive persistence
3.12. Locking
3.13. Caching
3.14. Checking the state of an object
3.15. Native Hibernate API

Like in Hibernate (comparable terms in parentheses), an entity instance is in one of the following states:

The EntityManager API allows you to change the state of an entity, or in other words, to load and store objects. You will find persistence with JPA easier to understand if you think about object state management, not managing of SQL statements.

Once you've created a new entity instance (using the common new operator) it is in new state. You can make it persistent by associating it to an entity manager:

DomesticCat fritz = new DomesticCat();

fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
em.persist(fritz);

If the DomesticCat entity type has a generated identifier, the value is associated to the instance when persist() is called. If the identifier is not automatically generated, the application-assigned (usually natural) key value has to be set on the instance before persist() is called.

Load an entity instance by its identifier value with the entity manager's find() method:

cat = em.find(Cat.class, catId);


// You may need to wrap the primitive identifiers
long catId = 1234;
em.find( Cat.class, new Long(catId) );

In some cases, you don't really want to load the object state, but just having a reference to it (ie a proxy). You can get this reference using the getReference() method. This is especially useful to link a child to its parent without having to load the parent.

child = new Child();

child.SetName("Henry");
Parent parent = em.getReference(Parent.class, parentId); //no query to the DB
child.setParent(parent);
em.persist(child);

You can reload an entity instance and it's collections at any time using the em.refresh() operation. This is useful when database triggers are used to initialize some of the properties of the entity. Note that only the entity instance and its collections are refreshed unless you specify REFRESH as a cascade style of any associations:

em.persist(cat);

em.flush(); // force the SQL insert and triggers to run
em.refresh(cat); //re-read the state (after the trigger executes)

If you don't know the identifier values of the objects you are looking for, you need a query. The Hibernate EntityManager implementation supports an easy-to-use but powerful object-oriented query language (JP-QL) which has been inspired by HQL (and vice-versa). HQL is strictly speaking a superset of JP-QL. Both query languages are portable across databases, the use entity and property names as identifiers (instead of table and column names). You may also express your query in the native SQL of your database, with optional support from JPA for result set conversion into Java business objects.

JP-QL and SQL queries are represented by an instance of javax.persistence.Query. This interface offers methods for parameter binding, result set handling, and for execution of the query. Queries are always created using the current entity manager:

List<?> cats = em.createQuery(

    "select cat from Cat as cat where cat.birthdate < ?1")
    .setParameter(1, date, TemporalType.DATE)
    .getResultList();
List<?> mothers = em.createQuery(
    "select mother from Cat as cat join cat.mother as mother where cat.name = ?1")
    .setParameter(1, name)
    .getResultList();
List<?> kittens = em.createQuery(
    "from Cat as cat where cat.mother = ?1")
    .setEntity(1, pk)
    .getResultList();
Cat mother = (Cat) em.createQuery(
    "select cat.mother from Cat as cat where cat = ?1")
    .setParameter(1, izi)
    .getSingleResult();

A query is usually executed by invoking getResultList(). This method loads the resulting instances of the query completely into memory. Entity instances retrieved by a query are in persistent state. The getSingleResult() method offers a shortcut if you know your query will only return a single object.

JPA 2 provides more type-safe approaches to queries. The truly type-safe approach is the Criteria API explained in Chapter 9, Criteria Queries.

CriteriaQuery<Cat> criteria = builder.createQuery( Cat.class );

Root<Cat> cat = criteria.from( Cat.class );
criteria.select( cat );
criteria.where( builder.lt( cat.get( Cat_.birthdate ), catDate ) );
List<Cat> cats = em.createQuery( criteria ).getResultList(); //notice no downcasting is necessary

But you can benefit form some type-safe convenience even when using JP-QL (note that it's not as type-safe as the compiler has to trust you with the return type.

//No downcasting since we pass the return type

List<Cat> cats = em.createQuery(
    "select cat from Cat as cat where cat.birthdate < ?1", Cat.class)
    .setParameter(1, date, TemporalType.DATE)
    .getResultList();

Note

We highly recommend the Criteria API approach. While more verbose, it provides compiler-enforced safety (including down to property names) which will pay off when the application will move to maintenance mode.

Query hints (for performance optimization, usually) are implementation specific. Hints are declared using the query.setHint(String name, Object value) method, or through the @Named(Native)Query(hints) annotation Note that these are not SQL query hints! The Hibernate EJB3 implementation offers the following query hints:


The value object accept both the native type or its string equivalent (eg. CaheMode.REFRESH or “REFRESH”). Please refer to the Hibernate reference documentation for more information.

Transactional managed instances (ie. objects loaded, saved, created or queried by the entity manager) may be manipulated by the application and any changes to persistent state will be persisted when the Entity manager is flushed (discussed later in this chapter). There is no need to call a particular method to make your modifications persistent. A straightforward wayt to update the state of an entity instance is to find() it, and then manipulate it directly, while the persistence context is open:

Cat cat = em.find( Cat.class, new Long(69) );

cat.setName("PK");
em.flush();  // changes to cat are automatically detected and persisted

Sometimes this programming model is inefficient since it would require both an SQL SELECT (to load an object) and an SQL UPDATE (to persist its updated state) in the same session. Therefore Hibernate offers an alternate approach, using detached instances.

An object when loaded in the persistence context is managed by Hibernate. You can force an object to be detached (ie. no longer managed by Hibernate) by closing the EntityManager or in a more fine-grained approach by calling the detach() method.

Cat cat = em.find( Cat.class, new Long(69) );

...
em.detach(cat);
cat.setName("New name"); //not propatated to the database

Many applications need to retrieve an object in one transaction, send it to the presentation layer for manipulation, and later save the changes in a new transaction. There can be significant user think and waiting time between both transactions. Applications that use this kind of approach in a high-concurrency environment usually use versioned data to ensure isolation for the "long" unit of work.

The JPA specifications supports this development model by providing for persistence of modifications made to detached instances using the EntityManager.merge() method:

// in the first entity manager

Cat cat = firstEntityManager.find(Cat.class, catId);
Cat potentialMate = new Cat();
firstEntityManager.persist(potentialMate);
// in a higher layer of the application
cat.setMate(potentialMate);
// later, in a new entity manager
secondEntityManager.merge(cat);  // update cat
secondEntityManager.merge(mate); // update mate

The merge() method merges modifications made to the detached instance into the corresponding managed instance, if any, without consideration of the state of the persistence context. In other words, the merged objects state overrides the persistent entity state in the persistence context, if one is already present. The application should individually merge() detached instances reachable from the given detached instance if and only if it wants their state also to be persistent. This can be cascaded to associated entities and collections, using transitive persistence, see Transitive persistence.

The merge operation is clever enough to automatically detect whether the merging of the detached instance has to result in an insert or update. In other words, you don't have to worry about passing a new instance (and not a detached instance) to merge(), the entity manager will figure this out for you:

// In the first entity manager

Cat cat = firstEntityManager.find(Cat.class, catID);
// In a higher layer of the application, detached
Cat mate = new Cat();
cat.setMate(mate);
// Later, in a new entity manager
secondEntityManager.merge(cat);   // update existing state
secondEntityManager.merge(mate);  // save the new instance

The usage and semantics of merge() seems to be confusing for new users. Firstly, as long as you are not trying to use object state loaded in one entity manager in another new entity manager, you should not need to use merge() at all. Some whole applications will never use this method.

Usually merge() is used in the following scenario:

Here is the exact semantic of merge():

EntityManager.remove() will remove an objects state from the database. Of course, your application might still hold a reference to a deleted object. You can think of remove() as making a persistent instance new (aka transient) again. It is not detached, and a merge would result in an insertion.

From time to time the entity manager will execute the SQL DML statements needed to synchronize the data store with the state of objects held in memory. This process is called flushing.

Flush occurs by default (this is Hibernate specific and not defined by the specification) at the following points:

(*) if a transaction is active

The SQL statements are issued in the following order

(Exception: entity instances using application-assigned identifiers are inserted when they are saved.)

Except when you explicitly flush(), there are no guarantees about when the entity manager executes the JDBC calls, only the order in which they are executed. However, Hibernate does guarantee that the Query.getResultList()/Query.getSingleResult() will never return stale data; nor will they return wrong data if executed in an active transaction.

It is possible to change the default behavior so that flush occurs less frequently. The FlushModeType for an entity manager defines two different modes: only flush at commit time or flush automatically using the explained routine unless flush() is called explicitly.

em = emf.createEntityManager();

Transaction tx = em.getTransaction().begin();
em.setFlushMode(FlushModeType.COMMIT); // allow queries to return stale state
Cat izi = em.find(Cat.class, id);
izi.setName(iznizi);
// might return stale data
em.createQuery("from Cat as cat left outer join cat.kittens kitten").getResultList();
// change to izi is not flushed!
...
em.getTransaction().commit(); // flush occurs

During flush, an exception might happen (e.g. if a DML operation violates a constraint). TODO: Add link to exception handling.

Hibernate provides more flush modes than the one described in the JPA specification. In particular FlushMode.MANUAL for long running conversation. Please refer to the Hibernate core reference documentation for more informations.

It is quite cumbersome to save, delete, or reattach individual objects, especially if you deal with a graph of associated objects. A common case is a parent/child relationship. Consider the following example:

If the children in a parent/child relationship would be value typed (e.g. a collection of addresses or strings), their lifecycle would depend on the parent and no further action would be required for convenient "cascading" of state changes. When the parent is persisted, the value-typed child objects are persisted as well, when the parent is removed, the children will be removed, etc. This even works for operations such as the removal of a child from the collection; Hibernate will detect this and, since value-typed objects can't have shared references, remove the child from the database.

Now consider the same scenario with parent and child objects being entities, not value-types (e.g. categories and items, or parent and child cats). Entities have their own lifecycle, support shared references (so removing an entity from the collection does not mean it can be deleted), and there is by default no cascading of state from one entity to any other associated entities. The EJB3 specification does not require persistence by reachability. It supports a more flexible model of transitive persistence, as first seen in Hibernate.

For each basic operation of the entity manager - including persist(), merge(), remove(), refresh() - there is a corresponding cascade style. Respectively, the cascade styles are named PERSIST, MERGE, REMOVE, REFRESH, DETACH. If you want an operation to be cascaded to associated entity (or collection of entities), you must indicate that in the association annotation:

@OneToOne(cascade=CascadeType.PERSIST)

Cascading options can be combined:

@OneToOne(cascade= { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH } )

You may even use CascadeType.ALL to specify that all operations should be cascaded for a particular association. Remember that by default, no operation is cascaded.

There is an additional cascading mode used to describe orphan deletion (ie an object no longer linked to an owning object should be removed automatically by Hibernate. Use orphanRemoval=true on @OneToOne or @OneToMany. Check Hibernate Annotations's documentation for more information.

Hibernate offers more native cascading options, please refer to the Hibernate Annotations manual and the Hibernate reference guide for more informations.

Recommendations:

You can define various levels of locking strategies. A lock can be applied in several ways:

You can use various lock approaches:

All these locks prevent dirty reads and non-repeatable reads on a given entity. Optimistic locks enforce the lock as late as possible hoping nobody changes the data underneath while pessimistic locks enforce the lock right away and keep it till the transaction is committed.

When the second-level cache is activated (see Section 2.2.1, “Packaging” and the Hibernate Annotations reference documentation), Hibernate ensures it is used and properly updated. You can however adjust these settings by passing two properties:

  • javax.persistence.cache.retrieveMode which accepts CacheRetrieveMode values

  • javax.persistence.cache.storeMode which accepts CacheStoreMode values

CacheRetrieveMode controls how Hibernate accesses information from the second-level cache: USE which is the default or BYPASS which means ignore the cache. CacheStoreMode controls how Hibernate pushes information to the second-level cache: USE which is the default and push data in the cache when reading from and writing to the database, BYPASS which does not insert new data in the cache (but can invalidate obsolete data) and REFRESH which does like default but also force data to be pushed to the cache on database read even if the data is already cached.

You can set these properties:

  • on a particular EntityManager via the setProperty method

  • on a query via a query hint (setHint method)

  • when calling find() and refresh() and passing the properties in the appropriate Map

JPA also introduces an API to interrogate the second-level cache and evict data manually.

Cache cache = entityManagerFactory.getCache();


if ( cache.contains(User.class, userId) ) {
   //load it as we don't hit the DB
}
cache.evict(User.class, userId); //manually evict user form the second-level cache
cache.evict(User.class); //evict all users from the second-level cache
cache.evictAll(); //purge the second-level cache entirely

You can check whether an object is managed by the persistence context

entityManager.get(Cat.class, catId);

...
boolean isIn = entityManager.contains(cat);
assert isIn;

You can also check whether an object, an association or a property is lazy or not. You can do that independently of the underlying persistence provider:

PersistenceUtil jpaUtil = Persistence.getPersistenceUtil();

if ( jpaUtil.isLoaded( customer.getAddress() ) {
   //display address if loaded
}
if ( jpaUtil.isLoaded( customer.getOrders ) ) {
   //display orders if loaded
}
if (jpaUtil.isLoaded(customer, "detailedBio") ) {
   //display property detailedBio if loaded
}

However, if you have access to the entityManagerFactory, we recommend you to use:

PersistenceUnitUtil jpaUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();


Customer customer = entityManager.get( Customer.class, customerId );
if ( jpaUtil.isLoaded( customer.getAddress() ) {
   //display address if loaded
}
if ( jpaUtil.isLoaded( customer.getOrders ) ) {
   //display orders if loaded
}
if (jpaUtil.isLoaded(customer, "detailedBio") ) {
   //display property detailedBio if loaded
}
log.debug( "Customer id {}", jpaUtil.getIdentifier(customer) );

The performances are likely to be slightly better and you can get the identifier value from an object (using getIdentifier()).

You can always fall back to the underlying Session API from a given EntityManager:

Session session = entityManager.unwrap(Session.class);