From 3153fb385d86ecc2b18dabed43e3ebaedaf9734e Mon Sep 17 00:00:00 2001 From: Emmanuel Bernard Date: Wed, 10 Mar 2010 10:56:16 +0000 Subject: [PATCH] HHH-4933 finish documentation on JPA 2 and EntityManager, migrate to jHighlight git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18956 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- entitymanager/src/main/docbook/en/master.xml | 6 + .../src/main/docbook/en/modules/batch.xml | 32 ++-- .../main/docbook/en/modules/configuration.xml | 10 +- .../docbook/en/modules/entitymanagerapi.xml | 181 +++++++++++++++--- .../src/main/docbook/en/modules/listeners.xml | 4 +- .../docbook/en/modules/query_criteria.xml | 52 ++--- .../main/docbook/en/modules/query_ejbql.xml | 126 +++++++----- .../main/docbook/en/modules/query_native.xml | 26 +-- .../main/docbook/en/modules/transactions.xml | 14 +- 9 files changed, 309 insertions(+), 142 deletions(-) diff --git a/entitymanager/src/main/docbook/en/master.xml b/entitymanager/src/main/docbook/en/master.xml index c999742549..3a104e2472 100644 --- a/entitymanager/src/main/docbook/en/master.xml +++ b/entitymanager/src/main/docbook/en/master.xml @@ -70,6 +70,12 @@ Ebersole + + Gavin + + King + + diff --git a/entitymanager/src/main/docbook/en/modules/batch.xml b/entitymanager/src/main/docbook/en/modules/batch.xml index 1ac359249e..02a7a5ae71 100644 --- a/entitymanager/src/main/docbook/en/modules/batch.xml +++ b/entitymanager/src/main/docbook/en/modules/batch.xml @@ -1,4 +1,4 @@ - + - - + Batch processing @@ -34,7 +34,7 @@ Hibernate reference guide, however, EJB3 persistence differs slightly. - +
Bulk update/delete As already discussed, automatic and transparent object/relational @@ -44,7 +44,7 @@ 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 EJB-QL (). + through JP-QL (). The pseudo-syntax for UPDATE and DELETE statements is: ( UPDATE | DELETE ) @@ -63,7 +63,7 @@ No joins (either implicit or explicit) can be specified in a - bulk EJB-QL query. Sub-queries may be used in the where-clause. + bulk JP-QL query. Sub-queries may be used in the where-clause. @@ -71,26 +71,26 @@ - As an example, to execute an EJB-QL UPDATE, use + As an example, to execute an JP-QL UPDATE, use the Query.executeUpdate() method: - EntityManager entityManager = entityManagerFactory.createEntityManager(); + EntityManager entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); -String ejbqlUpdate = "update Customer set name = :newName where name = :oldName" -int updatedEntities = entityManager.createQuery( ejbqlUpdate ) +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 EJB-QL DELETE, use the same + 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 entityManager = entityManagerFactory.createEntityManager(); entityManager.getTransaction().begin(); String hqlDelete = "delete Customer where name = :oldName"; @@ -103,13 +103,13 @@ 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. An EJB-QL bulk operation might + 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 inheritence hierarchy. - - \ No newline at end of file + further down the inheritance hierarchy. +
+
diff --git a/entitymanager/src/main/docbook/en/modules/configuration.xml b/entitymanager/src/main/docbook/en/modules/configuration.xml index c52aa8c925..8ed6743f1e 100644 --- a/entitymanager/src/main/docbook/en/modules/configuration.xml +++ b/entitymanager/src/main/docbook/en/modules/configuration.xml @@ -669,10 +669,10 @@ EntityManagerFactory programmaticEmf = <class>org.hibernate.ejb.test.Distributor</class> <class>org.hibernate.ejb.test.Item</class> <properties> - <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/> - <property name="javax.persistence.jdbc.user" value="sa"/> - <property name="javax.persistence.jdbc.password" value=""/> - <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:."/> + <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/> + <property name="javax.persistence.jdbc.user" value="sa"/> + <property name="javax.persistence.jdbc.password" value=""/> + <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:."/> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/ <property name="hibernate.max_fetch_depth" value="3"/> @@ -857,7 +857,7 @@ EntityManagerFactory emf = Persistence class is bootstrap class to create an entity manager factory. - // Use persistence.xml configuration + // Use persistence.xml configuration EntityManagerFactory emf = Persistence.createEntityManagerFactory("manager1") EntityManager em = emf.createEntityManager(); // Retrieve an application managed entity manager // Work with the EM diff --git a/entitymanager/src/main/docbook/en/modules/entitymanagerapi.xml b/entitymanager/src/main/docbook/en/modules/entitymanagerapi.xml index 4644640ae1..f62ee7017f 100644 --- a/entitymanager/src/main/docbook/en/modules/entitymanagerapi.xml +++ b/entitymanager/src/main/docbook/en/modules/entitymanagerapi.xml @@ -74,7 +74,7 @@ new operator) it is in new state. You can make it persistent by associating it to an entity manager: - DomesticCat fritz = new DomesticCat(); + DomesticCat fritz = new DomesticCat(); fritz.setColor(Color.GINGER); fritz.setSex('M'); fritz.setName("Fritz"); @@ -93,7 +93,7 @@ em.persist(fritz); Load an entity instance by its identifier value with the entity manager's find() method: - cat = em.find(Cat.class, catId); + cat = em.find(Cat.class, catId); // You may need to wrap the primitive identifiers long catId = 1234; @@ -105,7 +105,7 @@ em.find( Cat.class, new Long(catId) ); useful to link a child to its parent without having to load the parent. - child = new Child(); + child = new Child(); child.SetName("Henry"); Parent parent = em.getReference(Parent.class, parentId); //no query to the DB child.setParent(parent); @@ -118,7 +118,7 @@ em.persist(child); refreshed unless you specify REFRESH as a cascade style of any associations: - em.persist(cat); + em.persist(cat); em.flush(); // force the SQL insert and triggers to run em.refresh(cat); //re-read the state (after the trigger executes) @@ -145,7 +145,7 @@ em.refresh(cat); //re-read the state (after the trigger executes) - List<?> cats = em.createQuery( + List<?> cats = em.createQuery( "select cat from Cat as cat where cat.birthdate < ?1") .setParameter(1, date, TemporalType.DATE) .getResultList(); @@ -176,7 +176,7 @@ Cat mother = (Cat) em.createQuery( type-safe approach is the Criteria API explained in . - CriteriaQuery<Cat> criteria = builder.createQuery( Cat.class ); + 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 ) ); @@ -186,7 +186,7 @@ List<Cat> cats = em.createQuery( criteria ).getResultList(); //notice no d 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 + //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) @@ -205,7 +205,7 @@ List<Cat> cats = em.createQuery( JPA queries can return tuples of objects if projection is used. Each result tuple is returned as an object array: - Iterator kittensAndMothers = sess.createQuery( + Iterator kittensAndMothers = sess.createQuery( "select kitten, mother from Cat kitten join kitten.mother mother") .getResultList() .iterator(); @@ -233,7 +233,7 @@ while ( kittensAndMothers.hasNext() ) { persistent state (in other words, they are considered "read only"): - Iterator results = em.createQuery( + Iterator results = em.createQuery( "select cat.color, min(cat.birthdate), count(cat) from Cat cat " + "group by cat.color") .getResultList() @@ -258,7 +258,7 @@ while ( results.hasNext() ) { in the query string. Named parameters should be preferred, they are more robust and easier to read and understand: - // Named parameter (preferred) + // Named parameter (preferred) Query q = em.createQuery("select cat from DomesticCat cat where cat.name = :name"); q.setParameter("name", "Fritz"); List cats = q.getResultList(); @@ -284,7 +284,7 @@ List cats = q.list(); number of rows you want to retrieve and/or the first row you want to retrieve), use the following methods: - Query q = em.createQuery("select cat from DomesticCat cat"); + 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 @@ -298,20 +298,20 @@ List cats = q.getResultList(); //return cats from the 20th position to 29thYou may also define named queries through annotations: - @javax.persistence.NamedQuery(name="eg.DomesticCat.by.name.and.minimum.weight", + @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"); + 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); + 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(); @@ -335,7 +335,7 @@ List<Cat> cats = q.getResultList(); remember that all entity columns have to be returned for this mechanism to work): - @SqlResultSetMapping(name="getItem", entities = + @SqlResultSetMapping(name="getItem", entities = @EntityResult(entityClass=org.hibernate.ejb.test.Item.class, fields= { @FieldResult(name="name", column="itemname"), @FieldResult(name="descr", column="itemdescription") @@ -358,7 +358,7 @@ item = (Item) q.getSingleResult(); //from a class columns names match the mappin Query lock and flush mode You can adjust the flush mode used when executing the query as - well as define the lock mode used to load the entities. + well as define the lock mode used to load the entities. Adjusting the flush mode is interesting when one must guaranty that a query execution will not trigger a flush operation. Most of the @@ -367,7 +367,7 @@ item = (Item) q.getSingleResult(); //from a class columns names match the mappin Adjusting the lock mode is useful if you need to lock the objects returns by the query to a certain level. - query.setFlushMode(FlushModeType.COMMIT) + query.setFlushMode(FlushModeType.COMMIT) .setLockMode(LockModeType.PESSIMISTIC_READ); @@ -492,7 +492,7 @@ item = (Item) q.getSingleResult(); //from a class columns names match the mappin find() it, and then manipulate it directly, while the persistence context is open: - Cat cat = em.find( Cat.class, new Long(69) ); + Cat cat = em.find( Cat.class, new Long(69) ); cat.setName("PK"); em.flush(); // changes to cat are automatically detected and persisted @@ -510,7 +510,7 @@ em.flush(); // changes to cat are automatically detected and persisteddetach() method. - Cat cat = em.find( Cat.class, new Long(69) ); + Cat cat = em.find( Cat.class, new Long(69) ); ... em.detach(cat); cat.setName("New name"); //not propatated to the database @@ -530,7 +530,7 @@ cat.setName("New name"); //not propatated to the database for persistence of modifications made to detached instances using the EntityManager.merge() method: - // in the first entity manager + // in the first entity manager Cat cat = firstEntityManager.find(Cat.class, catId); Cat potentialMate = new Cat(); firstEntityManager.persist(potentialMate); @@ -563,7 +563,7 @@ secondEntityManager.merge(mate); // update mate not a detached instance) to merge(), the entity manager will figure this out for you: - // In the first entity manager + // In the first entity manager Cat cat = firstEntityManager.find(Cat.class, catID); // In a higher layer of the application, detached @@ -739,7 +739,7 @@ secondEntityManager.merge(mate); // save the new instance automatically using the explained routine unless flush() is called explicitly. - em = emf.createEntityManager(); + em = emf.createEntityManager(); Transaction tx = em.getTransaction().begin(); em.setFlushMode(FlushModeType.COMMIT); // allow queries to return stale state @@ -817,11 +817,11 @@ em.getTransaction().commit(); // flush occurs associated entity (or collection of entities), you must indicate that in the association annotation: - @OneToOne(cascade=CascadeType.PERSIST) + @OneToOne(cascade=CascadeType.PERSIST) Cascading options can be combined: - @OneToOne(cascade= { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH } ) + @OneToOne(cascade= { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH } ) You may even use CascadeType.ALL to specify that all operations should be cascaded for a particular association. Remember @@ -924,4 +924,137 @@ em.getTransaction().commit(); // flush occurs nobody changes the data underneath while pessimistic locks enforce the lock right away and keep it till the transaction is committed. + +
+ Caching + + When the second-level cache is activated (see 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 CacheRetrieveMode + values + + + + 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 +
+ +
+ Checking the state of an object + + You can check whether an object is managed by the persistence + context + + entityManager.get(Cat.class, catId); +... +boolean isIn = entityManager.contains(cat); +assert isIn; + + You can also check whether an object, an association or a property + is lazy or not. You can do that independently of the underlying + persistence provider: + + PersistenceUtil jpaUtil = Persistence.getPersistenceUtil(); +if ( jpaUtil.isLoaded( customer.getAddress() ) { + //display address if loaded +} +if ( jpaUtil.isLoaded( customer.getOrders ) ) { + //display orders if loaded +} +if (jpaUtil.isLoaded(customer, "detailedBio") ) { + //display property detailedBio if loaded +} + + However, if you have access to the entityManagerFactory, we + recommend you to use: + + PersistenceUnitUtil jpaUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil(); + +Customer customer = entityManager.get( Customer.class, customerId ); + +if ( jpaUtil.isLoaded( customer.getAddress() ) { + //display address if loaded +} +if ( jpaUtil.isLoaded( customer.getOrders ) ) { + //display orders if loaded +} +if (jpaUtil.isLoaded(customer, "detailedBio") ) { + //display property detailedBio if loaded +} + +log.debug( "Customer id {}", jpaUtil.getIdentifier(customer) ); + + The performances are likely to be slightly better and you can get + the identifier value from an object (using + getIdentifier()). + + + These are roughly the counterpart methods of + Hibernate.isInitialize. + +
+ +
+ Native Hibernate API + + You can always fall back to the underlying + Session API from a given + EntityManager: + + Session session = entityManager.unwrap(Session.class); +
diff --git a/entitymanager/src/main/docbook/en/modules/listeners.xml b/entitymanager/src/main/docbook/en/modules/listeners.xml index ea18046301..2d3c5b1772 100644 --- a/entitymanager/src/main/docbook/en/modules/listeners.xml +++ b/entitymanager/src/main/docbook/en/modules/listeners.xml @@ -45,7 +45,7 @@ entity class with the @EntityListeners annotation: - @Entity + @Entity @EntityListeners(class=Audit.class) public class Cat { @Id private Integer id; @@ -221,7 +221,7 @@ public class LastUpdateListener { deployment descriptors. There is also an additional feature that can be useful: default event listeners. - <?xml version="1.0" encoding="UTF-8"?> + <?xml version="1.0" encoding="UTF-8"?> <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" diff --git a/entitymanager/src/main/docbook/en/modules/query_criteria.xml b/entitymanager/src/main/docbook/en/modules/query_criteria.xml index c6e3edc927..01cddd5f39 100644 --- a/entitymanager/src/main/docbook/en/modules/query_criteria.xml +++ b/entitymanager/src/main/docbook/en/modules/query_criteria.xml @@ -49,7 +49,7 @@ of the javax.persistence.EntityManagerFactory - CriteriaBuilder builder = entityManagerFactory.getCriteriaBuilder(); + CriteriaBuilder builder = entityManagerFactory.getCriteriaBuilder(); The next step is to obtain a javax.persistence.criteria.CriteriaQuery. You @@ -57,11 +57,11 @@ javax.persistence.criteria.CriteriaBuilder for this purpose. - CriteriaQuery<T> createQuery(Class<T>) + CriteriaQuery<T> createQuery(Class<T>) - CriteriaQuery<Tuple> createTupleQuery() + CriteriaQuery<Tuple> createTupleQuery() - CriteriaQuery<Object> createQuery() + CriteriaQuery<Object> createQuery() Each serves a different purpose depending on the expected type of the query results. @@ -77,7 +77,7 @@
Typed criteria queries - CriteriaQuery<T> createQuery(Class<T>) + CriteriaQuery<T> createQuery(Class<T>) The type of the criteria query (aka the <T>) indicates the expected types in the query result. This might be an entity, an Integer, @@ -106,7 +106,7 @@ - CriteriaQuery<Person> criteria = builder.createQuery( Person.class ); + CriteriaQuery<Person> criteria = builder.createQuery( Person.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( personRoot ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); @@ -160,7 +160,7 @@ for ( Person person : people ) { ... } - CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class ); + CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( personRoot.get( Person_.age ) ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); @@ -195,7 +195,7 @@ for ( Integer age : ages ) { ... } - CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class ); + CriteriaQuery<Integer> criteria = builder.createQuery( Integer.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( builder.max( personRoot.get( Person_.age ) ) ); criteria.where( builder.equal( personRoot.get( Person_.eyeColor ), "brown" ) ); @@ -239,7 +239,7 @@ Integer maxAge = em.createQuery( criteria ).getSingleResult(); - CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class ); + CriteriaQuery<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 ); @@ -284,7 +284,7 @@ for ( Object[] values : valueArray ) { - CriteriaQuery<Object[]> criteria = builder.createQuery( Object[].class ); + CriteriaQuery<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 ); @@ -351,7 +351,7 @@ for ( Object[] values : valueArray ) { - public class PersonWrapper { + public class PersonWrapper { private final Long id; private final Integer age; public PersonWrapper(Long id, Integer age) { @@ -433,7 +433,7 @@ for ( PersonWrapper person : people ) { ... } - CriteriaQuery<Tuple> criteria = builder.createTupleQuery(); + CriteriaQuery<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 ); @@ -493,7 +493,7 @@ for ( Tuple tuple : valueArray ) { typed - <X> X get(TupleElement<X> tupleElement) + <X> X get(TupleElement<X> tupleElement) This allows typed access to the underlying tuple elements. We see this in in @@ -508,9 +508,9 @@ for ( Tuple tuple : valueArray ) { positional - Object get(int i) + Object get(int i) - <X> X get(int i, Class<X> type) + <X> X get(int i, Class<X> type) Very similar to what we saw in and aliased - Object get(String alias) + Object get(String alias) - <X> X get(String alias, Class<X> type) + <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 @@ -574,14 +574,14 @@ for ( Tuple tuple : valueArray ) { from methods on javax.persistence.criteria.CriteriaQuery: - <X> Root<X> from(Class<X>) + <X> Root<X> from(Class<X>) - <X> Root<X> from(EntityType<X>) + <X> Root<X> from(EntityType<X>) Adding a root - CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); + CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); // create and add the root person.from( Person.class ); ... @@ -593,7 +593,7 @@ person.from( Person.class ); 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(); + CriteriaQuery query = builder.createQuery(); Root<Person> men = query.from( Person.class ); Root<Person> women = query.from( Person.class ); Predicate menRestriction = builder.and( @@ -620,7 +620,7 @@ query.where( builder.and( menRestriction, womenRestriction ) ); Example with Embedded and ManyToOne - CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); + CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); Root<Person> personRoot = person.from( Person.class ); // Person.address is an embedded attribute Join<Person,Address> personAddress = personRoot.join( Person_.address ); @@ -632,7 +632,7 @@ Join<Address,Country> addressCountry = personAddress.join( Address_.countr Example with Collections - CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); + 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 ); @@ -652,7 +652,7 @@ Join<Order,LineItem> orderLines = orders.join( Order_.lineItems ); Example with Embedded and ManyToOne - CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); + CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); Root<Person> personRoot = person.from( Person.class ); // Person.address is an embedded attribute Join<Person,Address> personAddress = personRoot.fetch( Person_.address ); @@ -672,7 +672,7 @@ Join<Address,Country> addressCountry = personAddress.fetch( Address_.count Example with Collections - CriteriaQuery<Person> personCriteria = builder.createQuery( Person.class ); + 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 ); @@ -704,7 +704,7 @@ Join<Order,LineItem> orderLines = orders.fetch( Order_.lineItems ); - CriteriaQuery<Person> criteria = build.createQuery( Person.class ); + CriteriaQuery<Person> criteria = build.createQuery( Person.class ); Root<Person> personRoot = criteria.from( Person.class ); criteria.select( personRoot ); ParameterExpression<String> eyeColorParam = builder.parameter( String.class ); diff --git a/entitymanager/src/main/docbook/en/modules/query_ejbql.xml b/entitymanager/src/main/docbook/en/modules/query_ejbql.xml index 4b9570f765..336626ead1 100644 --- a/entitymanager/src/main/docbook/en/modules/query_ejbql.xml +++ b/entitymanager/src/main/docbook/en/modules/query_ejbql.xml @@ -1,4 +1,4 @@ - + - - + - EJB-QL: The Object Query Language + JP-QL: The Object Query Language - - EJB3-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 EJB-QL. Actually, you - use the same query API for EJB-QL and HQL queries. Portable EJB3 applications however - should stick to EJB-QL or similar vendor extensions are needed. - + 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 . + Case Sensitivity @@ -45,7 +49,7 @@ org.hibernate.eg.Foo and foo.barSet is not foo.BARSET. - This manual uses lowercase EJBQL keywords. Some users find queries + This manual uses lowercase JP-QL keywords. Some users find queries with uppercase keywords more readable, but we find this convention ugly when embedded in Java code. @@ -53,28 +57,28 @@ The from clause - The simplest possible EJB-QL query is of the form: + The simplest possible JP-QL query is of the form: select c from eg.Cat c which simply returns all instances of the class eg.Cat. Unlike HQL, the select clause is not optional - in EJB-QL. We don't usually need to qualify the class name, since the + in JP-QL. We don't usually need to qualify the class name, since the entity name defaults to the unqualified class name (@Entity). So we almost always just write: select c from Cat c As you may have noticed you can assign aliases to classes, the - as keywork is optional. An alias allows you to refer - to Cat in other parts of the query. + as keywork is optional. An alias allows you to refer to + Cat in other parts of the query. select cat from Cat as cat Multiple classes may appear, resulting in a cartesian product or "cross" join. - select form, param from Formula as form, Parameter as param + select from, param from Formula as form, Parameter as param It is considered good practice to name query aliases using an initial lowercase, consistent with Java naming standards for local @@ -88,7 +92,7 @@ elements of a collection of values, using a join. - select cat, mate, kitten from Cat as cat + select cat, mate, kitten from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten @@ -109,18 +113,18 @@ The inner join, left outer join constructs may be abbreviated. - select cat, mate, kitten from Cat as cat + select cat, mate, kitten from Cat as cat join cat.mate as mate left join cat.kittens as kitten In addition, a "fetch" join allows associations or collections of values to be initialized along with their parent objects, using a single select. This is particularly useful in the case of a collection. It - effectively overrides the fetching options in the associations and collection - mapping metadata. See the Performance chapter of the Hibernate reference guide - for more information. + effectively overrides the fetching options in the associations and + collection mapping metadata. See the Performance chapter of the Hibernate + reference guide for more information. - select cat from Cat as cat + select cat from Cat as cat inner join fetch cat.mate left join fetch cat.kittens @@ -141,15 +145,12 @@ iterate(). Nor should fetch be used together with setMaxResults() or setFirstResult(). It is possible to create a cartesian - product by join fetching more than one collection in a query (as in the example - above), be careful the result of this product isn't bigger than you expect. - Join fetching multiple collection roles also sometimes gives unexpected results for - bag mappings, so be careful about how you formulate your queries in this case. - - - TODO: The last statement is useless and typical developer thinking, please elaborate. - The word "sometimes" should never appear in any technical documentation. - + product by join fetching more than one collection in a query (as in the + example above), be careful the result of this product isn't bigger than + you expect. Join fetching multiple collection roles gives unexpected + results for bag mappings as it is impossible for Hibernate to + differentiate legit duplicates of a given bag from artificial duplicates + created by the multi-table cartesian product. If you are using property-level lazy fetching (with bytecode instrumentation), it is possible to force Hibernate to fetch the lazy @@ -200,7 +201,8 @@ from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr - or as an actual typesafe Java object, + or as an actual type-safe Java object (often called a view + object), select new Family(mother, mate, offspr) from DomesticCat as mother @@ -254,7 +256,8 @@ from Cat cat You may use arithmetic operators, concatenation, and recognized SQL - functions in the select clause (dpending on configured dialect, HQL specific feature): + functions in the select clause (dpending on configured dialect, HQL + specific feature): select cat.weight + sum(kitten.weight) from Cat cat @@ -281,7 +284,7 @@ select count(distinct cat.name), count(cat) from Cat cat returns instances not only of Cat, but also of subclasses like DomesticCat. Hibernate queries may name any Java class or interface in the - from clause (portable EJB-QL queries should only name + from clause (portable JP-QL queries should only name mapped entities). The query will return instances of all persistent classes that extend that class or implement the interface. The following query would return all persistent objects: @@ -342,7 +345,8 @@ where cat.mate = mate The special property (lowercase) id may be used to reference the unique identifier of an object. (You may also use its - mapped identifer property name.). Note that this keyword is specific to HQL. + mapped identifer property name.). Note that this keyword is specific to + HQL. select cat from Cat as cat where cat.id = 123 @@ -426,15 +430,21 @@ where log.item.class = 'Payment' and log.item.id = payment.id of + + exists, all, + any, some (taking + subqueries) + + "Simple" case, case ... when ... then ... else ... end, and "searched" case, case when ... then ... - else ... end (specific to HQL) + else ... end string concatenation ...||... or - concat(...,...) (use concat() for portable EJB-QL + concat(...,...) (use concat() for portable JP-QL queries) @@ -452,9 +462,9 @@ where log.item.class = 'Payment' and log.item.id = payment.id - Any function or operator defined by EJB-QL 3.0: - substring(), trim(), lower(), upper(), length(), locate(), - abs(), sqrt(), bit_length() + Any function or operator: substring(), trim(), lower(), + upper(), length(), locate(), abs(), sqrt(), + bit_length() @@ -462,6 +472,13 @@ where log.item.class = 'Payment' and log.item.id = payment.id nullif() + + TYPE ... in ..., where the first argument is + an identifier variable and the second argument is the subclass to + restrict polymorphism to (or a list of subclasses surrounded by + parenthesis) + + cast(... as ...), where the second argument is the name of a Hibernate type, and extract(... from @@ -490,6 +507,11 @@ where log.item.class = 'Payment' and log.item.id = payment.id '1970-01-01 10:00:01.0' + + JDBC escape syntax for dates (dependent on your JDBC driver + support) (eg. where date = {d '2008-12-31'}) + + Java public static final constants eg.Color.TABBY @@ -548,7 +570,7 @@ where log.item.class = 'Payment' and log.item.id = payment.id supported when passed the element or index set of a collection (elements and indices functions) or the result of a subquery (see below). While subqueries are supported by - EJB-QL, elements and indices are + JP-QL, elements and indices are specific HQL features. select mother from Cat as mother, Cat as kit @@ -567,7 +589,13 @@ where p.name = some elements(list.names) elements, indices, minindex, maxindex, minelement, maxelement - may only be - used in the where clause in Hibernate3. + used in the where clause in Hibernate. + + JP-QL lets you access the key or the value of a map by using the + KEY() and VALUE() operations (even + access the Entry object using ENTRY()) + + SELECT i.name, VALUE(p) FROM Item i JOIN i.photos p WHERE KEY(p) LIKE ‘%egret’ In HQL, elements of indexed collections (arrays, lists, maps) may be referred to by index (in a where clause only): @@ -687,7 +715,7 @@ order by count(kitten) asc, sum(kitten.weight) desc Subqueries - For databases that support subselects, EJB-QL supports subqueries + For databases that support subselects, JP-QL supports subqueries within queries. A subquery must be surrounded by parentheses (often by an SQL aggregate function call). Even correlated subqueries (subqueries that refer to an alias in the outer query) are allowed. @@ -737,11 +765,11 @@ where not ( cat.name, cat.color ) in ( - EJB-QL examples + JP-QL examples Hibernate queries can be quite powerful and complex. In fact, the power of the query language is one of Hibernate's main selling points (and - now EJB-QL). Here are some example queries very similar to queries that I + now JP-QL). Here are some example queries very similar to queries that I used on a recent project. Note that most queries you will write are much simpler than these! @@ -848,8 +876,8 @@ order by account.type.sortOrder, account.accountNumber, payment.dueDate Bulk UPDATE & DELETE Statements - Hibernate now supports UPDATE and DELETE statements in HQL/EJB-QL. See for details. + Hibernate now supports UPDATE and DELETE statements in HQL/JP-QL. + See for details. @@ -888,4 +916,4 @@ from User as usr group by usr.id, usr.name having count(msg) = 0 - \ No newline at end of file + diff --git a/entitymanager/src/main/docbook/en/modules/query_native.xml b/entitymanager/src/main/docbook/en/modules/query_native.xml index 6c4748f112..18d7ab1ce8 100644 --- a/entitymanager/src/main/docbook/en/modules/query_native.xml +++ b/entitymanager/src/main/docbook/en/modules/query_native.xml @@ -1,4 +1,4 @@ - + - - + Native query @@ -31,7 +31,7 @@ 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 Hibernate3 allows you to specify handwritten SQL (including stored + 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.) @@ -42,10 +42,10 @@ description will help the EntityManager to map your columns onto entity properties. This is done using the @SqlResultSetMapping annotation. Each - @SqlResultSetMapping has a name wich is used when + @SqlResultSetMapping has a name which is used when creating a SQL query on EntityManager. - @SqlResultSetMapping(name="GetNightAndArea", entities={ + @SqlResultSetMapping(name="GetNightAndArea", entities={ @EntityResult(name="org.hibernate.test.annotations.query.Night", fields = { @FieldResult(name="id", column="nid"), @FieldResult(name="duration", column="night_duration"), @@ -65,7 +65,7 @@ You can also define scalar results and even mix entity results and scalar results - @SqlResultSetMapping(name="ScalarAndEntities", + @SqlResultSetMapping(name="ScalarAndEntities", entities={ @EntityResult(name="org.hibernate.test.annotations.query.Night", fields = { @FieldResult(name="id", column="nid"), @@ -103,7 +103,7 @@ third one (not yet supported by Hibernate entity manager), returns pure scalar results. - String sqlQuery = "select night.id nid, night.night_duration, night.night_date, area.id aid, " + 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"); @@ -113,7 +113,7 @@ q.getResultList(); This native query returns nights and area based on the GetNightAndArea result set. - String sqlQuery = "select * from tbl_spaceship where owner = ?"; + String sqlQuery = "select * from tbl_spaceship where owner = ?"; Query q = entityManager.createNativeQuery(sqlQuery, SpaceShip.class); q.setParameter( 1, "Han" ); q.getResultList(); @@ -125,12 +125,12 @@ q.getResultList(); Named queries - Native named queries share the same calling API than EJB-QL named + Native named queries share the same calling API than JP-QL named queries. Your code doesn't need to know the difference between the two. - This is very useful for migration from SQL to EJB-QL: + This is very useful for migration from SQL to JP-QL: - Query q = entityManager.createNamedQuery("getSeasonByNativeQuery"); + Query q = entityManager.createNamedQuery("getSeasonByNativeQuery"); q.setParameter( 1, name ); Season season = (Season) q.getSingleResult(); - \ No newline at end of file + diff --git a/entitymanager/src/main/docbook/en/modules/transactions.xml b/entitymanager/src/main/docbook/en/modules/transactions.xml index d10c58969c..7ef102c8d4 100644 --- a/entitymanager/src/main/docbook/en/modules/transactions.xml +++ b/entitymanager/src/main/docbook/en/modules/transactions.xml @@ -418,7 +418,7 @@ mechanism behind the scenes. The common entity manager and transaction handling idiom looks like this: - // Non-managed environment idiom + // Non-managed environment idiom EntityManager em = emf.createEntityManager(); EntityTransaction tx = null; try { @@ -501,7 +501,7 @@ finally { If you use bean-managed transactions (BMT), the code will look like this: - // BMT idiom + // BMT idiom @Resource public UserTransaction utx; @Resource public EntityManagerFactory factory; @@ -554,13 +554,13 @@ finally { Our entity manager/transaction management idiom for CMT and EJB3 container-use is reduced to this: - //CMT idiom through injection + //CMT idiom through injection @PersistenceContext(name="sample") EntityManager em; Or this if you use Java Context and Dependency Injection (CDI). - @Inject EntityManager em; + @Inject EntityManager em; In other words, all you have to do in a managed environment is to inject the EntityManager, do your data access work, @@ -834,7 +834,7 @@ finally { the least efficient in terms of database access. It is the approach most similar to EJB2 entities: - // foo is an instance loaded by a previous entity manager + // foo is an instance loaded by a previous entity manager em = factory.createEntityManager(); EntityTransaction t = em.getTransaction(); t.begin(); @@ -895,7 +895,7 @@ em.close(); performance impact. The following examples show the idiom in a non-managed environment: - // foo is an instance loaded earlier by the extended entity manager + // foo is an instance loaded earlier by the extended entity manager em.getTransaction.begin(); // new connection to data store is obtained and tx started foo.setProperty("bar"); em.getTransaction().commit(); // End tx, flush and check version, disconnect @@ -936,7 +936,7 @@ em.getTransaction().commit(); // End tx, flush and check version, disconnect

EntityManager.merge(): - // foo is an instance loaded by a non-extended entity manager + // foo is an instance loaded by a non-extended entity manager foo.setProperty("bar"); entityManager = factory.createEntityManager(); entityManager.getTransaction().begin();