Hibernate.org Community Documentation
EntityManagerFactory
EntityManager
An Entity Manager whose lifecycle is managed by the container
An Entity Manager whose lifecycle is managed by the application.
Entity manager using a resource transaction (not a JTA transaction).
Alternatively, if you use Maven, add the following dependencies
<project ...>
...
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate-core-version}</version>
</dependency>
</dependencies>
</project>
We recommend you use Hibernate Validator and the
Bean Validation specification capabilities as its integration with Java
Persistence 2 has been standardized. Download Hibernate Validator 4 or
above from the Hibernate website and add
hibernate-validator.jar
and
validation-api.jar
in your classpath. Alternatively
add the following dependency in your pom.xml
.
<project>
...
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator-version}</version>
</dependency>
...
</dependencies>
...
</project>
If you wish to use Hibernate Search (full-text
search for Hibernate aplications), download it from the Hibernate website
and add hibernate-search.jar
and its dependencies in
your classpath. Alternatively add the following dependency in your
pom.xml
.
<project>
...
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search</artifactId>
<version>${hibernate-search-version}</version>
</dependency>
...
</dependencies>
...
</project>
<persistencexmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unitname="sample">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<propertyname="hibernate.dialect"value="org.hibernate.dialect.HSQLDialect"/>
<propertyname="hibernate.hbm2ddl.auto"value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
Here's a more complete example of a
filepersistence.xml
<persistencexmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unitname="manager1"transaction-type="JTA">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/DefaultDS</jta-data-source>
<mapping-file>ormap.xml</mapping-file>
<jar-file>MyApp.jar</jar-file>
<class>org.acme.Employee</class>
<class>org.acme.Person</class>
<class>org.acme.Address</class>
<shared-cache-mode>ENABLE_SELECTOVE</shared-cache-mode>
<validation-mode>CALLBACK</validation-mode>
<properties>
<propertyname="hibernate.dialect"value="org.hibernate.dialect.HSQLDialect"/>
<propertyname="hibernate.hbm2ddl.auto"value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
name
transaction-type
provider
jta-data-source
,
non-jta-data-source
mapping-file
jar-file
<jar-file>file:/home/turin/work/local/lab8/build/classes</jar-file>
exclude-unlisted-classes
class
You can fine-tune that if needed:
<propertyname="javax.persistence.validation.mode">
ddl
</property>
With this approach, you can mix ddl and callback modes:
<propertyname="javax.persistence.validation.mode">
ddl, callback
</property>
properties
The following properties can only be used in a SE environment where no datasource/JNDI is available:
<persistencexmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
EntityManagerFactory emf =Persistence.createEntityManagerFactory("manager1");
//or
Map<String,Object> configOverrides =newHashMap<String,Object>();
configOverrides.put("hibernate.hbm2ddl.auto","create-drop");
EntityManagerFactory programmaticEmf =
Persistence.createEntityManagerFactory("manager1", configOverrides);
The first version is equivalent to the second with an empty map.
The map version is a set of overrides that will take precedence over any
properties defined in your persistence.xml
files.
All the properties defined in Section 2.2.1, "Packaging" can be passed to the
createEntityManagerFactory
method and there are
a few additional ones:
javax.persistence.provider
to define the
provider class used
javax.persistence.transactionType
to define
the transaction type used (either JTA
or
RESOURCE_LOCAL
)
javax.persistence.jtaDataSource
to define
the JTA datasource name in JNDI
javax.persistence.nonJtaDataSource
to
define the non JTA datasource name in JNDI
javax.persistence.lock.timeout
pessimistic
lock timeout in milliseconds (Integer
or
String
)
javax.persistence.query.timeout
query
timeout in milliseconds (Integer
or
String
)
javax.persistence.sharedCache.mode
corresponds to the share-cache-mode
element
defined in Section 2.2.1, "Packaging".
javax.persistence.validation.mode
corresponds to the validation-mode
element
defined in Section 2.2.1, "Packaging".
When Persistence.createEntityManagerFactory()
is
called, the persistence implementation will search your classpath for
any META-INF/persistence.xml
files using the
ClassLoader.getResource("META-INF/persistence.xml")
method.
Actually the Persistence
class will look at all
the Persistence Providers available in the classpath and ask each of
them if they are responsible for the creation of the entity manager
factory manager1
. Each provider, from this list of
resources, it will try to find an entity manager that matches the name
you specify in the command line with what is specified in the
persistence.xml file (of course the provider element
must match the current persistent provider). If no persistence.xml with
the correct name are found or if the expected persistence provider is
not found, a PersistenceException
is
raised.
Apart from Hibernate system-level settings, all the properties
available in Hibernate can be set in properties
element of
the persistence.xml file or as an override in the map you pass to
createEntityManagerFactory()
. Please refer to the Hibernate
reference documentation for a complete listing. There are however a
couple of properties available in the EJB3 provider only.
Note that you can mix XML <class>
declaration and hibernate.ejb.cfgfile
usage in the
same configuration. Be aware of the potential clashed. The properties
set in persistence.xml
will override the one in the
defined hibernate.cfg.xml
.
It is important that you do not override
hibernate.transaction.factory_class
, Hibernate
EntityManager automatically set the appropriate transaction factory
depending on the EntityManager type (ie JTA
versus
RESOURSE_LOCAL
). If you are working in a Java EE
environment, you might want to set the
hibernate.transaction.manager_lookup_class
though.
Here is a typical configuration in a Java SE environment
<persistence>
<persistence-unitname="manager1"transaction-type="RESOURCE_LOCAL">
<class>org.hibernate.ejb.test.Cat</class>
<class>org.hibernate.ejb.test.Distributor</class>
<class>org.hibernate.ejb.test.Item</class>
<properties>
<propertyname="javax.persistence.jdbc.driver"value="org.hsqldb.jdbcDriver"/>
<propertyname="javax.persistence.jdbc.user"value="sa"/>
<propertyname="javax.persistence.jdbc.password"value=""/>
<propertyname="javax.persistence.jdbc.url"value="jdbc:hsqldb:."/>
<propertyname="hibernate.dialect"value="org.hibernate.dialect.HSQLDialect"/
<propertyname="hibernate.max_fetch_depth"value="3"/>
<!-- cache configuration -->
<propertyname="hibernate.ejb.classcache.org.hibernate.ejb.test.Item"value="read-write"/>
<propertyname="hibernate.ejb.collectioncache.org.hibernate.ejb.test.Item.distributors"value="read-write, RegionName"/>
<!-- alternatively to <class> and <property> declarations, you can use a regular hibernate.cfg.xml file -->
<!-- property name="hibernate.ejb.cfgfile" value="/org/hibernate/ejb/test/hibernate.cfg.xml"/ -->
</properties>
</persistence-unit>
</persistence>
To ease the programmatic configuration, Hibernate Entity Manager
provide a proprietary API. This API is very similar to the
Configuration
API and share the same concepts:
Ejb3Configuration
. Refer to the JavaDoc and the
Hibernate reference guide for more detailed informations on how to use
it.
TODO: me more descriptive on some APIs like setDatasource()
Ejb3Configuration cfg =newEjb3Configuration();
EntityManagerFactory emf =
cfg.addProperties( properties )//add some properties
.setInterceptor( myInterceptorImpl )// set an interceptor
.addAnnotatedClass(MyAnnotatedClass.class)//add a class to be mapped
.addClass(NonAnnotatedClass.class)//add an hbm.xml file using the Hibernate convention
.addRerousce( "mypath/MyOtherCLass.hbm.xml )//add an hbm.xml file
.addRerousce( "mypath/orm.xml )//add an EJB3 deployment descriptor
.configure("/mypath/hibernate.cfg.xml")//add a regular hibernate.cfg.xml
.buildEntityManagerFactory();//Create the entity manager factory
Note that the JACC*EventListeners
are removed
if the security is not enabled.
You can configure the event listeners either through the properties
(see Configuration and bootstrapping) or through the
ejb3configuration.getEventListeners()
API.
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,newLong(catId));
child =newChild();
child.SetName("Henry");
Parent parent = em.getReference(Parent.class, parentId);//no query to the DB
child.setParent(parent);
em.persist(child);
em.persist(cat);
em.flush();// force the SQL insert and triggers to run
em.refresh(cat);//re-read the state (after the trigger executes)
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();
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.
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();
// in the first entity manager
Cat cat = firstEntityManager.find(Cat.class, catId);
Cat potentialMate =newCat();
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.
//In the first entity manager
Cat cat = firstEntityManager.find(Cat.class, catID);
//In a higher layer of the application, detached
Cat mate =newCat();
cat.setMate(mate);
//Later, in a new entity manager
secondEntityManager.merge(cat);// update existing state
secondEntityManager.merge(mate);// save the new instance
Usually merge()
is used in the following
scenario:
Here is the exact semantic of
merge()
:
(*) if a transaction is active
The SQL statements are issued in the following order
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
@OneToOne(cascade=CascadeType.PERSIST)
Cascading options can be combined:
@OneToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE,CascadeType.REFRESH })
You can define various levels of locking strategies. A lock can be applied in several ways:
You can use various lock approaches:
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;
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 Metamodel itself is described in Chapter 5 Metamodel API of the [ JPA 2 Specification ]. Chapter 6 Criteria API of the [ JPA 2 Specification ] describes and shows uses of the metamodel in criteria queries, as does Chapter 9, Criteria Queries.
The metamodel is a set of objects that describe your domain model.
javax.persistence.metamodel.Metamodel
acts as a repository of these metamodel
objects and provides access to them, and can be obtained from either the
javax.persistence.EntityManagerFactory
or the
javax.persistence.EntityManager
via their
getMetamodel
method.
This metamodel is important in 2 ways. First, it allows providers and frameworks a generic way to deal with an application's domain model. Persistence providers will already have some form of metamodel that they use to describe the domain model being mapped. This API however defines a single, independent access to that existing information. A validation framework, for example, could use this information to understand associations; a marshaling framework might use this information to decide how much of an entity graph to marshal. This usage is beyond the scope of this documentation.
As of today the JPA 2 metamodel does not provide any facility for accessing relational information pertaining to the physical model. It is expected this will be addressed in a future release of the specification.
Second, from an application writer's perspective, it allows very fluent expression of completely type-safe criteria queries, especially the Static Metamodel approach. The [ JPA 2 Specification ] defines a number of ways the metamodel can be accessed and used, including the Static Metamodel approach, which we will look at later. The Static Metamodel approach is wonderful when the code has a priori knowledge of the domain model. Chapter 9, Criteria Queries uses this approach exclusively in its examples.
A static metamodel is a series of classes that "mirror" the entities and embeddables in the domain model and provide static access to the metadata about the mirrored class's attributes. We will exclusively discuss what the [ JPA 2 Specification ] terms a Canonical Metamodel:
Import statements must be included for the needed javax.persistence.metamodel types
as appropriate (e.g., | |
-- [ JPA 2 Specification, section 6.2.1.1, pp 198-199] |
These canonical metamodel classes can be generated manually if you wish though it is expected that most developers will prefer use of an annotation processor. Annotation processors themselves are beyond the scope of this document. However, the Hibernate team does develop an annotation processor tool for generating a canonical metamodel. See Hibernate Metamodel Generator.
When the Hibernate EntityManagerFactory
is being built, it will
look for a canonical metamodel class for each of the managed typed is knows about and if it finds
any it will inject the appropriate metamodel information into them, as outlined in
[ JPA 2 Specification, section 6.2.2, pg 200]
[1]
(from the original) If the class was generated, the
javax.annotation.Generated
annotation should be
used to annotate the class. The use of any other annotations on static metamodel
classes is undefined.
The most important point about Hibernate Entity Manager and concurrency control is that it is very easy to understand. Hibernate Entity Manager directly uses JDBC connections and JTA resources without adding any additional locking behavior. We highly recommend you spend some time with the JDBC, ANSI, and transaction isolation specification of your database management system. Hibernate Entity Manager only adds automatic versioning but does not lock objects in memory or change the isolation level of your database transactions. Basically, use Hibernate Entity Manager like you would use direct JDBC (or JTA/CMT) with your database resources.
We start the discussion of concurrency control in Hibernate with the
granularity of EntityManagerFactory
, and
EntityManager
, as well as database transactions and long
units of work..
In this chapter, and unless explicitly expressed, we will mix and match the concept of entity manager and persistence context. One is an API and programming object, the other a definition of scope. However, keep in mind the essential difference. A persistence context is usually bound to a JTA transaction in Java EE, and a persistence context starts and ends at transaction boundaries (transaction-scoped) unless you use an extended entity manager. Please refer to Section 1.2.3, "Persistence context scope" for more information.
The persistence context caches every object that is in managed
state (watched and checked for dirty state by Hibernate). This means
it grows endlessly until you get an
OutOfMemoryException
, if you keep it open for
a long time or simply load too much data. One solution for this is
some kind batch processing with regular flushing of the persistence
context, but you should consider using a database stored procedure
if you need mass data operations. Some solutions for this problem
are shown in Chapter 7, Batch processing. Keeping a persistence context
open for the duration of a user session also means a high
probability of stale data, which you have to know about and control
appropriately.
Usually, ending a unit of work involves four distinct phases:
//Non-managed environment idiom
EntityManager em = emf.createEntityManager();
EntityTransaction tx =null;
try{
tx = em.getTransaction();
tx.begin();
//do some work
...
tx.commit();
}
catch(RuntimeException e){
if( tx !=null&& tx.isActive()) tx.rollback();
throw e;// or display error message
}
finally{
em.close();
}
If you use bean-managed transactions (BMT), the code will look like this:
// BMT idiom
@ResourcepublicUserTransaction utx;
@ResourcepublicEntityManagerFactory factory;
publicvoid doBusiness(){
EntityManager em = factory.createEntityManager();
try{
//do some work
...
utx.commit();
}
catch(RuntimeException e){
if(utx !=null) utx.rollback();
throw e;// or display error message
}
finally{
em.close();
}
If you work in a CMT environment, you might also want to use the
same entity manager in different parts of your code. Typically, in a
non-managed environment you would use a ThreadLocal
variable to hold the entity manager, but a single EJB request might
execute in different threads (e.g. session bean calling another session
bean). The EJB3 container takes care of the persistence context
propagation for you. Either using injection or lookup, the EJB3
container will return an entity manager with the same persistence
context bound to the JTA context if any, or create a new one and bind it
(see Section 1.2.4, "Persistence context propagation" .)
Our entity manager/transaction management idiom for CMT and EJB3 container-use is reduced to this:
//CMT idiom through injection
@PersistenceContext(name="sample")EntityManager em;
Or this if you use Java Context and Dependency Injection (CDI).
@InjectEntityManager em;
In other words, all you have to do in a managed environment is to
inject the EntityManager
, do your data access work,
and leave the rest to the container. Transaction boundaries are set
declaratively in the annotations or deployment descriptors of your
session beans. The lifecycle of the entity manager and persistence
context is completely managed by the container.
Due to a silly limitation of the JTA spec, it is not possible for
Hibernate to automatically clean up any unclosed
ScrollableResults
or Iterator
instances returned by scroll()
or
iterate()
. You must release the
underlying database cursor by calling
ScrollableResults.close()
or
Hibernate.close(Iterator)
explicitly from a
finally
block. (Of course, most applications can
easily avoid using scroll()
or
iterate()
at all from the CMT code.)
EntityNotFoundException
: an entity was
expected but none match the requirement
NonUniqueResultException
: more than one
entity is found when calling
getSingleResult()
NoResultException: when
getSingleResult()
does not find any
matching entity
EntityExistsException
: an existing
entity is passed to persist()
TransactionRequiredException
: this
operation has to be in a transaction
IllegalStateException
: the entity
manager is used in a wrong way
JDBCConnectionException
- indicates an
error with the underlying JDBC communication.
SQLGrammarException
- indicates a grammar
or syntax problem with the issued SQL.
ConstraintViolationException
- indicates
some form of integrity constraint violation.
GenericJDBCException
- a generic exception
which did not fall into any of the other categories.
@Entity
@EntityListeners(class=Audit.class)
publicclassCat{
@IdprivateInteger id;
privateString name;
privateCalendar dateOfBirth;
@Transientprivateint age;
privateDate lastUpdate;
//getters and setters
/**
* Set my transient property at load time based on a calculation,
* note that a native Hibernate formula mapping is better for this purpose.
*/
@PostLoad
publicvoid calculateAge(){
Calendar birth =newGregorianCalendar();
birth.setTime(dateOfBirth);
Calendar now =newGregorianCalendar();
now.setTime(newDate());
int adjust =0;
if( now.get(Calendar.DAY_OF_YEAR)- birth.get(Calendar.DAY_OF_YEAR)<0){
adjust =-1;
}
age = now.get(Calendar.YEAR)- birth.get(Calendar.YEAR)+ adjust;
}
}
publicclassLastUpdateListener{
/**
* automatic property set before any database persistence
*/
@PreUpdate
@PrePersist
publicvoid setLastUpdate(Cat o){
o.setLastUpdate(newDate());
}
}
A callback method must not invoke
EntityManager
or Query
methods!
Batch processing has traditionally been difficult in full object/relational mapping. ORM is all about object state management, which implies that object state is available in memory. However, Hibernate has some features to optimize batch processing which are discussed in the Hibernate reference guide, however, EJB3 persistence differs slightly.
As already discussed, automatic and transparent object/relational
mapping is concerned with the management of object state. This implies
that the object state is available in memory, hence updating or deleting
(using SQL UPDATE
and DELETE
) data
directly in the database will not affect in-memory state. However,
Hibernate provides methods for bulk SQL-style UPDATE
and DELETE
statement execution which are performed
through JP-QL (Chapter 8, JP-QL: The Object Query Language).
The pseudo-syntax for UPDATE
and
DELETE
statements is: ( UPDATE | DELETE )
FROM? ClassName (WHERE WHERE_CONDITIONS)?
. Note that:
In the from-clause, the FROM keyword is optional.
There can only be a single class named in the from-clause, and it cannot have an alias (this is a current Hibernate limitation and will be removed soon).
No joins (either implicit or explicit) can be specified in a bulk JP-QL query. Sub-queries may be used in the where-clause.
The where-clause is also optional.
As an example, to execute an JP-QL UPDATE
, use
the Query.executeUpdate()
method:
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
String jpqlUpdate ="update Customer set name = :newName where name = :oldName"
int updatedEntities = entityManager.createQuery( jpqlUpdate )
.setParameter("newName", newName )
.setParameter("oldName", oldName )
.executeUpdate();
entityManager.getTransaction().commit();
entityManager.close();
To execute an JP-QL DELETE
, use the same
Query.executeUpdate()
method (the method is named for
those familiar with JDBC's
PreparedStatement.executeUpdate()
):
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
String hqlDelete ="delete Customer where name = :oldName";
int deletedEntities = entityManager.createQuery( hqlDelete )
.setParameter("oldName", oldName )
.executeUpdate();
entityManager.getTransaction().commit();
entityManager.close();
The int
value returned by the
Query.executeUpdate()
method indicate the number of
entities effected by the operation. This may or may not correlate with the
number of rows effected in the database. A JP-QL bulk operation might
result in multiple actual SQL statements being executed, for
joined-subclass, for example. The returned number indicates the number of
actual entities affected by the statement. Going back to the example of
joined-subclass, a delete against one of the subclasses may actually
result in deletes against not just the table to which that subclass is
mapped, but also the "root" table and potentially joined-subclass tables
further down the inheritance hierarchy.
The Java Persistence Query Language (JP-QL) has been heavily inspired by HQL, the native Hibernate Query Language. Both are therefore very close to SQL, but portable and independent of the database schema. People familiar with HQL shouldn't have any problem using JP-QL. In fact HQL is a strict superset of JP-QL and you use the same query API for both types of queries. Portable JPA applications however should stick to JP-QL.
For a type-safe approach to query, we highly recommend you to use the Criteria query, see Chapter 9, Criteria Queries.
The simplest possible JP-QL query is of the form:
select c from eg.Cat c
select c from Cat c
select cat from Cat as cat
Multiple classes may appear, resulting in a cartesian product or "cross" join.
select from, param from Formula as form, Parameter as param
select cat, mate, kitten from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten
select cat from Cat as cat left join cat.mate.kittens as kittens
The supported join types are borrowed from ANSI SQL
The inner join
, left outer
join
constructs may be abbreviated.
select cat, mate, kitten from Cat as cat join cat.mate as mate left join cat.kittens as kitten
select cat from Cat as cat inner join fetch cat.mate left join fetch cat.kittens
select cat from Cat as cat inner join fetch cat.mate left join fetch cat.kittens child left join fetch child.kittens
select doc from Document doc fetch all properties order by doc.name
select doc from Document doc fetch all properties where lower(doc.name) like '%cats%'
The select
clause picks which objects and
properties to return in the query result set. Consider:
select mate from Cat as cat inner join cat.mate as mate
The query will select mate
s of other
Cat
s. Actually, you may express this query more
compactly as:
select cat.mate from Cat cat
Queries may return properties of any value type including properties of component type:
select cat.name from DomesticCat cat where cat.name like 'fri%'
select cust.name.firstName from Customer as cust
Queries may return multiple objects and/or properties as an array of
type Object[]
,
select mother, offspr, mate.name from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
or as a List
(HQL specific feature)
select new list(mother, offspr, mate.name) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
or as an actual type-safe Java object (often called a view object),
select new Family(mother, mate, offspr) from DomesticCat as mother join mother.mate as mate left join mother.kittens as offspr
assuming that the class Family
has an appropriate
constructor.
You may assign aliases to selected expressions using
as
:
select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n from Cat cat
This is most useful when used together with select new
map
(HQL specific feature):
select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n ) from Cat cat
HQL queries may even return the results of aggregate functions on properties:
select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat) from Cat cat
The supported aggregate functions are
select cat.weight + sum(kitten.weight) from Cat cat join cat.kittens kitten group by cat.id, cat.weight
select firstName||' '||initial||' '||upper(lastName) from Person
The distinct
and all
keywords
may be used and have the same semantics as in SQL.
select distinct cat.name from Cat cat select count(distinct cat.name), count(cat) from Cat cat
select cat from Cat as cat
from java.lang.Object o // HQL only
The interface Named
might be implemented by
various persistent classes:
from Named n, Named m where n.name = m.name // HQL only
select cat from Cat cat where cat.name='Fritz'
returns instances of Cat
named 'Fritz'.
select foo from Foo foo, Bar bar where foo.startDate = bar.date
select cat from Cat cat where cat.mate.name is not null
This query translates to an SQL query with a table (inner) join. If you were to write something like
select foo from Foo foo where foo.bar.baz.customer.address.city is not null
you would end up with a query that would require four table joins in SQL.
The =
operator may be used to compare not only
properties, but also instances:
select cat, rival from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate from Cat cat, Cat mate where cat.mate = mate
select cat from Cat as cat where cat.id = 123 select cat from Cat as cat where cat.mate.id = 69
The second query is efficient. No table join is required!
select person from bank.Person person where person.id.country = 'AU' and person.id.medicareNumber = 123456
select account from bank.Account account where account.owner.id.country = 'AU' and account.owner.id.medicareNumber = 123456
Once again, the second query requires no table join.
select cat from Cat cat where cat.class = DomesticCat
store.owner.address.city // okay store.owner.address // error!
from AuditLog log, Payment payment where log.item.class = 'Payment' and log.item.id = payment.id
Expressions allowed in the where
clause include
most of the kind of things you could write in SQL:
in
, not in
,
between
, is null
, is
not null
, is empty
, is not
empty
, member of
and not member
of
string concatenation ...||...
or
concat(...,...) (use concat() for portable JP-QL
queries)
second(...)
, minute(...)
,
hour(...)
, day(...)
,
month(...)
, year(...)
, (specific
to HQL)
Any database-supported SQL scalar function like
sign()
, trunc()
,
rtrim()
, sin()
in
and between
may be used as
follows:
select cat from DomesticCat cat where cat.name between 'A' and 'B'
select cat from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )
and the negated forms may be written
select cat from DomesticCat cat where cat.name not between 'A' and 'B'
select cat from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )
Likewise, is null
and is not
null
may be used to test for null values.
hibernate.query.substitutions true 1, false 0
select cat from Cat cat where cat.alive = true
select cat from Cat cat where cat.kittens.size > 0
select cat from Cat cat where size(cat.kittens) > 0
select cal from Calendar cal where maxelement(cal.holidays) > current date
select order from Order order where maxindex(order.items) > 100
select order from Order order where minelement(order.items) > 10000
select mother from Cat as mother, Cat as kit where kit in elements(foo.kittens)
select p from NameList list, Person p where p.name = some elements(list.names)
select cat from Cat cat where exists elements(cat.kittens)
select cat from Player p where 3 > all elements(p.scores)
select cat from Show show where 'fizard' in indices(show.acts)
SELECT i.name, VALUE(p) FROM Item i JOIN i.photos p WHERE KEY(p) LIKE ‘%egret’
select order from Order order where order.items[0].id = 1234
select person from Person person, Calendar calendar where calendar.holidays['national day'] = person.birthDay and person.nationality.calendar = calendar
select item from Item item, Order order where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
select item from Item item, Order order where order.items[ maxindex(order.items) ] = item and order.id = 11
The expression inside []
may even be an
arithmetic expression.
select item from Item item, Order order where order.items[ size(order.items) - 1 ] = item
select item, index(item) from Order order join order.items item where index(item) < 5
Scalar SQL functions supported by the underlying database may be used
select cat from DomesticCat cat where upper(cat.name) like 'FRI%'
select cust from Product prod, Store store inner join store.customers cust where prod.name = 'widget' and store.location.name in ( 'Melbourne', 'Sydney' ) and prod = all elements(cust.currentOrder.lineItems)
SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order FROM customers cust, stores store, locations loc, store_customers sc, product prod WHERE prod.name = 'widget' AND store.loc_id = loc.id AND loc.name IN ( 'Melbourne', 'Sydney' ) AND sc.store_id = store.id AND sc.cust_id = cust.id AND prod.id = ALL( SELECT item.prod_id FROM line_items item, orders o WHERE item.order_id = o.id AND cust.current_order = o.id )
The list returned by a query may be ordered by any property of a returned class or components:
select cat from DomesticCat cat order by cat.name asc, cat.weight desc, cat.birthdate
The optional asc
or desc
indicate ascending or descending order respectively.
select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color
select foo.id, avg(name), max(name) from Foo foo join foo.names name group by foo.id
A having
clause is also allowed.
select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color having cat.color in (eg.Color.TABBY, eg.Color.BLACK)
select cat from Cat cat join cat.kittens kitten group by cat having avg(kitten.weight) > 100 order by count(kitten) asc, sum(kitten.weight) desc
Note that neither the group by
clause nor the
order by
clause may contain arithmetic
expressions.
select fatcat from Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
select cat from DomesticCat as cat where cat.name = some ( select name.nickName from Name as name )
select cat from Cat as cat where not exists ( from Cat as mate where mate.mate = cat )
select cat from DomesticCat as cat where cat.name not in ( select name.nickName from Name as name )
For subqueries with more than one expression in the select list, you can use a tuple constructor:
select cat from Cat as cat where not ( cat.name, cat.color ) in ( select cat.name, cat.color from DomesticCat cat )
select cat from Person where name = ('Gavin', 'A', 'King')
Which is equivalent to the more verbose:
select cat from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog.effectiveDate < sysdate and catalog.effectiveDate >= all ( select cat.effectiveDate from Catalog as cat where cat.effectiveDate < sysdate ) group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog = :currentCatalog group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc
select count(payment), status.name from Payment as payment join payment.currentStatus as status join payment.statusChanges as statusChange where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or ( statusChange.timeStamp = ( select max(change.timeStamp) from PaymentStatusChange change where change.payment = payment ) and statusChange.user <> :currentUser ) group by status.name, status.sortOrder order by status.sortOrder
select count(payment), status.name from Payment as payment join payment.currentStatus as status where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser group by status.name, status.sortOrder order by status.sortOrder
However the query would have been HQL specific.
select account, payment from Account as account join account.holder.users as user left outer join account.payments as payment where :currentUser = user and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID) order by account.type.sortOrder, account.accountNumber, payment.dueDate
Hibernate now supports UPDATE and DELETE statements in HQL/JP-QL. See Section 7.1, "Bulk update/delete" for details.
To order a result by the size of a collection, use the following query:
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name order by count(msg)
from User usr where size(usr.messages) >= 1
If your database doesn't support subselects, use the following query:
select usr.id, usr.name from User usr.name join usr.messages msg group by usr.id, usr.name having count(msg) >= 1
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name having count(msg) = 0
Criteria queries are a programmatic, type-safe way to express a query.
They are type-safe in terms of using interfaces and classes to represent
various structural parts of a query such as the query itself, or the select
clause, or an order-by, etc. They can also be type-safe in terms of
referencing attributes as we will see in a bit. Users of the older Hibernate
org.hibernate.Criteria
query API will
recognize the general approach, though we believe the JPA API to be superior
as it represents a clean look at the lessons learned from that API.
Criteria queries are essentially an object graph, where each part of
the graph represents an increasing (as we navigate down this graph) more
atomic part of query. The first step in performing a criteria query is
building this graph. The
javax.persistence.criteria.CriteriaBuilder
interface is the first thing with which you need to become acquainted to
begin using criteria queries. Its role is that of a factory for all the
individual pieces of the criteria. You obtain a
javax.persistence.criteria.CriteriaBuilder
instance by calling the getCriteriaBuilder
method
of the
javax.persistence.EntityManagerFactory
CriteriaBuilder builder = entityManagerFactory.getCriteriaBuilder();
The next step is to obtain a
javax.persistence.criteria.CriteriaQuery
. You
do this by one of the 3 methods on
javax.persistence.criteria.CriteriaBuilder
for this purpose.
CriteriaQuery<T> createQuery(Class<T>)
CriteriaQuery<Tuple> createTupleQuery()
CriteriaQuery<Object> createQuery()
Each serves a different purpose depending on the expected type of the query results.
Chapter 6 Criteria API of the [ JPA 2 Specification ] already contains a decent amount of reference material pertaining to the various parts of a criteria query. So rather than duplicate all that content here, lets instead look at some of the more widely anticipated usages of the API.
CriteriaQuery<T> createQuery(Class<T>)
Example 9.1. Selecting the root entity
CriteriaQuery<(1)Person> criteria = builder.createQuery( Person.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.selec(2)t( personRoot ); criteria.where(3)( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Person> people = em.createQuery( criteria ).getResultList(); for ( Person p(1)erson : people ) { ... }
1 | We use the form createQuery( Person.class ) here because the expected returns are in fact Person entities as we see when we begin processing the results. |
2 | personCriteria.select( personRoot ) here is completely unneeded in this specific case because of the fact that personRoot will be the implied selection since we have only a single root. It was done here only for completeness of an example |
3 | Person_.eyeColor is an example of the static form of metamodel reference. We will use that form exclusively in this chapter. See Section 4.1, "Static metamodel" for details. |
Example 9.3. Selecting an expression
CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class );
Root<Person> personRoot = criteria.from( Person.class );
criteria.selec(1)t( builder.max( personRoot.get( Person_.age ) ) );
criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) );
Integer maxAge = em.createQuery( criteria ).getSingleResult();
1 | Here we see
|
There are actually a few different ways to select multiple values using criteria queries. We will explore 2 options here, but an alternative recommended approach is to use tuples as described in Section 9.2, "Tuple criteria queries"
Example 9.5. Selecting an array (2)
CriteriaQuery<(1)Object[]> criteria = builder.createQuery( Object[].class ); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.multi(2)select( idPath, agePath ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Object[]>(1) valueArray = em.createQuery( criteria ).getResultList(); for ( Object[] values : valueArray ) { final Long id = (Long) values[0]; final Integer age = (Integer) values[1]; ... }
1 | Just as we saw in Example 9.4, "Selecting an array" we have a "typed" criteria query returning an Object array. |
2 | This actually functions exactly the same as what we saw in
Example 9.4, "Selecting an array". The
|
Another alternative to Section 9.1.3, "Selecting multiple values" is to instead select an object that will "wrap" the multiple values. Going back to the example query there, rather than returning an array of [Person#id, Person#age] instead declare a class that holds these values and instead return that.
A better approach to Section 9.1.3, "Selecting multiple values" is to either use a
wrapper (which we just saw in Section 9.1.4, "Selecting a wrapper") or using the
javax.persistence.Tuple
contract.
Example 9.7. Selecting a tuple
CriteriaQuery<(1)Tuple> criteria = builder.createTupleQuery(); Root<Person> personRoot = criteria.from( Person.class ); Path<Long> idPath = personRoot.get( Person_.id ); Path<Integer> agePath = personRoot.get( Person_.age ); criteria.multi(2)select( idPath, agePath ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); List<Tuple> tu(1)ples = em.createQuery( criteria ).getResultList(); for ( Tuple tuple : valueArray ) { assert tup(3)le.get( 0 ) == tuple.get( idPath ); assert tup(3)le.get( 1 ) == tuple.get( agePath ); ... }
1 | Here we see the use of a new
|
2 | Again we see the use of the
|
3 | Here we see
|
The javax.persistence.Tuple
contract provides 3 basic forms of access to the underlying
elements:
<X> X get(TupleElement<X> tupleElement)
This allows typed access to the underlying tuple elements.
We see this in Example 9.7, "Selecting a tuple" in
the tuple.get( idPath ) and
tuple.get( agePath ) calls. Just about
everything is a
javax.persistence.TupleElement
.
Object get(int i)
<X> X get(int i,Class<X> type)
Very similar to what we saw in Example 9.4, "Selecting an array" and Example 9.5, "Selecting an array (2)" in terms of positional access. Only the second form here provides typing, because the user explicitly provides the typing on access. We see this in Example 9.7, "Selecting a tuple" in the tuple.get( 0 ) and tuple.get( 1 ) calls.
Object get(String alias)
<X> X get(String alias,Class<X> type)
Again, only the second form here provides typing, because the user explicitly provides the typing on access. We have not seen an example of using this, but its trivial. We would simply, for example, have applies an alias to either of the paths like idPath.alias( "id" ) and/or agePath.alias( "age" ) and we could have accessed the individual tuple elements by those specified aliases.
A CriteriaQuery object defines a query over one or more entity, embeddable, or basic abstract schema types. The root objects of the query are entities, from which the other types are reached by navigation. | |
--[ JPA 2 Specification, section 6.5.2 Query Roots, pg 262] |
<X>Root<X> from(Class<X>)
<X>Root<X> from(EntityType<X>)
Criteria queries may define multiple roots, the effect of which is to create a cartesian product between the newly added root and the others. Here is an example matching all single men and all single women:
CriteriaQuery query = builder.createQuery();
Root<Person> men = query.from(Person.class);
Root<Person> women = query.from(Person.class);
Predicate menRestriction = builder.and(
builder.equal( men.get(Person_.gender ),Gender.MALE ),
builder.equal( men.get(Person_.relationshipStatus ),RelationshipStatus.SINGLE )
);
Predicate womenRestriction = builder.and(
builder.equal( women.get(Person_.gender ),Gender.FEMALE ),
builder.equal( women.get(Person_.relationshipStatus ),RelationshipStatus.SINGLE )
);
query.where( builder.and( menRestriction, womenRestriction ));
Example 9.10. Example with Collections
CriteriaQuery<Person> personCriteria = builder.createQuery(Person.class);
Root<Person> personRoot = person.from(Person.class);
Join<Person,Order> orders = personRoot.join(Person_.orders );
Join<Order,LineItem> orderLines = orders.join(Order_.lineItems );
...
Technically speaking, embedded attributes are always fetched
with their owner. However in order to define the fetching of
Address#country we needed a
javax.persistence.criteria.Fetch
for
its parent path.
Example 9.12. Example with Collections
CriteriaQuery<Person> personCriteria = builder.createQuery(Person.class);
Root<Person> personRoot = person.from(Person.class);
Join<Person,Order> orders = personRoot.fetch(Person_.orders );
Join<Order,LineItem> orderLines = orders.fetch(Order_.lineItems );
...
You may also express queries in the native SQL dialect of your database. This is useful if you want to utilize database specific features such as query hints or the CONNECT BY option in Oracle. It also provides a clean migration path from a direct SQL/JDBC based application to Hibernate. Note that Hibernate allows you to specify handwritten SQL (including stored procedures) for all create, update, delete, and load operations (please refer to the reference guide for more information.)
@SqlResultSetMapping(name="GetNightAndArea", entities={
@EntityResult(name="org.hibernate.test.annotations.query.Night", fields ={
@FieldResult(name="id", column="nid"),
@FieldResult(name="duration", column="night_duration"),
@FieldResult(name="date", column="night_date"),
@FieldResult(name="area", column="area_id")
}),
@EntityResult(name="org.hibernate.test.annotations.query.Area", fields ={
@FieldResult(name="id", column="aid"),
@FieldResult(name="name", column="name")
})
}
)
//or
@SqlResultSetMapping(name="defaultSpaceShip", entities=@EntityResult(name="org.hibernate.test.annotations.query.SpaceShip"))
You can also define scalar results and even mix entity results and scalar results
@SqlResultSetMapping(name="ScalarAndEntities",
entities={
@EntityResult(name="org.hibernate.test.annotations.query.Night", fields ={
@FieldResult(name="id", column="nid"),
@FieldResult(name="duration", column="night_duration"),
@FieldResult(name="date", column="night_date"),
@FieldResult(name="area", column="area_id")
}),
@EntityResult(name="org.hibernate.test.annotations.query.Area", fields ={
@FieldResult(name="id", column="aid"),
@FieldResult(name="name", column="name")
})
},
columns={
@ColumnResult(name="durationInSec")
}
)
The SQL query will then have to return a column alias
durationInSec
.
TODO: This sounds like a dupe...
String sqlQuery ="select night.id nid, night.night_duration, night.night_date, area.id aid, "
+"night.area_id, area.name from Night night, Area area where night.area_id = area.id "
+"and night.night_duration >= ?";
Query q = entityManager.createNativeQuery(sqlQuery,"GetNightAndArea");
q.setParameter(1, expectedDuration );
q.getResultList();
This native query returns nights and area based on the
GetNightAndArea
result set.
String sqlQuery ="select * from tbl_spaceship where owner = ?";
Query q = entityManager.createNativeQuery(sqlQuery,SpaceShip.class);
q.setParameter(1,"Han");
q.getResultList();
Copyright © 2005 Red Hat Inc. and the various authors