|
Hibernate example source code file (entitymanagerapi.xml)
This example Hibernate source code file (entitymanagerapi.xml) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Hibernate entitymanagerapi.xml source code
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2008, Red Hat Inc or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Inc.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="objectstate">
<title>Working with objects
<section>
<title>Entity states
<para>Like in Hibernate (comparable terms in parentheses), an entity
instance is in one of the following states:</para>
<itemizedlist>
<listitem>
<para>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.</para>
</listitem>
<listitem>
<para>Managed (persistent): a managed entity instance is an instance
with a persistent identity that is currently associated with a
persistence context.</para>
</listitem>
<listitem>
<para>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.</para>
</listitem>
<listitem>
<para>Removed: a removed entity instance is an instance with a
persistent identity, associated with a persistence context, but
scheduled for removal from the database.</para>
</listitem>
</itemizedlist>
<para>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.</para>
</section>
<section>
<title>Making objects persistent
<para>Once you've created a new entity instance (using the common
<literal>new operator) it is in new state.
You can make it persistent by associating it to an entity manager:</para>
<programlisting language="JAVA" role="JAVA">DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
em.persist(fritz);</programlisting>
<para>If the DomesticCat entity type has a generated
identifier, the value is associated to the instance when
<code>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 <code>persist() is called.
</section>
<section>
<title>Loading an object
<para>Load an entity instance by its identifier value with the entity
manager's <code>find() method:
<programlisting language="JAVA" role="JAVA">cat = em.find(Cat.class, catId);
// You may need to wrap the primitive identifiers
long catId = 1234;
em.find( Cat.class, new Long(catId) );</programlisting>
<para>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 <literal>getReference() method. This is especially
useful to link a child to its parent without having to load the
parent.</para>
<programlisting language="JAVA" role="JAVA">child = new Child();
child.SetName("Henry");
Parent parent = em.getReference(Parent.class, parentId); //no query to the DB
child.setParent(parent);
em.persist(child);</programlisting>
<para>You can reload an entity instance and it's collections at any time
using the <code>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 <literal>REFRESH as a cascade style
of any associations:</para>
<programlisting language="JAVA" role="JAVA">em.persist(cat);
em.flush(); // force the SQL insert and triggers to run
em.refresh(cat); //re-read the state (after the trigger executes)</programlisting>
</section>
<section>
<title>Querying objects
<para>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.</para>
<section>
<title>Executing queries
<para>JP-QL and SQL queries are represented by an instance of
<classname>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:</para>
<programlisting language="JAVA" role="JAVA">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();</programlisting>
<para>A query is usually executed by invoking
<methodname>getResultList(). This method loads the
resulting instances of the query completely into memory. Entity
instances retrieved by a query are in persistent state. The
<methodname>getSingleResult() method offers a shortcut if
you know your query will only return a single object.</para>
<para>JPA 2 provides more type-safe approaches to queries. The truly
type-safe approach is the Criteria API explained in <xref
linkend="querycriteria" />.</para>
<programlisting language="JAVA" role="JAVA">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</programlisting>
<para>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.</para>
<programlisting language="JAVA" role="JAVA">//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();</programlisting>
<note>
<para>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.</para>
</note>
<section>
<title>Projection
<para>JPA queries can return tuples of objects if projection is used.
Each result tuple is returned as an object array:</para>
<programlisting language="JAVA" role="JAVA">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];
....
}</programlisting>
<note>
<para>The criteria API provides a type-safe approach to projection
results. Check out <xref linkend="querycriteria-tuple" />.
</note>
</section>
<section>
<title>Scalar results
<para>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"):</para>
<programlisting language="JAVA" role="JAVA">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];
.....
}</programlisting>
</section>
<section>
<title>Bind parameters
<para>Both named and positional query parameters are supported, the
<literal>Query API offers several methods to bind arguments.
The JPA specification numbers positional parameters from one. Named
parameters are identifiers of the form <literal>:paramname
in the query string. Named parameters should be preferred, they are
more robust and easier to read and understand:</para>
<programlisting language="JAVA" role="JAVA">// 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();</programlisting>
</section>
<section>
<title>Pagination
<para>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:</para>
<programlisting language="JAVA" role="JAVA">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</programlisting>
<para>Hibernate knows how to translate this limit query into the
native SQL of your DBMS.</para>
</section>
<section>
<title>Externalizing named queries
<para>You may also define named queries through annotations:
<programlisting language="JAVA" role="JAVA">@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")</programlisting>
<para>Parameters are bound programmatically to the named query, before
it is executed:</para>
<programlisting language="JAVA" role="JAVA">Query q = em.createNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
q.setString(1, name);
q.setInt(2, minWeight);
List<?> cats = q.getResultList();</programlisting>
<para>You can also use the slightly more type-safe approach:
<programlisting language="JAVA" role="JAVA">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();</programlisting>
<para>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.</para>
</section>
<section>
<title>Native queries
<para>You may express a query in SQL, using
<methodname>createNativeQuery() and let Hibernate take
care mapping from JDBC result sets to business objects. Use the
<literal>@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):</para>
<programlisting language="JAVA" role="JAVA">@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</programlisting>
<note>
<para>For more information about scalar support in named queries,
please refers to the Hibernate Annotations documentation</para>
</note>
</section>
<section>
<title>Query lock and flush mode
<para>You can adjust the flush mode used when executing the query as
well as define the lock mode used to load the entities.</para>
<para>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.</para>
<para>Adjusting the lock mode is useful if you need to lock the
objects returns by the query to a certain level.</para>
<programlisting language="JAVA" role="JAVA">query.setFlushMode(FlushModeType.COMMIT)
.setLockMode(LockModeType.PESSIMISTIC_READ);</programlisting>
<note>
<para>If you want to use FlushMode.MANUAL (ie the
Hibernate specific flush mode), you will need to use a query hint.
See below.</para>
</note>
</section>
<section>
<title>Query hints
<para>Query hints (for performance optimization, usually) are
implementation specific. Hints are declared using the
<methodname>query.setHint(String name, Object value)
method, or through the <literal>@Named(Native)Query(hints)
annotation Note that these are not SQL query hints! The Hibernate EJB3
implementation offers the following query hints:</para>
<table>
<title>Hibernate query hints
<tgroup cols="2">
<thead>
<colspec colname="C1" colwidth="1*" />
<colspec colname="c2" colwidth="3*" />
<row>
<entry align="center">Hint
<entry align="center">Description
</row>
</thead>
<tbody>
<row>
<entry>org.hibernate.timeout
<entry>Query timeout in seconds ( eg. new Integer(10)
)</entry>
</row>
<row>
<entry>org.hibernate.fetchSize
<entry>Number of rows fetched by the JDBC driver per roundtrip
( eg. new Integer(50) )</entry>
</row>
<row>
<entry>org.hibernate.comment
<entry>Add a comment to the SQL query, useful for the DBA (
e.g. new String("fetch all orders in 1 statement") )</entry>
</row>
<row>
<entry>org.hibernate.cacheable
<entry>Whether or not a query is cacheable ( eg. new
Boolean(true) ), defaults to false</entry>
</row>
<row>
<entry>org.hibernate.cacheMode
<entry>Override the cache mode for this query ( eg.
CacheMode.REFRESH )</entry>
</row>
<row>
<entry>org.hibernate.cacheRegion
<entry>Cache region of this query ( eg. new
String("regionName") )</entry>
</row>
<row>
<entry>org.hibernate.readOnly
<entry>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</entry>
</row>
<row>
<entry>org.hibernate.flushMode
<entry>Flush mode used for this query (useful to pass
Hibernate specific flush modes, in particular
<literal>MANUAL).
</row>
<row>
<entry>org.hibernate.cacheMode
<entry>Cache mode used for this query
</row>
</tbody>
</tgroup>
</table>
<para>The value object accept both the native type or its string
equivalent (eg. <literal>CaheMode.REFRESH or
<quote>REFRESH). Please refer to the
Hibernate reference documentation for more information.</para>
</section>
</section>
</section>
<section>
<title>Modifying persistent objects
<para>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 way to update the state of an entity instance is to
<methodname>find() it, and then manipulate it directly, while
the persistence context is open:</para>
<programlisting language="JAVA" role="JAVA">Cat cat = em.find( Cat.class, new Long(69) );
cat.setName("PK");
em.flush(); // changes to cat are automatically detected and persisted</programlisting>
<para>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.</para>
</section>
<section>
<title>Detaching a object
<para>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 <methodname>detach() method.
<programlisting language="JAVA" role="JAVA">Cat cat = em.find( Cat.class, new Long(69) );
...
em.detach(cat);
cat.setName("New name"); //not propatated to the database</programlisting>
</section>
<section>
<title>Modifying detached objects
<para>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.</para>
<para>The JPA specifications supports this development model by providing
for persistence of modifications made to detached instances using the
<methodname>EntityManager.merge() method:
<programlisting language="JAVA" role="JAVA">// 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</programlisting>
<para>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 <methodname>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 <xref
linkend="objectstate-transitive" />.</para>
</section>
<section>
<title>Automatic state detection
<para>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 <literal>merge(), the entity manager
will figure this out for you:</para>
<programlisting language="JAVA" role="JAVA">// 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</programlisting>
<para>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 <methodname>merge() at all. Some
whole applications will never use this method.</para>
<para>Usually merge() is used in the following
scenario:</para>
<itemizedlist>
<listitem>
<para>the application loads an object in the first entity
manager</para>
</listitem>
<listitem>
<para>the object is passed up to the presentation layer
</listitem>
<listitem>
<para>some modifications are made to the object
</listitem>
<listitem>
<para>the object is passed back down to the business logic
layer</para>
</listitem>
<listitem>
<para>the application persists these modifications by calling
<methodname>merge() in a second entity manager
</listitem>
</itemizedlist>
<para>Here is the exact semantic of
<methodname>merge():
<itemizedlist>
<listitem>
<para>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</para>
</listitem>
<listitem>
<para>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</para>
</listitem>
<listitem>
<para>the managed instance is returned
</listitem>
<listitem>
<para>the given instance does not become associated with the
persistence context, it remains detached and is usually
discarded</para>
</listitem>
</itemizedlist>
<note>
<title>Merging vs. saveOrUpdate/saveOrUpdateCopy
<para>Merging in JPA is similar to the
<literal>saveOrUpdateCopy() method in native Hibernate.
However, it is not the same as the <literal>saveOrUpdate()
method, the given instance is not reattached with the persistence
context, but a managed instance is returned by the
<methodname>merge() method.
</note>
</section>
<section>
<title>Deleting managed objects
<para>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
<methodname>remove() as making a persistent instance new (aka
transient) again. It is not detached, and a merge would result in an
insertion.</para>
</section>
<section>
<title>Flush the persistence context
<para>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.</para>
<section>
<title>In a transaction
<para>Flush occurs by default (this is Hibernate specific and not
defined by the specification) at the following points:</para>
<itemizedlist>
<listitem>
<para>before query execution*
</listitem>
<listitem>
<para>from
<methodname>javax.persistence.EntityTransaction.commit()*
</listitem>
<listitem>
<para>when EntityManager.flush() is
called*</para>
</listitem>
</itemizedlist>
<para>(*) if a transaction is active
<para>The SQL statements are issued in the following order
<itemizedlist>
<listitem>
<para>all entity insertions, in the same order the corresponding
objects were saved using
<methodname>EntityManager.persist()
</listitem>
<listitem>
<para>all entity updates
</listitem>
<listitem>
<para>all collection deletions
</listitem>
<listitem>
<para>all collection element deletions, updates and
insertions</para>
</listitem>
<listitem>
<para>all collection insertions
</listitem>
<listitem>
<para>all entity deletions, in the same order the corresponding
objects were deleted using
<methodname>EntityManager.remove()
</listitem>
</itemizedlist>
<para>(Exception: entity instances using application-assigned
identifiers are inserted when they are saved.)</para>
<para>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
<methodname>Query.getResultList()/Query.getSingleResult()
will never return stale data; nor will they return wrong data if
executed in an active transaction.</para>
<para>It is possible to change the default behavior so that flush occurs
less frequently. The <classname>FlushModeType for an entity
manager defines two different modes: only flush at commit time or flush
automatically using the explained routine unless
<methodname>flush() is called explicitly.
<programlisting language="JAVA" role="JAVA">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</programlisting>
<para>During flush, an exception might happen (e.g. if a DML operation
violates a constraint). TODO: Add link to exception handling.</para>
<para>Hibernate provides more flush modes than the one described in the
JPA specification. In particular <literal>FlushMode.MANUAL for
long running conversation. Please refer to the Hibernate core reference
documentation for more informations.</para>
</section>
<section>
<title>Outside a transaction
<para>In an EXTENDED persistence context, all read
only operations of the entity manager can be executed outside a
transaction (<literal>find(),
<literal>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 <literal>persist(),
<literal>merge(),
<literal>remove(). Some operations cannot be called outside a
transaction: <literal>flush(), lock(), and
update/delete queries.</para>
</section>
</section>
<section id="objectstate-transitive" revision="1"
xreflabel="Transitive persistence">
<title>Transitive persistence
<para>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:</para>
<para>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.</para>
<para>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.</para>
<para>For each basic operation of the entity manager - including
<methodname>persist(), merge(),
<methodname>remove(), refresh() -
there is a corresponding cascade style. Respectively, the cascade styles
are named <literal>PERSIST, MERGE,
<literal>REMOVE, REFRESH,
<literal>DETACH. If you want an operation to be cascaded to
associated entity (or collection of entities), you must indicate that in
the association annotation:</para>
<programlisting language="JAVA" role="JAVA">@OneToOne(cascade=CascadeType.PERSIST)
<para>Cascading options can be combined:
<programlisting language="JAVA" role="JAVA">@OneToOne(cascade= { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH } )
<para>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.</para>
<para>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
<literal>orphanRemoval=true on @OneToOne
or <classname>@OneToMany. Check Hibernate Annotations's
documentation for more information.</para>
<para>Hibernate offers more native cascading options, please refer to the
Hibernate Annotations manual and the Hibernate reference guide for more
informations.</para>
<para>Recommendations:
<itemizedlist>
<listitem>
<para>It doesn't usually make sense to enable cascade on a
<literal>@ManyToOne or @ManyToMany
association. Cascade is often useful for <literal>@OneToOne
and <literal>@OneToMany associations.
</listitem>
<listitem>
<para>If the child object's lifespan is bounded by the lifespan of the
parent object, make the parent a full lifecycle object by specifying
<literal>CascadeType.ALL and
<literal>org.hibernate.annotations.CascadeType.DELETE_ORPHAN
(please refer to the Hibernate reference guide for the semantics of
orphan delete)</para>
</listitem>
<listitem>
<para>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 <code>cascade={PERSIST, MERGE}. These options
can even make sense for a many-to-many association.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Locking
<para>You can define various levels of locking strategies. A lock can be
applied in several ways:</para>
<itemizedlist>
<listitem>
<para>via the explicit entityManager.lock()
method</para>
</listitem>
<listitem>
<para>via lookup methods on EntityManager:
<literal>find(), refresh()
</listitem>
<listitem>
<para>on queries: query.setLockMode()
</listitem>
</itemizedlist>
<para>You can use various lock approaches:
<itemizedlist>
<listitem>
<para>OPTIMISTIC (previously
<literal>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.</para>
</listitem>
<listitem>
<para>OPTIMISTIC_FORCE_INCREMENT (previously
<literal>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.</para>
</listitem>
<listitem>
<para>PESSIMISTIC_READ: apply a database-level read
lock when the lock operation is requested: roughly concurrent readers
are allowed but no writer is allowed.</para>
</listitem>
<listitem>
<para>PESSIMISTIC_WRITE: apply a database-level
write lock when the lock operation is requested: roughly no reader nor
writer is allowed.</para>
</listitem>
</itemizedlist>
<para>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.</para>
</section>
<section>
<title>Caching
<para>When the second-level cache is activated (see javax.persistence.cache.retrieveMode which
accepts <literal>CacheRetrieveMode
values</para>
</listitem>
<listitem>
<para>javax.persistence.cache.storeMode which
accepts <classname>CacheStoreMode values
</listitem>
</itemizedlist>
<para>CacheRetrieveMode controls how Hibernate
accesses information from the second-level cache: <literal>USE
which is the default or <literal>BYPASS which means ignore the
cache. <classname>CacheStoreMode controls how Hibernate pushes
information to the second-level cache: <literal>USE which is the
default and push data in the cache when reading from and writing to the
database, <literal>BYPASS which does not insert new data in the
cache (but can invalidate obsolete data) and
<classname>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.</para>
<para>You can set these properties:
<itemizedlist>
<listitem>
<para>on a particular EntityManager via the
<methodname>setProperty method
</listitem>
<listitem>
<para>on a query via a query hint (setHint
method)</para>
</listitem>
<listitem>
<para>when calling find() and
<methodname>refresh() and passing the properties in the
appropriate <classname>Map
</listitem>
</itemizedlist>
<para>JPA also introduces an API to interrogate the second-level cache and
evict data manually.</para>
<programlisting language="JAVA" role="JAVA">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</programlisting>
</section>
<section>
<title>Checking the state of an object
<para>You can check whether an object is managed by the persistence
context</para>
<programlisting language="JAVA" role="JAVA">entityManager.get(Cat.class, catId);
...
boolean isIn = entityManager.contains(cat);
assert isIn;</programlisting>
<para>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: </para>
<programlisting language="JAVA" role="JAVA">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
}</programlisting>
<para>However, if you have access to the entityManagerFactory, we
recommend you to use:</para>
<programlisting language="JAVA" role="JAVA">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) );</programlisting>
<para>The performances are likely to be slightly better and you can get
the identifier value from an object (using
<methodname>getIdentifier()).
<note>
<para>These are roughly the counterpart methods of
<methodname>Hibernate.isInitialize.
</note>
</section>
<section>
<title>Native Hibernate API
<para>You can always fall back to the underlying
<classname>Session API from a given
<classname>EntityManager:
<programlisting language="JAVA" role="JAVA">Session session = entityManager.unwrap(Session.class);
</section>
</chapter>
Other Hibernate examples (source code examples)
Here is a short list of links related to this Hibernate entitymanagerapi.xml source code file:
|