Hibernate.orgCommunity Documentation
Table of Contents
Both the org.hibernate.Session
API and
javax.persistence.EntityManager
API represent a context for dealing with
persistent data. This concept is called a persistence context
. Persistent data has a
state in relation to both a persistence context and the underlying database.
Entity states
transient
- the entity has just been instantiated and is
not associated with a persistence context. It has no persistent representation in the database and
typically no identifier value has been assigned.
managed
, or persistent
- the entity has an associated identifier
and is associated with a persistence context. It may or may not physically exist in the database
yet.
detached
- the entity has an associated identifier, but is no longer associated with
a persistence context (usually because the persistence context was closed or the instance was evicted
from the context)
removed
- the entity has an associated identifier and is associated with a persistence
context, however it is scheduled for removal from the database.
Much of the org.hibernate.Session
and
javax.persistence.EntityManager
methods deal with moving entities between these
states.
Once you've created a new entity instance (using the standard new
operator) it is in
new
state. You can make it persistent by associating it to either a
org.hibernate.Session
or
javax.persistence.EntityManager
Example 5.1. Example of making an entity persistent
// Using the Hibernate Session DomesticCat fritz = new DomesticCat(); fritz.setColor( Color.GINGER ); fritz.setSex( 'M' ); fritz.setName( "Fritz" ); session.save( fritz );
// Using the JPA EntityManager DomesticCat fritz = new DomesticCat(); fritz.setColor( Color.GINGER ); fritz.setSex( 'M' ); fritz.setName( "Fritz" ); entityManager.persist( fritz );
org.hibernate.Session
also has a method named persist
which follows the exact semantic defined in the JPA specification for the persist
method. It is this method on org.hibernate.Session
to which the
Hibernate javax.persistence.EntityManager
implementation delegates.
If the DomesticCat
entity type has a generated identifier, the value is associated
to the instance when the save
or 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 save
or persist
is called.
Entities can also be deleted.
It is important to note that Hibernate itself can handle deleting detached state. JPA, however, disallows
it. The implication here is that the entity instance passed to the
org.hibernate.Session
delete
method can be either
in managed or detached state, while the entity instance passed to remove
on
javax.persistence.EntityManager
must be in managed state.
Sometimes referred to as lazy loading, the ability to obtain a reference to an entity without having to load its data is hugely important. The most common case being the need to create an association between an entity and another, existing entity.
Example 5.3. Example of obtaining an entity reference without initializing its data
Book book = new Book(); book.setAuthor( session.byId( Author.class ).getReference( authorId ) );
Book book = new Book(); book.setAuthor( entityManager.getReference( Author.class, authorId ) );
The above works on the assumption that the entity is defined to allow lazy loading, generally through use of runtime proxies. For more information see ???. In both cases an exception will be thrown later if the given entity does not refer to actual database state if and when the application attempts to use the returned proxy in any way that requires access to its data.
It is also quite common to want to obtain an entity along with with its data, for display for example.
Example 5.4. Example of obtaining an entity reference with its data initialized
session.byId( Author.class ).load( authorId );
entityManager.find( Author.class, authorId );
In both cases null is returned if no matching database row was found.
In addition to allowing to load by identifier, Hibernate allows applications to load by declared natural identifier.
Example 5.5. Example of simple natural-id access
@Entity public class User { @Id @GeneratedValue Long id; @NaturalId String userName; ... } // use getReference() to create associations... Resource aResource = (Resource) session.byId( Resource.class ).getReference( 123 ); User aUser = (User) session.bySimpleNaturalId( User.class ).getReference( "steve" ); aResource.assignTo( user ); // use load() to pull initialzed data return session.bySimpleNaturalId( User.class ).load( "steve" );
Example 5.6. Example of natural-id access
import java.lang.String; @Entity public class User { @Id @GeneratedValue Long id; @NaturalId String system; @NaturalId String userName; ... } // use getReference() to create associations... Resource aResource = (Resource) session.byId( Resource.class ).getReference( 123 ); User aUser = (User) session.byNaturalId( User.class ) .using( "system", "prod" ) .using( "userName", "steve" ) .getReference(); aResource.assignTo( user ); // use load() to pull initialzed data return session.byNaturalId( User.class ) .using( "system", "prod" ) .using( "userName", "steve" ) .load();
Just like we saw above, access entity data by natural id allows both the load
and getReference
forms, with the same semantics.
Accessing persistent data by identifier and by natural-id is consistent in the Hibernate API. Each defines the same 2 data access methods:
getReference
Should be used in cases where the identifier is assumed to exist, where non-existence would be an actual error. Should never be used to test existence. That is because this method will prefer to create and return a proxy if the data is not already associated with the Session rather than hit the database. The quintessential use-case for using this method is to create foreign-key based associations.
load
Will return the persistent data associated with the given identifier value or null if that identifier does not exist.
In addition to those 2 methods, each also defines the method with
accepting
a org.hibernate.LockOptions
argument. Locking is discussed in a separate
chapter.
You can reload an entity instance and it's collections at any time.
Example 5.7. Example of refreshing entity state
Cat cat = session.get( Cat.class, catId ); ... session.refresh( cat );
Cat cat = entityManager.find( Cat.class, catId ); ... entityManager.refresh( cat );
One case where this is useful is when it is known that the database state has changed since the data was read. Refreshing allows the current database state to be pulled into the entity instance and the persistence context.
Another case where this might be useful is 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. However, please note that
Hibernate has the capability to handle this automatically through its notion of generated properties.
See the discussion of non-identifier generated attributes in the
Hibernate User Guide
Entities in managed/persistent state may be manipulated by the application and any changes will be automatically detected and persisted when the persistence context is flushed. There is no need to call a particular method to make your modifications persistent.
Example 5.8. Example of modifying managed state
Cat cat = session.get( Cat.class, catId ); cat.setName( "Garfield" ); session.flush(); // generally this is not explicitly needed
Cat cat = entityManager.find( Cat.class, catId ); cat.setName( "Garfield" ); entityManager.flush(); // generally this is not explicitly needed
Detachment is the process of working with data outside the scope of any persistence context. Data becomes detached in a number of ways. Once the persistence context is closed, all data that was associated with it becomes detached. Clearing the persistence context has the same effect. Evicting a particular entity from the persistence context makes it detached. And finally, serialization will make the deserialized form be detached (the original instance is still managed).
Detached data can still be manipulated, however the persistence context will no longer automatically know about these modification and the application will need to intervene to make the changes persistent.
Reattachment is the process of taking an incoming entity instance that is in detached state and re-associating it with the current persistence context.
JPA does not provide for this model. This is only available through Hibernate
org.hibernate.Session
.
Example 5.9. Example of reattaching a detached entity
session.lock( someDetachedCat, LockMode.NONE );
session.saveOrUpdate( someDetachedCat );
The method name update
is a bit misleading here. It does not mean that an
SQL
UPDATE
is immediately performed. It does, however, mean that
an SQL
UPDATE
will be performed when the persistence context is
flushed since Hibernate does not know its previous state against which to compare for changes. Unless
the entity is mapped with select-before-update
, in which case Hibernate will
pull the current state from the database and see if an update is needed.
Provided the entity is detached, update
and saveOrUpdate
operate exactly the same.
Merging is the process of taking an incoming entity instance that is in detached state and copying its data over onto a new instance that is in managed state.
Example 5.10. Visualizing merge
Object detached = ...; Object managed = entityManager.find( detached.getClass(), detached.getId() ); managed.setXyz( detached.getXyz() ); ... return managed;
That is not exactly what happens, but its a good visualization.
Example 5.11. Example of merging a detached entity
Cat theManagedInstance = session.merge( someDetachedCat );
Cat theManagedInstance = entityManager.merge( someDetachedCat );
An application can verify the state of entities and collections in relation to the persistence context.
Example 5.12. Examples of verifying managed state
assert session.contains( cat );
assert entityManager.contains( cat );
Example 5.13. Examples of verifying laziness
if ( Hibernate.isInitialized( customer.getAddress() ) { //display address if loaded } if ( Hibernate.isInitialized( customer.getOrders()) ) ) { //display orders if loaded } if (Hibernate.isPropertyInitialized( customer, "detailedBio" ) ) { //display property detailedBio if loaded }
javax.persistence.PersistenceUnitUtil jpaUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil(); 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 }
In JPA there is an alternative means to check laziness using the following
javax.persistence.PersistenceUtil
pattern. However, the
javax.persistence.PersistenceUnitUtil
is recommended where ever possible
Example 5.14. Alternative JPA means to verify laziness
javax.persistence.PersistenceUtil jpaUtil = javax.persistence.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 }