Hibernate.orgCommunity Documentation
Like in Hibernate (comparable terms in parentheses), an entity instance is in one of the following states:
New (transient): an entity is new if it has just been instantiated using the new operator, and it is not associated with a persistence context. It has no persistent representation in the database and no identifier value has been assigned.
Managed (persistent): a managed entity instance is an instance with a persistent identity that is currently associated with a persistence context.
Detached: the entity instance is an instance with a persistent identity that is no longer associated with a persistence context, usually because the persistence context was closed or the instance was evicted from the context.
Removed: a removed entity instance is an instance with a persistent identity, associated with a persistence context, but scheduled for removal from the database.
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();
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.
JPA queries can return tuples of objects if projection is used. Each result tuple is returned as an object array:
Iterator kittensAndMothers = sess.createQuery(
"select kitten, mother from Cat kitten join kitten.mother mother")
.getResultList()
.iterator();
while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = (Cat) tuple[0];
Cat mother = (Cat) tuple[1];
....
}
The criteria API provides a type-safe approach to projection results. Check out Section 9.2, “Tuple criteria queries”.
Queries may specify a particular property of an entity in the select clause, instead of an entity alias. You may call SQL aggregate functions as well. Returned non-transactional objects or aggregation results are considered "scalar" results and are not entities in persistent state (in other words, they are considered "read only"):
Iterator results = em.createQuery(
"select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
"group by cat.color")
.getResultList()
.iterator();
while ( results.hasNext() ) {
Object[] row = results.next();
Color type = (Color) row[0];
Date oldest = (Date) row[1];
Integer count = (Integer) row[2];
.....
}
Both named and positional query parameters are supported, the
Query
API offers several methods to bind arguments.
The JPA specification numbers positional parameters from one. Named
parameters are identifiers of the form :paramname
in the query string. Named parameters should be preferred, they are
more robust and easier to read and understand:
// Named parameter (preferred)
Query q = em.createQuery("select cat from DomesticCat cat where cat.name = :name");
q.setParameter("name", "Fritz");
List cats = q.getResultList();
// Positional parameter
Query q = em.createQuery("select cat from DomesticCat cat where cat.name = ?1");
q.setParameter(1, "Izi");
List cats = q.getResultList();
// Named parameter list
List names = new ArrayList();
names.add("Izi");
names.add("Fritz");
Query q = em.createQuery("select cat from DomesticCat cat where cat.name in (:namesList)");
q.setParameter("namesList", names);
List cats = q.list();
If you need to specify bounds upon your result set (the maximum number of rows you want to retrieve and/or the first row you want to retrieve), use the following methods:
Query q = em.createQuery("select cat from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.getResultList(); //return cats from the 20th position to 29th
Hibernate knows how to translate this limit query into the native SQL of your DBMS.
You may also define named queries through annotations:
@javax.persistence.NamedQuery(name="eg.DomesticCat.by.name.and.minimum.weight",
query="select cat from eg.DomesticCat as cat where cat.name = ?1 and cat.weight > ?2")
Parameters are bound programmatically to the named query, before it is executed:
Query q = em.createNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
q.setString(1, name);
q.setInt(2, minWeight);
List<?> cats = q.getResultList();
You can also use the slightly more type-safe approach:
Query q = em.createNamedQuery("eg.DomesticCat.by.name.and.minimum.weight", Cat.class);
q.setString(1, name);
q.setInt(2, minWeight);
List<Cat> cats = q.getResultList();
Note that the actual program code is independent of the query language that is used, you may also define native SQL queries in metadata, or use Hibernate's native facilities by placing them in XML mapping files.
You may express a query in SQL, using
createNativeQuery()
and let Hibernate take
care mapping from JDBC result sets to business objects. Use the
@SqlResultSetMapping
(please see the Hibernate
Annotations reference documentation on how to map a SQL resultset
mapping) or the entity mapping (if the column names of the query
result are the same as the names declared in the entity mapping;
remember that all entity columns have to be returned for this
mechanism to work):
@SqlResultSetMapping(name="getItem", entities =
@EntityResult(entityClass=org.hibernate.ejb.test.Item.class, fields= {
@FieldResult(name="name", column="itemname"),
@FieldResult(name="descr", column="itemdescription")
})
)
Query q = em.createNativeQuery("select name as itemname, descr as itemdescription from Item", "getItem");
item = (Item) q.getSingleResult(); //from a resultset
Query q = em.createNativeQuery("select * from Item", Item.class);
item = (Item) q.getSingleResult(); //from a class columns names match the mapping
For more information about scalar support in named queries, please refers to the Hibernate Annotations documentation
You can adjust the flush mode used when executing the query as well as define the lock mode used to load the entities.
Adjusting the flush mode is interesting when one must guaranty that a query execution will not trigger a flush operation. Most of the time you don't need to care about this.
Adjusting the lock mode is useful if you need to lock the objects returns by the query to a certain level.
query.setFlushMode(FlushModeType.COMMIT)
.setLockMode(LockModeType.PESSIMISTIC_READ);
If you want to use FlushMode.MANUAL
(ie the
Hibernate specific flush mode), you will need to use a query hint.
See below.
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:
Table 3.1. Hibernate query hints
Hint | Description |
---|---|
org.hibernate.timeout | Query timeout in seconds ( eg. new Integer(10) ) |
org.hibernate.fetchSize | Number of rows fetched by the JDBC driver per roundtrip ( eg. new Integer(50) ) |
org.hibernate.comment | Add a comment to the SQL query, useful for the DBA ( e.g. new String("fetch all orders in 1 statement") ) |
org.hibernate.cacheable | Whether or not a query is cacheable ( eg. new Boolean(true) ), defaults to false |
org.hibernate.cacheMode | Override the cache mode for this query ( eg. CacheMode.REFRESH ) |
org.hibernate.cacheRegion | Cache region of this query ( eg. new String("regionName") ) |
org.hibernate.readOnly | Entities retrieved by this query will be loaded in a read-only mode where Hibernate will never dirty-check them or make changes persistent ( eg. new Boolean(true) ), default to false |
org.hibernate.flushMode | Flush mode used for this query (useful to pass
Hibernate specific flush modes, in particular
MANUAL ). |
org.hibernate.cacheMode | Cache mode used for this query |
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:
the application loads an object in the first entity manager
the object is passed up to the presentation layer
some modifications are made to the object
the object is passed back down to the business logic layer
the application persists these modifications by calling
merge()
in a second entity manager
Here is the exact semantic of
merge()
:
if there is a managed instance with the same identifier currently associated with the persistence context, copy the state of the given object onto the managed instance
if there is no managed instance currently associated with the persistence context, try to load it from the database, or create a new managed instance
the managed instance is returned
the given instance does not become associated with the persistence context, it remains detached and is usually discarded
Merging in JPA is similar to the
saveOrUpdateCopy()
method in native Hibernate.
However, it is not the same as the saveOrUpdate()
method, the given instance is not reattached with the persistence
context, but a managed instance is returned by the
merge()
method.
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:
before query execution*
from
javax.persistence.EntityTransaction.commit()*
when EntityManager.flush()
is
called*
(*) if a transaction is active
The SQL statements are issued in the following order
all entity insertions, in the same order the corresponding
objects were saved using
EntityManager.persist()
all entity updates
all collection deletions
all collection element deletions, updates and insertions
all collection insertions
all entity deletions, in the same order the corresponding
objects were deleted using
EntityManager.remove()
(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.
In an EXTENDED
persistence context, all read
only operations of the entity manager can be executed outside a
transaction (find()
,
getReference()
, refresh()
, and
read queries). Some modifications operations can be executed outside a
transaction, but they are queued until the persistence context join a
transaction. This is the case of persist()
,
,
merge()
remove()
. Some operations cannot be called outside a
transaction: flush()
, lock()
, and
update/delete queries.
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:
It doesn't usually make sense to enable cascade on a
@ManyToOne
or @ManyToMany
association. Cascade is often useful for @OneToOne
and @OneToMany
associations.
If the child object's lifespan is bounded by the lifespan of the
parent object, make the parent a full lifecycle object by specifying
CascadeType.ALL
and
org.hibernate.annotations.CascadeType.DELETE_ORPHAN
(please refer to the Hibernate reference guide for the semantics of
orphan delete)
Otherwise, you might not need cascade at all. But if you think
that you will often be working with the parent and children together
in the same transaction, and you want to save yourself some typing,
consider using cascade={PERSIST, MERGE}
. These options
can even make sense for a many-to-many association.
You can define various levels of locking strategies. A lock can be applied in several ways:
via the explicit entityManager.lock()
method
via lookup methods on EntityManager
:
find()
, refresh()
on queries: query.setLockMode()
You can use various lock approaches:
OPTIMISTIC
(previously
READ
): use an optimistic locking scheme where the
version number is compared: the version number is compared and has to
match before the transaction is committed.
OPTIMISTIC_FORCE_INCREMENT
(previously
WRITE
): use an optimistic locking scheme but force
a version number increase as well: the version number is compared and
has to match before the transaction is committed.
PESSIMISTIC_READ
: apply a database-level read
lock when the lock operation is requested: roughly concurrent readers
are allowed but no writer is allowed.
PESSIMISTIC_WRITE
: apply a database-level
write lock when the lock operation is requested: roughly no reader nor
writer is allowed.
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
valuesCacheRetrieveMode
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()
).
These are roughly the counterpart methods of
Hibernate.isInitialize
.
Copyright © 2005 Red Hat Inc. and the various authors