Hibernate.orgCommunity Documentation
Hibernate is a full object/relational mapping solution that not only
shields the developer from the details of the underlying database management
system, but also offers state management of objects.
This is, contrary to the management of SQL statements
in
common JDBC/SQL persistence layers, a natural object-oriented view of
persistence in Java applications.
In other words, Hibernate application developers should always think about the state of their objects, and not necessarily about the execution of SQL statements. This part is taken care of by Hibernate and is only relevant for the application developer when tuning the performance of the system.
DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);
Alternatively, you can assign the identifier using an overloaded
version of save()
.
DomesticCat pk = new DomesticCat();
pk.setColor(Color.TABBY);
pk.setSex('F');
pk.setName("PK");
pk.setKittens( new HashSet() );
pk.addKitten(fritz);
sess.save( pk, new Long(1234) );
Cat fritz = (Cat) sess.load(Cat.class, generatedId);
// you need to wrap primitive identifiers
long id = 1234;
DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );
Alternatively, you can load state into a given instance:
Cat cat = new DomesticCat();
// load pk's state into cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();
Cat cat = (Cat) sess.get(Cat.class, id);
if (cat==null) {
cat = new Cat();
sess.save(cat, id);
}
return cat;
Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);
sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)
How much does Hibernate load from the database and how many SQL
SELECT
s will it use? This depends on the
fetching strategy. This is explained in Section 21.1, “Fetching strategies”.
List cats = session.createQuery(
"from Cat as cat where cat.birthdate < ?")
.setDate(0, date)
.list();
List mothers = session.createQuery(
"select mother from Cat as cat join cat.mother as mother where cat.name = ?")
.setString(0, name)
.list();
List kittens = session.createQuery(
"from Cat as cat where cat.mother = ?")
.setEntity(0, pk)
.list();
Cat mother = (Cat) session.createQuery(
"select cat.mother from Cat as cat where cat = ?")
.setEntity(0, izi)
.uniqueResult();]]
Query mothersWithKittens = (Cat) session.createQuery(
"select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());
Hibernate queries sometimes return tuples of objects. Each tuple is returned as an array:
Iterator kittensAndMothers = sess.createQuery(
"select kitten, mother from Cat kitten join kitten.mother mother")
.list()
.iterator();
while ( kittensAndMothers.hasNext() ) {
Object[] tuple = (Object[]) kittensAndMothers.next();
Cat kitten = (Cat) tuple[0];
Cat mother = (Cat) tuple[1];
....
}
//named parameter (preferred)
Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
q.setString("name", "Fritz");
Iterator cats = q.iterate();
//positional parameter
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
Iterator cats = q.iterate();
//named parameter list
List names = new ArrayList();
names.add("Izi");
names.add("Fritz");
Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
q.setParameterList("namesList", names);
List cats = q.list();
Query q = sess.createQuery("from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();
Hibernate knows how to translate this limit query into the native SQL of your DBMS.
Queries can also be configured as so called named queries using
annotations or Hibernate mapping documents.
@NamedQuery
and @NamedQueries
can be defined at the class level as seen in Example 11.1, “Defining a named query using
@NamedQuery” . However their
definitions are global to the session factory/entity manager factory
scope. A named query is defined by its name and the actual query
string.
Using a mapping document can be configured using the
<query>
node. Remember to use a
CDATA
section if your query contains characters
that could be interpreted as markup.
Example 11.2. Defining a named query using
<query>
<query name="ByNameAndMaximumWeight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query>
Parameter binding and executing is done programatically as seen in Example 11.3, “Parameter binding of a named query”.
The actual program code is independent of the query language that is used. You can also define native SQL queries in metadata, or migrate existing queries to Hibernate by placing them in mapping files.
Also note that a query declaration inside a
<hibernate-mapping>
element requires a global
unique name for the query, while a query declaration inside a
<class>
element is made unique automatically
by prepending the fully qualified name of the class. For example
eg.Cat.ByNameAndMaximumWeight
.
Collection blackKittens = session.createFilter(
pk.getKittens(),
"where this.color = ?")
.setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
.list()
);
Collection blackKittenMates = session.createFilter(
pk.getKittens(),
"select this.mate where this.color = eg.Color.BLACK.intValue")
.list();
Even an empty filter query is useful, e.g. to load a subset of elements in a large collection:
Collection tenKittens = session.createFilter(
mother.getKittens(), "")
.setFirstResult(0).setMaxResults(10)
.list();
Criteria crit = session.createCriteria(Cat.class);
crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );
crit.setMaxResults(10);
List cats = crit.list();
The Criteria
and the associated
Example
API are discussed in more detail in Chapter 17, Criteria Queries.
List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10")
.addEntity("cat", Cat.class)
.list();
List cats = session.createSQLQuery(
"SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
"{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
"FROM CAT {cat} WHERE ROWNUM<10")
.addEntity("cat", Cat.class)
.list()
SQL queries can contain named and positional parameters, just like Hibernate queries. More information about native SQL queries in Hibernate can be found in Chapter 18, Native SQL.
// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);
// in a higher layer of the application
cat.setMate(potentialMate);
// later, in a new session
secondSession.update(cat); // update cat
secondSession.update(mate); // update mate
The application should individually update()
detached instances that are reachable from the given detached instance
only if it wants their state to be updated. This can
be automated using transitive persistence. See Section 11.11, “Transitive persistence” for more information.
The lock()
method also allows an application to
reassociate an object with a new session. However, the detached instance
has to be unmodified.
//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);
Note that lock()
can be used with various
LockMode
s. See the API documentation and the chapter on
transaction handling for more information. Reattachment is not the only
usecase for lock()
.
Other models for long units of work are discussed in Section 13.3, “Optimistic concurrency control”.
// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catID);
// in a higher tier of the application
Cat mate = new Cat();
cat.setMate(mate);
// later, in a new session
secondSession.saveOrUpdate(cat); // update existing state (cat has a non-null id)
secondSession.saveOrUpdate(mate); // save the new instance (mate has a null id)
Usually update()
or
saveOrUpdate()
are used in the following
scenario:
saveOrUpdate()
does the following:
//retrieve a cat from one database
Session session1 = factory1.openSession();
Transaction tx1 = session1.beginTransaction();
Cat cat = session1.get(Cat.class, catId);
tx1.commit();
session1.close();
//reconcile with a second database
Session session2 = factory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();
The SQL statements are issued in the following order:
An exception is that objects using native
ID
generation are inserted when they are saved.
It is possible to change the default behavior so that flush occurs
less frequently. The FlushMode
class defines three
different modes: only flush at commit time when the Hibernate
Transaction
API is used, flush automatically using the
explained routine, or never flush unless flush()
is
called explicitly. The last mode is useful for long running units of work,
where a Session
is kept open and disconnected for a
long time (see Section 13.3.2, “Extended session and automatic versioning”).
sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state
Cat izi = (Cat) sess.load(Cat.class, id);
izi.setName(iznizi);
// might return stale data
sess.find("from Cat as cat left outer join cat.kittens kitten");
// change to izi is not flushed!
...
tx.commit(); // flush occurs
sess.close();
During flush, an exception might occur (e.g. if a DML operation violates a constraint). Since handling exceptions involves some understanding of Hibernate's transactional behavior, we discuss it in Chapter 13, Transactions and Concurrency.
<one-to-one name="person" cascade="persist"/>
Cascade styles my be combined:
<one-to-one name="person" cascade="persist,delete,lock"/>
A special cascade style, delete-orphan
, applies
only to one-to-many associations, and indicates that the
delete()
operation should be applied to any child
object that is removed from the association. Using annotations there is no
CascadeType.DELETE-ORPHAN
equivalent. Instead you can
use the attribute orphanRemoval as seen in
Example 11.4, “@OneToMany with
orphanRemoval”. If an entity is
removed from a @OneToMany
collection or an
associated entity is dereferenced from a @OneToOne
association, this associated entity can be marked for deletion if
orphanRemoval
is set to true.
Recommendations:
It does not usually make sense to enable cascade on a
many-to-one or many-to-many association. In fact the
@ManyToOne
and @ManyToMany
don't
even offer a orphanRemoval
attribute. Cascading is
often useful for one-to-one and one-to-many associations.
If the child object's lifespan is bounded by the lifespan of the
parent object, make it a life cycle object by
specifying
cascade="all,delete-orphan"(
.@OneToMany(cascade=CascadeType.ALL,
orphanRemoval=true)
)
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,save-update"
.
Mapping an association (either a single valued association, or a
collection) with cascade="all"
marks the association as
a parent/child style relationship where
save/update/delete of the parent results in save/update/delete of the
child or children.
Furthermore, a mere reference to a child from a persistent parent
will result in save/update of the child. This metaphor is incomplete,
however. A child which becomes unreferenced by its parent is
not automatically deleted, except in the case of a
one-to-many association mapped with
cascade="delete-orphan"
. The precise semantics of
cascading operations for a parent/child relationship are as
follows:
If a parent is passed to persist()
, all
children are passed to persist()
If a parent is passed to merge()
, all
children are passed to merge()
If a parent is passed to save()
,
update()
or saveOrUpdate()
, all
children are passed to saveOrUpdate()
If a transient or detached child becomes referenced by a
persistent parent, it is passed to
saveOrUpdate()
If a parent is deleted, all children are passed to
delete()
If a child is dereferenced by a persistent parent,
nothing special happens - the application should
explicitly delete the child if necessary - unless
cascade="delete-orphan"
, in which case the
"orphaned" child is deleted.
Finally, note that cascading of operations can be applied to an
object graph at call time or at flush
time. All operations, if enabled, are cascaded to associated
entities reachable when the operation is executed. However,
save-update
and delete-orphan
are
transitive for all associated entities reachable during flush of the
Session
.
Copyright © 2004 Red Hat, Inc.