Hibernate SVN: r19965 - search/branches.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2010-07-19 09:50:41 -0400 (Mon, 19 Jul 2010)
New Revision: 19965
Added:
search/branches/Branch_3_2/
Log:
create Hibernate Search Branch 3.2
Copied: search/branches/Branch_3_2 (from rev 19964, search/tags/v3_2_0_Final)
14 years, 4 months
Hibernate SVN: r19964 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2010-07-16 16:53:11 -0400 (Fri, 16 Jul 2010)
New Revision: 19964
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml
Log:
HHH-5389
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml 2010-07-16 19:46:12 UTC (rev 19963)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml 2010-07-16 20:53:11 UTC (rev 19964)
@@ -928,15 +928,42 @@
<para>Hibernate3 can use custom SQL for create, update, and delete
operations. The SQL can be overridden at the statement level or
inidividual column level. This section describes statement overrides. For
- columns, see <xref linkend="mapping-column-read-and-write" />.</para>
+ columns, see <xref linkend="mapping-column-read-and-write" />. <xref
+ linkend="example-custom-crdu-via-annotations" /> shows how to define
+ custom SQL operatons using annotations.</para>
- <para>The class and collection persisters in Hibernate already contain a
- set of configuration time generated strings (insertsql, deletesql,
- updatesql etc.). The mapping tags <literal><sql-insert></literal>,
- <literal><sql-delete></literal>, and
- <literal><sql-update></literal> override these strings:</para>
+ <example id="example-custom-crdu-via-annotations">
+ <title>Custom CRUD via annotations</title>
- <programlisting role="XML"><class name="Person">
+ <programlisting language="JAVA" role="JAVA">@Entity
+@Table(name="CHAOS")
+@SQLInsert( sql="INSERT INTO CHAOS(size, name, nickname, id) VALUES(?,upper(?),?,?)")
+@SQLUpdate( sql="UPDATE CHAOS SET size = ?, name = upper(?), nickname = ? WHERE id = ?")
+@SQLDelete( sql="DELETE CHAOS WHERE id = ?")
+@SQLDeleteAll( sql="DELETE CHAOS")
+@Loader(namedQuery = "chaos")
+@NamedNativeQuery(name="chaos", query="select id, size, name, lower( nickname ) as nickname from CHAOS where id= ?", resultClass = Chaos.class)
+public class Chaos {
+ @Id
+ private Long id;
+ private Long size;
+ private String name;
+ private String nickname;</programlisting>
+ </example>
+
+ <para><literal>@SQLInsert</literal>, <literal>@SQLUpdate</literal>,
+ <literal>@SQLDelete</literal>, <literal>@SQLDeleteAll</literal>
+ respectively override the INSERT, UPDATE, DELETE, and DELETE all
+ statement. The same can be achieved using Hibernate mapping files and the
+ <literal><sql-insert></literal>,
+ <literal><sql-update></literal> and
+ <literal><sql-delete></literal> nodes. This can be seen in <xref
+ linkend="example-custom-crdu-via-xml" />.</para>
+
+ <example id="example-custom-crdu-via-xml">
+ <title>Custom CRUD XML</title>
+
+ <programlisting role="XML"><class name="Person">
<id name="id">
<generator class="increment"/>
</id>
@@ -945,41 +972,100 @@
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class></programlisting>
+ </example>
- <para>The SQL is directly executed in your database, so you can use any
- dialect you like. This will reduce the portability of your mapping if you
- use database specific SQL.</para>
+ <para>If you expect to call a store procedure, be sure to set the
+ <literal>callable</literal> attribute to <constant>true</constant>. In
+ annotations as well as in xml. </para>
- <para>Stored procedures are supported if the <literal>callable</literal>
- attribute is set:</para>
+ <para>To check that the execution happens correctly, Hibernate allows you
+ to define one of those three strategies:</para>
- <programlisting role="XML"><class name="Person">
- <id name="id">
- <generator class="increment"/>
- </id>
- <property name="name" not-null="true"/>
- <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
- <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
- <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
-</class></programlisting>
+ <itemizedlist>
+ <listitem>
+ <para>none: no check is performed: the store procedure is expected to
+ fail upon issues</para>
+ </listitem>
- <para>The order of the positional parameters is vital, as they must be in
- the same sequence as Hibernate expects them.</para>
+ <listitem>
+ <para>count: use of rowcount to check that the update is
+ successful</para>
+ </listitem>
- <para>You can view the expected order by enabling debug logging for the
- <literal>org.hibernate.persister.entity</literal> level. With this level
- enabled, Hibernate will print out the static SQL that is used to create,
- update, delete etc. entities. To view the expected sequence, do not
- include your custom SQL in the mapping files, as this will override the
- Hibernate generated static SQL.</para>
+ <listitem>
+ <para>param: like COUNT but using an output parameter rather that the
+ standard mechanism</para>
+ </listitem>
+ </itemizedlist>
- <para>The stored procedures are in most cases required to return the
- number of rows inserted, updated and deleted, as Hibernate has some
- runtime checks for the success of the statement. Hibernate always
+ <para>To define the result check style, use the <literal>check</literal>
+ parameter which is again available in annoations as well as in xml.</para>
+
+ <para>You can use the exact same set of annotations respectively xml nodes
+ to override the collection related statements -see <xref
+ linkend="example-overriding-sql-collections-annotations" />.</para>
+
+ <example id="example-overriding-sql-collections-annotations">
+ <title>Overriding SQL statements for collections using
+ annotations</title>
+
+ <programlisting language="JAVA" role="JAVA">@OneToMany
+@JoinColumn(name="chaos_fk")
+@SQLInsert( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = ? where id = ?")
+@SQLDelete( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = null where id = ?")
+private Set<CasimirParticle> particles = new HashSet<CasimirParticle>();</programlisting>
+ </example>
+
+ <tip>
+ <para>The parameter order is important and is defined by the order
+ Hibernate handles properties. You can see the expected order by enabling
+ debug logging for the <literal>org.hibernate.persister.entity</literal>
+ level. With this level enabled Hibernate will print out the static SQL
+ that is used to create, update, delete etc. entities. (To see the
+ expected sequence, remember to not include your custom SQL through
+ annotations or mapping files as that will override the Hibernate
+ generated static sql)</para>
+ </tip>
+
+ <para>Overriding SQL statements for secondary tables is also possible
+ using <literal>@org.hibernate.annotations.Table</literal> and either (or
+ all) attributes <literal>sqlInsert</literal>,
+ <literal>sqlUpdate</literal>, <literal>sqlDelete</literal>:</para>
+
+ <example>
+ <title>Overriding SQL statements for secondary tables</title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+@SecondaryTables({
+ @SecondaryTable(name = "`Cat nbr1`"),
+ @SecondaryTable(name = "Cat2"})
+(a)org.hibernate.annotations.Tables( {
+ @Table(appliesTo = "Cat", comment = "My cat table" ),
+ @Table(appliesTo = "Cat2", foreignKey = @ForeignKey(name="FK_CAT2_CAT"), fetch = FetchMode.SELECT,
+ sqlInsert=@SQLInsert(sql="insert into Cat2(storyPart2, id) values(upper(?), ?)") )
+} )
+public class Cat implements Serializable {</programlisting>
+ </example>
+
+ <para>The previous example also shows that you can give a comment to a
+ given table (primary or secondary): This comment will be used for DDL
+ generation.</para>
+
+ <tip>
+ <para>The SQL is directly executed in your database, so you can use any
+ dialect you like. This will, however, reduce the portability of your
+ mapping if you use database specific SQL.</para>
+ </tip>
+
+ <para>Last but not least, stored procedures are in most cases required to
+ return the number of rows inserted, updated and deleted. Hibernate always
registers the first statement parameter as a numeric output parameter for
the CUD operations:</para>
- <programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ <example>
+ <title>Stored procedures and their return value</title>
+
+ <programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
@@ -992,6 +1078,7 @@
return SQL%ROWCOUNT;
END updatePerson;</programlisting>
+ </example>
</section>
<section id="querysql-load">
@@ -1052,5 +1139,9 @@
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query></programlisting>
+
+ <para>The annotation equivalent <literal><loader></literal> is the
+ @Loader annotation as seen in <xref
+ linkend="example-custom-crdu-via-annotations" />.</para>
</section>
</chapter>
14 years, 4 months
Hibernate SVN: r19963 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2010-07-16 15:46:12 -0400 (Fri, 16 Jul 2010)
New Revision: 19963
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/filters.xml
Log:
HHH-5153 Added @Filter and @FilterDef to filtering chapter
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/filters.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/filters.xml 2010-07-16 19:04:26 UTC (rev 19962)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/filters.xml 2010-07-16 19:46:12 UTC (rev 19963)
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
@@ -22,156 +22,194 @@
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
-
-<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;
-
]>
-
<chapter id="filters">
- <title>Filtering data</title>
-
- <para>
- Hibernate3 provides an innovative new approach to handling data with "visibility" rules.
- A <emphasis>Hibernate filter</emphasis> is a global, named, parameterized filter that can be
- enabled or disabled for a particular Hibernate session.
- </para>
+ <title>Filtering data</title>
- <section id="objectstate-filters" revision="1">
- <title>Hibernate filters</title>
+ <para>Hibernate3 provides an innovative new approach to handling data with
+ "visibility" rules. A <emphasis>Hibernate filter</emphasis> is a global,
+ named, parameterized filter that can be enabled or disabled for a particular
+ Hibernate session.</para>
- <para>
- Hibernate3 has the ability to pre-define filter criteria and attach those filters at both
- a class level and a collection level. A filter criteria allows you to define a restriction clause
- similar to the existing "where" attribute available on the class and various collection
- elements. These filter conditions, however, can be parameterized. The application can then
- decide at runtime whether certain filters should be enabled and what their parameter
- values should be. Filters can be used like database views, but they are parameterized inside the
- application.
- </para>
+ <section id="objectstate-filters" revision="1">
+ <title>Hibernate filters</title>
- <para>
- In order to use filters, they must first be defined and then attached to the appropriate
- mapping elements. To define a filter, use the <literal><filter-def/></literal> element
- within a <literal><hibernate-mapping/></literal> element:
- </para>
+ <para>Hibernate3 has the ability to pre-define filter criteria and attach
+ those filters at both a class level and a collection level. A filter
+ criteria allows you to define a restriction clause similar to the existing
+ "where" attribute available on the class and various collection elements.
+ These filter conditions, however, can be parameterized. The application
+ can then decide at runtime whether certain filters should be enabled and
+ what their parameter values should be. Filters can be used like database
+ views, but they are parameterized inside the application.</para>
- <programlisting role="XML"><![CDATA[<filter-def name="myFilter">
- <filter-param name="myFilterParam" type="string"/>
-</filter-def>]]></programlisting>
+ <para>Using annotatons filters are defined via
+ <literal>@org.hibernate.annotations.FilterDef</literal> or
+ <literal>@org.hibernate.annotations.FilterDefs</literal>. A filter
+ definition has a <methodname>name()</methodname> and an array of
+ parameters(). A parameter will allow you to adjust the behavior of the
+ filter at runtime. Each parameter is defined by a
+ <literal>@ParamDef</literal> which has a name and a type. You can also
+ define a <methodname>defaultCondition()</methodname> parameter for a given
+ <literal>@FilterDef</literal> to set the default condition to use when
+ none are defined in each individual <literal>@Filter</literal>.
+ <literal>@FilterDef</literal>(s) can be defined at the class or package
+ level. </para>
- <para>
- This filter can then be attached to a class:
- </para>
+ <para>We now need to define the SQL filter clause applied to either the
+ entity load or the collection load. <literal>@Filter</literal> is used and
+ placed either on the entity or the collection element. The connection
+ between <classname>@FilterName</classname> and
+ <classname>@Filter</classname> is a matching name.</para>
- <programlisting role="XML"><![CDATA[<class name="myClass" ...>
- ...
- <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
-</class>]]></programlisting>
+ <example>
+ <title>@FilterDef and @Filter annotations</title>
- <para>
- Or, to a collection:
- </para>
+ <programlisting language="JAVA" role="JAVA">@Entity
+@FilterDef(name="minLength", parameters=@ParamDef( name="minLength", type="integer" ) )
+@Filters( {
+ @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
+ @Filter(name="minLength", condition=":minLength <= length")
+} )
+public class Forest { ... }</programlisting>
+ </example>
- <programlisting role="XML"><![CDATA[<set ...>
- <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
-</set>]]></programlisting>
+ <para>When the collection use an association table as a relational
+ representation, you might want to apply the filter condition to the
+ association table itself or to the target entity table. To apply the
+ constraint on the target entity, use the regular
+ <literal>@Filter</literal> annotation. However, if you want to target the
+ association table, use the <literal>@FilterJoinTable</literal>
+ annotation.</para>
- <para>
- Or, to both or multiples of each at the same time.
- </para>
+ <example>
+ <title>Using <classname>@FilterJoinTable</classname> for filterting on
+ the association table</title>
- <para>
- The methods on <literal>Session</literal> are: <literal>enableFilter(String filterName)</literal>,
- <literal>getEnabledFilter(String filterName)</literal>, and <literal>disableFilter(String filterName)</literal>.
- By default, filters are <emphasis>not</emphasis> enabled for a given session. Filters must be
- enabled through use of the <literal>Session.enableFilter()</literal> method, which returns an
- instance of the <literal>Filter</literal> interface. If you used the simple filter defined above, it would
- look like this:
- </para>
+ <programlisting language="JAVA" role="JAVA"> @OneToMany
+ @JoinTable
+ //filter on the target entity table
+ @Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
+ //filter on the association table
+ @FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
+ public Set<Forest> getForests() { ... }</programlisting>
+ </example>
- <programlisting role="JAVA"><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
+ <para>Using Hibernate mapping files for defining filters the situtation is
+ very similar. The filters must first be defined and then attached to the
+ appropriate mapping elements. To define a filter, use the
+ <literal><filter-def/></literal> element within a
+ <literal><hibernate-mapping/></literal> element:</para>
- <para>
- Methods on the org.hibernate.Filter interface do allow the method-chaining common to much of Hibernate.
- </para>
+ <example>
+ <title>Defining a filter definition via
+ <literal><filter-def></literal></title>
- <para>
- The following is a full example, using temporal data with an effective record date pattern:
- </para>
+ <programlisting role="XML"><filter-def name="myFilter">
+ <filter-param name="myFilterParam" type="string"/>
+</filter-def></programlisting>
+ </example>
- <programlisting role="XML"><![CDATA[<filter-def name="effectiveDate">
- <filter-param name="asOfDate" type="date"/>
-</filter-def>
+ <para>This filter can then be attached to a class or collection (or, to
+ both or multiples of each at the same time):</para>
-<class name="Employee" ...>
+ <example>
+ <title>Attaching a filter to a class or collection using
+ <literal><filter></literal></title>
+
+ <programlisting role="XML"><class name="myClass" ...>
+ ...
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+
+ <set ...>
+ <filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
+ </set>
+</class></programlisting>
+ </example>
+
+ <para>The methods on <literal>Session</literal> are:
+ <literal>enableFilter(String filterName)</literal>,
+ <literal>getEnabledFilter(String filterName)</literal>, and
+ <literal>disableFilter(String filterName)</literal>. By default, filters
+ are <emphasis>not</emphasis> enabled for a given session. Filters must be
+ enabled through use of the <literal>Session.enableFilter()</literal>
+ method, which returns an instance of the <literal>Filter</literal>
+ interface. If you used the simple filter defined above, it would look like
+ this:</para>
+
+ <programlisting role="JAVA">session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");</programlisting>
+
+ <para>Methods on the org.hibernate.Filter interface do allow the
+ method-chaining common to much of Hibernate.</para>
+
+ <para>The following is a full example, using temporal data with an
+ effective record date pattern:</para>
+
+ <programlisting role="XML"><filter-def name="effectiveDate">
+ <filter-param name="asOfDate" type="date"/>
+</filter-def>
+
+<class name="Employee" ...>
...
- <many-to-one name="department" column="dept_id" class="Department"/>
- <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
- <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
+ <many-to-one name="department" column="dept_id" class="Department"/>
+ <property name="effectiveStartDate" type="date" column="eff_start_dt"/>
+ <property name="effectiveEndDate" type="date" column="eff_end_dt"/>
...
- <!--
+ <!--
Note that this assumes non-terminal records have an eff_end_dt set to
a max db date for simplicity-sake
- -->
- <filter name="effectiveDate"
- condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
-</class>
+ -->
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+</class>
-<class name="Department" ...>
+<class name="Department" ...>
...
- <set name="employees" lazy="true">
- <key column="dept_id"/>
- <one-to-many class="Employee"/>
- <filter name="effectiveDate"
- condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
- </set>
-</class>]]></programlisting>
+ <set name="employees" lazy="true">
+ <key column="dept_id"/>
+ <one-to-many class="Employee"/>
+ <filter name="effectiveDate"
+ condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
+ </set>
+</class></programlisting>
- <para>
- In order to ensure that you are provided with currently effective records,
- enable the filter on the session prior to retrieving employee data:
- </para>
+ <para>In order to ensure that you are provided with currently effective
+ records, enable the filter on the session prior to retrieving employee
+ data:</para>
-<programlisting role="JAVA"><![CDATA[Session session = ...;
+ <programlisting role="JAVA">Session session = ...;
session.enableFilter("effectiveDate").setParameter("asOfDate", new Date());
-List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
+List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
-]]></programlisting>
+</programlisting>
- <para>
- Even though a salary constraint was mentioned explicitly on the results in the above HQL,
- because of the enabled filter, the query will return only currently active employees who have
- a salary greater than one million dollars.
- </para>
+ <para>Even though a salary constraint was mentioned explicitly on the
+ results in the above HQL, because of the enabled filter, the query will
+ return only currently active employees who have a salary greater than one
+ million dollars.</para>
- <para>
- If you want to use filters with outer joining, either through HQL or load fetching, be
- careful of the direction of the condition expression. It is safest to set this up for left
- outer joining. Place the parameter first followed by the column name(s) after
- the operator.
- </para>
+ <para>If you want to use filters with outer joining, either through HQL or
+ load fetching, be careful of the direction of the condition expression. It
+ is safest to set this up for left outer joining. Place the parameter first
+ followed by the column name(s) after the operator.</para>
- <para>
- After being defined, a filter might be attached to multiple entities and/or
- collections each with its own condition. This can be problematic when the
- conditions are the same each time. Using <literal><filter-def/></literal>
- allows you to definine a default condition, either as an attribute or CDATA:
- </para>
+ <para>After being defined, a filter might be attached to multiple entities
+ and/or collections each with its own condition. This can be problematic
+ when the conditions are the same each time. Using
+ <literal><filter-def/></literal> allows you to definine a default
+ condition, either as an attribute or CDATA:</para>
- <programlisting role="XML"><![CDATA[<filter-def name="myFilter" condition="abc > xyz">...</filter-def>
-<filter-def name="myOtherFilter">abc=xyz</filter-def>]]></programlisting>
+ <programlisting role="XML"><filter-def name="myFilter" condition="abc > xyz">...</filter-def>
+<filter-def name="myOtherFilter">abc=xyz</filter-def></programlisting>
- <para>
- This default condition will be used whenever the filter is attached to something
- without specifying a condition. This means you can give a specific condition
- as part of the attachment of the filter that overrides the default condition in that
- particular case.
- </para>
-
- </section>
-
+ <para>This default condition will be used whenever the filter is attached
+ to something without specifying a condition. This means you can give a
+ specific condition as part of the attachment of the filter that overrides
+ the default condition in that particular case.</para>
+ </section>
</chapter>
-
14 years, 4 months
Hibernate SVN: r19962 - in core/trunk: core/src/main/java/org/hibernate/jdbc and 4 other directories.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2010-07-16 15:04:26 -0400 (Fri, 16 Jul 2010)
New Revision: 19962
Added:
core/trunk/core/src/main/java/org/hibernate/engine/transaction/NullSynchronizationException.java
core/trunk/core/src/main/java/org/hibernate/engine/transaction/SynchronizationRegistry.java
core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/
core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/AfterCompletionAction.java
core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/BeforeCompletionManagedFlushChecker.java
core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/CallbackCoordinator.java
core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/ExceptionMapper.java
core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/HibernateSynchronizationImpl.java
Modified:
core/trunk/core/src/main/java/org/hibernate/jdbc/JDBCContext.java
core/trunk/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java
Log:
HHH-5384 - HEM should not register its own Synchronization
Added: core/trunk/core/src/main/java/org/hibernate/engine/transaction/NullSynchronizationException.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/transaction/NullSynchronizationException.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/engine/transaction/NullSynchronizationException.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,41 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.engine.transaction;
+
+import org.hibernate.HibernateException;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public class NullSynchronizationException extends HibernateException {
+ public NullSynchronizationException() {
+ this( "Synchronization to register cannot be null" );
+ }
+
+ public NullSynchronizationException(String s) {
+ super( s );
+ }
+}
Added: core/trunk/core/src/main/java/org/hibernate/engine/transaction/SynchronizationRegistry.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/transaction/SynchronizationRegistry.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/engine/transaction/SynchronizationRegistry.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,101 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.engine.transaction;
+
+import java.util.LinkedHashSet;
+import javax.transaction.Synchronization;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.HibernateException;
+
+/**
+ * Manages a registry of {@link Synchronization Synchronizations}.
+ *
+ * @author Steve Ebersole
+ */
+public class SynchronizationRegistry {
+ private static final Logger log = LoggerFactory.getLogger( SynchronizationRegistry.class );
+
+ private LinkedHashSet<Synchronization> synchronizations;
+
+ /**
+ * Register a user {@link Synchronization} callback for this transaction.
+ *
+ * @param synchronization The synchronization callback to register.
+ *
+ * @throws HibernateException
+ */
+ public void registerSynchronization(Synchronization synchronization) {
+ if ( synchronization == null ) {
+ throw new NullSynchronizationException();
+ }
+
+ if ( synchronizations == null ) {
+ synchronizations = new LinkedHashSet<Synchronization>();
+ }
+
+ boolean added = synchronizations.add( synchronization );
+ if ( !added ) {
+ log.info( "Synchronization [{}] was already registered", synchronization );
+ }
+ }
+
+ /**
+ * Delegate {@link Synchronization#beforeCompletion} calls to {@link #registerSynchronization registered}
+ * {@link Synchronization Synchronizations}
+ */
+ public void notifySynchronizationsBeforeTransactionCompletion() {
+ if ( synchronizations != null ) {
+ for ( Synchronization synchronization : synchronizations ) {
+ try {
+ synchronization.beforeCompletion();
+ }
+ catch ( Throwable t ) {
+ log.error( "exception calling user Synchronization [{}]", synchronization, t );
+ }
+ }
+ }
+ }
+
+ /**
+ * Delegate {@link Synchronization#afterCompletion} calls to {@link #registerSynchronization registered}
+ * {@link Synchronization Synchronizations}
+ *
+ * @param status The transaction status (if known) per {@link javax.transaction.Status}
+ */
+ public void notifySynchronizationsAfterTransactionCompletion(int status) {
+ if ( synchronizations != null ) {
+ for ( Synchronization synchronization : synchronizations ) {
+ try {
+ synchronization.afterCompletion( status );
+ }
+ catch ( Throwable t ) {
+ log.error( "exception calling user Synchronization [{}]", synchronization, t );
+ }
+ }
+ }
+ }
+}
Modified: core/trunk/core/src/main/java/org/hibernate/jdbc/JDBCContext.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/jdbc/JDBCContext.java 2010-07-16 15:27:49 UTC (rev 19961)
+++ core/trunk/core/src/main/java/org/hibernate/jdbc/JDBCContext.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -41,6 +41,8 @@
import org.hibernate.SessionException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
+import org.hibernate.transaction.synchronization.CallbackCoordinator;
+import org.hibernate.transaction.synchronization.HibernateSynchronizationImpl;
import org.hibernate.util.JTAHelper;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
@@ -82,6 +84,8 @@
private transient boolean isTransactionCallbackRegistered;
private transient Transaction hibernateTransaction;
+ private CallbackCoordinator jtaSynchronizationCallbackCoordinator;
+
public JDBCContext(Context owner, Connection connection, Interceptor interceptor) {
this.owner = owner;
this.connectionManager = new ConnectionManager(
@@ -107,6 +111,20 @@
private JDBCContext() {
}
+ public CallbackCoordinator getJtaSynchronizationCallbackCoordinator() {
+ return jtaSynchronizationCallbackCoordinator;
+ }
+
+ public CallbackCoordinator getJtaSynchronizationCallbackCoordinator(javax.transaction.Transaction jtaTransaction) {
+ jtaSynchronizationCallbackCoordinator = new CallbackCoordinator( owner, this, jtaTransaction, hibernateTransaction );
+ return jtaSynchronizationCallbackCoordinator;
+ }
+
+ public void cleanUpJtaSynchronizationCallbackCoordinator() {
+ jtaSynchronizationCallbackCoordinator = null;
+ }
+
+
// ConnectionManager.Callback implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public void connectionOpened() {
@@ -194,7 +212,10 @@
if ( hibernateTransaction == null ) {
hibernateTransaction = owner.getFactory().getSettings().getTransactionFactory().createTransaction( this, owner );
}
- tx.registerSynchronization( new CacheSynchronization(owner, this, tx, hibernateTransaction) );
+ tx.registerSynchronization(
+ new HibernateSynchronizationImpl( getJtaSynchronizationCallbackCoordinator( tx ) )
+ );
+// tx.registerSynchronization( new CacheSynchronization(owner, this, tx, hibernateTransaction) );
isTransactionCallbackRegistered = true;
log.debug("successfully registered Synchronization");
return true;
Modified: core/trunk/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java 2010-07-16 15:27:49 UTC (rev 19961)
+++ core/trunk/core/src/main/java/org/hibernate/transaction/JDBCTransaction.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
+ * distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@@ -20,13 +20,10 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
- *
*/
package org.hibernate.transaction;
import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
import javax.transaction.Status;
import javax.transaction.Synchronization;
@@ -36,11 +33,11 @@
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.hibernate.TransactionException;
+import org.hibernate.engine.transaction.SynchronizationRegistry;
import org.hibernate.jdbc.JDBCContext;
/**
- * {@link Transaction} implementation based on transaction management through
- * a JDBC {@link java.sql.Connection}.
+ * {@link Transaction} implementation based on transaction management through a JDBC {@link java.sql.Connection}.
* <p/>
* This the Hibernate's default transaction strategy.
*
@@ -48,9 +45,9 @@
* @author Gavin King
*/
public class JDBCTransaction implements Transaction {
-
private static final Logger log = LoggerFactory.getLogger(JDBCTransaction.class);
+ private final SynchronizationRegistry synchronizationRegistry = new SynchronizationRegistry();
private final JDBCContext jdbcContext;
private final TransactionFactory.Context transactionContext;
@@ -59,7 +56,6 @@
private boolean rolledBack;
private boolean committed;
private boolean commitFailed;
- private List synchronizations;
private boolean callback;
private int timeout = -1;
@@ -137,7 +133,7 @@
transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
}
- notifyLocalSynchsBeforeTransactionCompletion();
+ notifySynchronizationsBeforeTransactionCompletion();
if ( callback ) {
jdbcContext.beforeTransactionCompletion( this );
}
@@ -149,7 +145,7 @@
if ( callback ) {
jdbcContext.afterTransactionCompletion( true, this );
}
- notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
+ notifySynchronizationsAfterTransactionCompletion( Status.STATUS_COMMITTED );
}
catch (SQLException e) {
log.error("JDBC commit failed", e);
@@ -157,7 +153,7 @@
if ( callback ) {
jdbcContext.afterTransactionCompletion( false, this );
}
- notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
+ notifySynchronizationsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
throw new TransactionException("JDBC commit failed", e);
}
finally {
@@ -196,11 +192,11 @@
rollbackAndResetAutoCommit();
log.debug("rolled back JDBC Connection");
rolledBack = true;
- notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
+ notifySynchronizationsAfterTransactionCompletion(Status.STATUS_ROLLEDBACK);
}
catch (SQLException e) {
log.error("JDBC rollback failed", e);
- notifyLocalSynchsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
+ notifySynchronizationsAfterTransactionCompletion(Status.STATUS_UNKNOWN);
throw new TransactionException("JDBC rollback failed", e);
}
finally {
@@ -258,41 +254,17 @@
/**
* {@inheritDoc}
*/
- public void registerSynchronization(Synchronization sync) throws HibernateException {
- if (sync==null) throw new NullPointerException("null Synchronization");
- if (synchronizations==null) {
- synchronizations = new ArrayList();
- }
- synchronizations.add(sync);
+ public void registerSynchronization(Synchronization sync) {
+ synchronizationRegistry.registerSynchronization( sync );
}
- private void notifyLocalSynchsBeforeTransactionCompletion() {
- if (synchronizations!=null) {
- for ( int i=0; i<synchronizations.size(); i++ ) {
- Synchronization sync = (Synchronization) synchronizations.get(i);
- try {
- sync.beforeCompletion();
- }
- catch (Throwable t) {
- log.error("exception calling user Synchronization", t);
- }
- }
- }
+ private void notifySynchronizationsBeforeTransactionCompletion() {
+ synchronizationRegistry.notifySynchronizationsBeforeTransactionCompletion();
}
- private void notifyLocalSynchsAfterTransactionCompletion(int status) {
+ private void notifySynchronizationsAfterTransactionCompletion(int status) {
begun = false;
- if (synchronizations!=null) {
- for ( int i=0; i<synchronizations.size(); i++ ) {
- Synchronization sync = (Synchronization) synchronizations.get(i);
- try {
- sync.afterCompletion(status);
- }
- catch (Throwable t) {
- log.error("exception calling user Synchronization", t);
- }
- }
- }
+ synchronizationRegistry.notifySynchronizationsAfterTransactionCompletion( status );
}
/**
Added: core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/AfterCompletionAction.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/AfterCompletionAction.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/AfterCompletionAction.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,35 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.transaction.synchronization;
+
+import org.hibernate.transaction.TransactionFactory;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public interface AfterCompletionAction {
+ public void doAction(TransactionFactory.Context ctx, int status);
+}
Added: core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/BeforeCompletionManagedFlushChecker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/BeforeCompletionManagedFlushChecker.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/BeforeCompletionManagedFlushChecker.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,51 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.transaction.synchronization;
+
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+import org.hibernate.transaction.TransactionFactory;
+
+/**
+ * Contract for checking whether to perform a managed flush in a
+ * {@link javax.transaction.Synchronization#beforeCompletion()} callback
+ *
+ * @author Steve Ebersole
+ */
+public interface BeforeCompletionManagedFlushChecker {
+ /**
+ * Check whether we should perform the managed flush
+ *
+ * @param ctx The Hibernate "transaction context"
+ * @param jtaTransaction The JTA transaction
+ *
+ * @return True to indicate to perform the managed flush; false otherwise.
+ *
+ * @throws SystemException Can be thrown while accessing the JTA transaction; will result in transaction being
+ * marked for rollback (best effort).
+ */
+ public boolean shouldDoManagedFlush(TransactionFactory.Context ctx, Transaction jtaTransaction)
+ throws SystemException;
+}
Added: core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/CallbackCoordinator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/CallbackCoordinator.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/CallbackCoordinator.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,186 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.transaction.synchronization;
+
+import javax.persistence.spi.PersistenceUnitTransactionType;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.HibernateException;
+import org.hibernate.TransactionException;
+import org.hibernate.jdbc.JDBCContext;
+import org.hibernate.transaction.TransactionFactory;
+import org.hibernate.util.JTAHelper;
+
+/**
+ * Manages callbacks from the {@link javax.transaction.Synchronization} registered by Hibernate.
+ *
+ * @author Steve Ebersole
+ */
+public class CallbackCoordinator {
+ private static final Logger log = LoggerFactory.getLogger( CallbackCoordinator.class );
+
+ private final TransactionFactory.Context ctx;
+ private JDBCContext jdbcContext;
+ private final Transaction jtaTransaction;
+ private final org.hibernate.Transaction hibernateTransaction;
+
+ private BeforeCompletionManagedFlushChecker beforeCompletionManagedFlushChecker;
+ private AfterCompletionAction afterCompletionAction;
+ private ExceptionMapper exceptionMapper;
+
+ public CallbackCoordinator(
+ TransactionFactory.Context ctx,
+ JDBCContext jdbcContext,
+ Transaction jtaTransaction,
+ org.hibernate.Transaction hibernateTransaction) {
+ this.ctx = ctx;
+ this.jdbcContext = jdbcContext;
+ this.jtaTransaction = jtaTransaction;
+ this.hibernateTransaction = hibernateTransaction;
+ reset();
+ }
+
+ public void reset() {
+ beforeCompletionManagedFlushChecker = STANDARD_MANAGED_FLUSH_CHECKER;
+ exceptionMapper = STANDARD_EXCEPTION_MAPPER;
+ afterCompletionAction = STANDARD_AFTER_COMPLETION_ACTION;
+ }
+
+ public BeforeCompletionManagedFlushChecker getBeforeCompletionManagedFlushChecker() {
+ return beforeCompletionManagedFlushChecker;
+ }
+
+ public void setBeforeCompletionManagedFlushChecker(BeforeCompletionManagedFlushChecker beforeCompletionManagedFlushChecker) {
+ this.beforeCompletionManagedFlushChecker = beforeCompletionManagedFlushChecker;
+ }
+
+ public ExceptionMapper getExceptionMapper() {
+ return exceptionMapper;
+ }
+
+ public void setExceptionMapper(ExceptionMapper exceptionMapper) {
+ this.exceptionMapper = exceptionMapper;
+ }
+
+ public AfterCompletionAction getAfterCompletionAction() {
+ return afterCompletionAction;
+ }
+
+ public void setAfterCompletionAction(AfterCompletionAction afterCompletionAction) {
+ this.afterCompletionAction = afterCompletionAction;
+ }
+
+
+ // sync callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public void beforeCompletion() {
+ log.trace( "transaction before completion callback" );
+
+ boolean flush;
+ try {
+ flush = beforeCompletionManagedFlushChecker.shouldDoManagedFlush( ctx, jtaTransaction );
+ }
+ catch ( SystemException se ) {
+ setRollbackOnly();
+ throw exceptionMapper.mapStatusCheckFailure( "could not determine transaction status in beforeCompletion()", se );
+ }
+
+ try {
+ if ( flush ) {
+ log.trace( "automatically flushing session" );
+ ctx.managedFlush();
+ }
+ }
+ catch ( RuntimeException re ) {
+ setRollbackOnly();
+ throw exceptionMapper.mapManagedFlushFailure( "error during managed flush", re );
+ }
+ finally {
+ jdbcContext.beforeTransactionCompletion( hibernateTransaction );
+ }
+ }
+
+ private void setRollbackOnly() {
+ try {
+ jtaTransaction.setRollbackOnly();
+ }
+ catch ( SystemException se ) {
+ // best effort
+ log.error( "could not set transaction to rollback only", se );
+ }
+ }
+
+ public void afterCompletion(int status) {
+ log.trace( "transaction after completion callback [status={}]", status );
+
+ try {
+ afterCompletionAction.doAction( ctx, status );
+
+ final boolean wasSuccessful = ( status == Status.STATUS_COMMITTED );
+ jdbcContext.afterTransactionCompletion( wasSuccessful, hibernateTransaction );
+ }
+ finally {
+ reset();
+ jdbcContext.cleanUpJtaSynchronizationCallbackCoordinator();
+ if ( ctx.shouldAutoClose() && !ctx.isClosed() ) {
+ log.trace( "automatically closing session" );
+ ctx.managedClose();
+ }
+ }
+ }
+
+ private static final BeforeCompletionManagedFlushChecker STANDARD_MANAGED_FLUSH_CHECKER = new BeforeCompletionManagedFlushChecker() {
+ public boolean shouldDoManagedFlush(TransactionFactory.Context ctx, Transaction jtaTransaction)
+ throws SystemException {
+ return !ctx.isFlushModeNever() &&
+ ctx.isFlushBeforeCompletionEnabled() &&
+ !JTAHelper.isRollback( jtaTransaction.getStatus() );
+ //actually, this last test is probably unnecessary, since
+ //beforeCompletion() doesn't get called during rollback
+ }
+ };
+
+ private static final ExceptionMapper STANDARD_EXCEPTION_MAPPER = new ExceptionMapper() {
+ public RuntimeException mapStatusCheckFailure(String message, SystemException systemException) {
+ log.error( "could not determine transaction status [{}]", systemException.getMessage() );
+ return new TransactionException( "could not determine transaction status in beforeCompletion()", systemException );
+ }
+
+ public RuntimeException mapManagedFlushFailure(String message, RuntimeException failure) {
+ log.error( "Error during managed flush [{}]", failure.getMessage() );
+ return failure;
+ }
+ };
+
+ private static final AfterCompletionAction STANDARD_AFTER_COMPLETION_ACTION = new AfterCompletionAction() {
+ public void doAction(TransactionFactory.Context ctx, int status) {
+ // nothing to do by default.
+ }
+ };
+}
Added: core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/ExceptionMapper.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/ExceptionMapper.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/ExceptionMapper.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,53 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.transaction.synchronization;
+
+import javax.transaction.SystemException;
+
+/**
+ * TODO : javadoc
+ *
+ * @author Steve Ebersole
+ */
+public interface ExceptionMapper {
+ /**
+ * Map a JTA {@link SystemException} to the appropriate runtime-based exception.
+ *
+ * @param message The message to use for the returned exception
+ * @param systemException The causal exception
+ *
+ * @return The appropriate exception to throw
+ */
+ public RuntimeException mapStatusCheckFailure(String message, SystemException systemException);
+
+ /**
+ * Map an exception encountered during a managed flush to the appropriate runtime-based exception.
+ *
+ * @param message The message to use for the returned exception
+ * @param failure The causal exception
+ *
+ * @return The appropriate exception to throw
+ */
+ public RuntimeException mapManagedFlushFailure(String message, RuntimeException failure);
+}
Added: core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/HibernateSynchronizationImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/HibernateSynchronizationImpl.java (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/transaction/synchronization/HibernateSynchronizationImpl.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -0,0 +1,61 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.transaction.synchronization;
+
+import javax.transaction.Synchronization;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link Synchronization} implementation Hibernate registers with the JTA {@link javax.transaction.Transaction}
+ *
+ * @author Gavin King
+ * @author Steve Ebersole
+ */
+public class HibernateSynchronizationImpl implements Synchronization {
+ private static final Logger log = LoggerFactory.getLogger( HibernateSynchronizationImpl.class );
+
+ private final CallbackCoordinator coordinator;
+
+ public HibernateSynchronizationImpl(CallbackCoordinator coordinator) {
+ this.coordinator = coordinator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void beforeCompletion() {
+ log.trace( "JTA sync : beforeCompletion()" );
+ coordinator.beforeCompletion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void afterCompletion(int status) {
+ log.trace( "JTA sync : afterCompletion({})", status );
+ coordinator.afterCompletion( status );
+ }
+}
Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2010-07-16 15:27:49 UTC (rev 19961)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -101,6 +101,10 @@
import org.hibernate.engine.query.sql.NativeSQLQueryRootReturn;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.transaction.TransactionFactory;
+import org.hibernate.transaction.synchronization.AfterCompletionAction;
+import org.hibernate.transaction.synchronization.BeforeCompletionManagedFlushChecker;
+import org.hibernate.transaction.synchronization.CallbackCoordinator;
+import org.hibernate.transaction.synchronization.ExceptionMapper;
import org.hibernate.transform.BasicTransformerAdapter;
import org.hibernate.util.JTAHelper;
import org.hibernate.util.ReflectHelper;
@@ -1017,73 +1021,31 @@
//flush before completion and
//register clear on rollback
log.trace( "Adding flush() and close() synchronization" );
- joinableCMTTransaction.registerSynchronization(
- new Synchronization() {
- public void beforeCompletion() {
- boolean flush = false;
- TransactionFactory.Context ctx = null;
- try {
- ctx = ( TransactionFactory.Context ) session;
- JoinableCMTTransaction joinable = ( JoinableCMTTransaction ) session.getTransaction();
- javax.transaction.Transaction transaction = joinable.getTransaction();
- if ( transaction == null ) {
- log.warn(
- "Transaction not available on beforeCompletionPhase: assuming valid"
- );
- }
- flush = !ctx.isFlushModeNever() &&
- //ctx.isFlushBeforeCompletionEnabled() &&
- //TODO probably make it ! isFlushBeforecompletion()
- ( transaction == null || !JTAHelper.isRollback( transaction.getStatus() ) );
- //transaction == null workaround a JBoss TMBug
+ CallbackCoordinator callbackCoordinator = ( (SessionImplementor ) getSession() ).getJDBCContext().getJtaSynchronizationCallbackCoordinator();
+ if ( callbackCoordinator == null ) {
+ throw new AssertionFailure( "Expecting CallbackCoordinator to be non-null" );
+ }
+ callbackCoordinator.setBeforeCompletionManagedFlushChecker(
+ new BeforeCompletionManagedFlushChecker() {
+ public boolean shouldDoManagedFlush(TransactionFactory.Context ctx, javax.transaction.Transaction jtaTransaction)
+ throws SystemException {
+ if ( transaction == null ) {
+ log.warn( "Transaction not available on beforeCompletion: assuming valid" );
}
- catch ( SystemException se ) {
- log.error( "could not determine transaction status", se );
- PersistenceException pe = new PersistenceException(
- "could not determine transaction status in beforeCompletion()",
- se
- );
- // handlePersistenceException will mark the transaction as rollbacked
- handlePersistenceException( pe );
- throw pe;
- }
- catch ( HibernateException he ) {
- throwPersistenceException( he );
- }
-
- try {
- if ( flush ) {
- log.trace( "automatically flushing session" );
- ctx.managedFlush();
- }
- else {
- log.trace( "skipping managed flushing" );
- }
- }
- catch ( HibernateException he ) {
- throw convert( he );
- }
- catch ( PersistenceException pe ) {
- handlePersistenceException( pe );
- throw pe;
- }
- catch ( RuntimeException re ) {
- PersistenceException wrapped = new PersistenceException( re );
- handlePersistenceException( wrapped );
- throw wrapped;
- }
+ return !ctx.isFlushModeNever()
+ && ( jtaTransaction == null || !JTAHelper.isRollback( jtaTransaction.getStatus() ) );
}
-
- public void afterCompletion(int status) {
+ }
+ );
+ callbackCoordinator.setAfterCompletionAction(
+ new AfterCompletionAction() {
+ public void doAction(TransactionFactory.Context ctx, int status) {
try {
- if ( Status.STATUS_ROLLEDBACK == status
- && transactionType == PersistenceUnitTransactionType.JTA ) {
- if ( session.isOpen() ) {
+ if ( !ctx.isClosed() ) {
+ if ( Status.STATUS_ROLLEDBACK == status
+ && transactionType == PersistenceUnitTransactionType.JTA ) {
session.clear();
}
- }
- if ( session.isOpen() ) {
- //only reset if the session is opened since you can't get the Transaction otherwise
JoinableCMTTransaction joinable = ( JoinableCMTTransaction ) session.getTransaction();
joinable.resetStatus();
}
@@ -1094,6 +1056,23 @@
}
}
);
+ callbackCoordinator.setExceptionMapper(
+ new ExceptionMapper() {
+ public RuntimeException mapStatusCheckFailure(String message, SystemException systemException) {
+ throw new PersistenceException( message, systemException );
+ }
+
+ public RuntimeException mapManagedFlushFailure(String message, RuntimeException failure) {
+ if ( HibernateException.class.isInstance( failure ) ) {
+ throw convert( failure );
+ }
+ if ( PersistenceException.class.isInstance( failure ) ) {
+ throw failure;
+ }
+ throw new PersistenceException( message, failure );
+ }
+ }
+ );
}
else {
log.warn( "Cannot join transaction: do not override {}", Environment.TRANSACTION_STRATEGY );
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java 2010-07-16 15:27:49 UTC (rev 19961)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
+ * distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@@ -37,12 +37,10 @@
import org.hibernate.FlushMode;
import org.hibernate.Session;
-import javax.transaction.Synchronization;
-
/**
* @author Adam Warski (adam at warski dot org)
*/
-public class AuditProcess implements BeforeTransactionCompletionProcess, Synchronization {
+public class AuditProcess implements BeforeTransactionCompletionProcess {
private final RevisionInfoGenerator revisionInfoGenerator;
private final SessionImplementor session;
@@ -157,12 +155,4 @@
session.flush();
}
}
-
- // Synchronization methods
-
- public void beforeCompletion() {
- doBeforeTransactionCompletion(session);
- }
-
- public void afterCompletion(int status) { }
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java 2010-07-16 15:27:49 UTC (rev 19961)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java 2010-07-16 19:04:26 UTC (rev 19962)
@@ -55,18 +55,7 @@
auditProcess = new AuditProcess(revisionInfoGenerator, session);
auditProcesses.put(transaction, auditProcess);
- /*
- * HHH-5315: the process must be both a BeforeTransactionCompletionProcess and a TX Synchronization.
- *
- * In a resource-local tx env, the process is called after the flush, and populates the audit tables.
- * Also, any exceptions that occur during that are propagated (if a Synchronization was used, the exceptions
- * would be eaten).
- *
- * In a JTA env, the before transaction completion is called before the flush, so not all changes are yet
- * written. However, Synchronization-s do propagate exceptions, so they can be safely used.
- */
session.getActionQueue().registerProcess(auditProcess);
- session.getTransaction().registerSynchronization(auditProcess);
session.getActionQueue().registerProcess(new AfterTransactionCompletionProcess() {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
14 years, 4 months
Hibernate SVN: r19961 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2010-07-16 11:27:49 -0400 (Fri, 16 Jul 2010)
New Revision: 19961
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml
core/trunk/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml
core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml
core/trunk/documentation/manual/src/main/docbook/en-US/content/session_api.xml
Log:
HHH-5388 Added annotation documentation for named (native) queries, tuplizers and fetch profiles
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml 2010-07-16 15:24:32 UTC (rev 19960)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml 2010-07-16 15:27:49 UTC (rev 19961)
@@ -532,14 +532,45 @@
<interfacename>org.hibernate.SessionFactory</interfacename> but enabled,
by name, on the <interfacename>org.hibernate.Session</interfacename>.
Once enabled on a <interfacename>org.hibernate.Session</interfacename>,
- the fetch profile wull be in affect for that
+ the fetch profile will be in affect for that
<interfacename>org.hibernate.Session</interfacename> until it is
explicitly disabled.</para>
<para>So what does that mean? Well lets explain that by way of an
- example. Say we have the following mappings:</para>
+ example which show the different available approaches to configure a
+ fetch profile:</para>
- <programlisting role="XML"><hibernate-mapping>
+ <example>
+ <title>Specifying a fetch profile using
+ <classname>@FetchProfile</classname></title>
+
+ <programlisting role="XML">@Entity
+@FetchProfile(name = "customer-with-orders", fetchOverrides = {
+ @FetchProfile.FetchOverride(entity = Customer.class, association = "orders", mode = FetchMode.JOIN)
+})
+public class Customer {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+ private long customerNumber;
+
+ @OneToMany
+ private Set<Order> orders;
+
+ // standard getter/setter
+ ...
+}</programlisting>
+ </example>
+
+ <example>
+ <title>Specifying a fetch profile using
+ <literal><fetch-profile></literal> outside
+ <literal><class></literal> node</title>
+
+ <programlisting role="XML"><hibernate-mapping>
<class name="Customer">
...
<set name="orders" inverse="true">
@@ -550,46 +581,65 @@
<class name="Order">
...
</class>
-</hibernate-mapping></programlisting>
-
- <para>Now normally when you get a reference to a particular customer,
- that customer's set of orders will be lazy meaning we will not yet have
- loaded those orders from the database. Normally this is a good thing.
- Now lets say that you have a certain use case where it is more efficient
- to load the customer and their orders together. One way certainly is to
- use "dynamic fetching" strategies via an HQL or criteria queries. But
- another option is to use a fetch profile to achieve that. Just add the
- following to your mapping:</para>
-
- <programlisting role="XML"><hibernate-mapping>
- ...
<fetch-profile name="customer-with-orders">
<fetch entity="Customer" association="orders" style="join"/>
</fetch-profile>
-</hibernate-mapping></programlisting>
+</hibernate-mapping>
+</programlisting>
+ </example>
- <para>or even:</para>
+ <example>
+ <title>Specifying a fetch profile using
+ <literal><fetch-profile></literal> inside
+ <literal><class></literal> node</title>
- <programlisting role="XML"><hibernate-mapping>
+ <programlisting role="XML"><hibernate-mapping>
<class name="Customer">
...
+ <set name="orders" inverse="true">
+ <key column="cust_id"/>
+ <one-to-many class="Order"/>
+ </set>
<fetch-profile name="customer-with-orders">
<fetch association="orders" style="join"/>
</fetch-profile>
</class>
- ...
-</hibernate-mapping></programlisting>
+ <class name="Order">
+ ...
+ </class>
+</hibernate-mapping>
+</programlisting>
+ </example>
- <para>Now the following code will actually load both the customer
- <emphasis>and their orders</emphasis>:</para>
+ <para>Now normally when you get a reference to a particular customer,
+ that customer's set of orders will be lazy meaning we will not yet have
+ loaded those orders from the database. Normally this is a good thing.
+ Now lets say that you have a certain use case where it is more efficient
+ to load the customer and their orders together. One way certainly is to
+ use "dynamic fetching" strategies via an HQL or criteria queries. But
+ another option is to use a fetch profile to achieve that. The following
+ code will load both the customer <emphasis>and</emphasis>their
+ orders:</para>
- <programlisting role="JAVA">
- Session session = ...;
- session.enableFetchProfile( "customer-with-orders" ); // name matches from mapping
- Customer customer = (Customer) session.get( Customer.class, customerId );
+ <example>
+ <title>Activating a fetch profile for a given
+ <classname>Session</classname></title>
+ <programlisting role="JAVA">Session session = ...;
+session.enableFetchProfile( "customer-with-orders" ); // name matches from mapping
+Customer customer = (Customer) session.get( Customer.class, customerId );
</programlisting>
+ </example>
+ <note>
+ <para><classname>@FetchProfile </classname>definitions are global and
+ it does not matter on which class you place them. You can place the
+ <classname>@FetchProfile</classname> annotation either onto a class or
+ package (package-info.java). In order to define multiple fetch
+ profiles for the same class or package
+ <classname>@FetchProfiles</classname> can be used.</para>
+ </note>
+
<para>Currently only join style fetch profiles are supported, but they
plan is to support additional styles. See <ulink
url="http://opensource.atlassian.com/projects/hibernate/browse/HHH-3414">HHH-3414</ulink>
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml 2010-07-16 15:24:32 UTC (rev 19960)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/persistent_classes.xml 2010-07-16 15:27:49 UTC (rev 19961)
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
@@ -22,39 +22,34 @@
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
-
-<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;
-
]>
-
<chapter id="persistent-classes" revision="2">
- <title>Persistent Classes</title>
+ <title>Persistent Classes</title>
- <para>
- Persistent classes are classes in an application that implement the entities
- of the business problem (e.g. Customer and Order in an E-commerce application).
- Not all instances of a persistent class are considered to be in the persistent
- state. For example, an instance can instead be transient or detached.
- </para>
+ <para>Persistent classes are classes in an application that implement the
+ entities of the business problem (e.g. Customer and Order in an E-commerce
+ application). Not all instances of a persistent class are considered to be
+ in the persistent state. For example, an instance can instead be transient
+ or detached.</para>
- <para>
- Hibernate works best if these classes follow some simple rules, also known
- as the Plain Old Java Object (POJO) programming model. However, none of these
- rules are hard requirements. Indeed, Hibernate3 assumes very little about
- the nature of your persistent objects. You can express a domain model in other
- ways (using trees of <literal>Map</literal> instances, for example).
- </para>
+ <para>Hibernate works best if these classes follow some simple rules, also
+ known as the Plain Old Java Object (POJO) programming model. However, none
+ of these rules are hard requirements. Indeed, Hibernate3 assumes very little
+ about the nature of your persistent objects. You can express a domain model
+ in other ways (using trees of <literal>Map</literal> instances, for
+ example).</para>
- <section id="persistent-classes-pojo">
- <title>A simple POJO example</title>
+ <section id="persistent-classes-pojo">
+ <title>A simple POJO example</title>
- <para>
- Most Java applications require a persistent class representing felines. For example:
- </para>
+ <para>Most Java applications require a persistent class representing
+ felines. For example:</para>
- <programlisting role="JAVA"><![CDATA[package eg;
+ <programlisting role="JAVA">package eg;
import java.util.Set;
import java.util.Date;
@@ -131,124 +126,110 @@
kitten.setLitterId( kittens.size() );
kittens.add(kitten);
}
-}]]></programlisting>
+}</programlisting>
- <para>
- The four main rules of persistent classes are explored in more detail in the following sections.
- </para>
+ <para>The four main rules of persistent classes are explored in more
+ detail in the following sections.</para>
+ <section id="persistent-classes-pojo-constructor" revision="1">
+ <title>Implement a no-argument constructor</title>
- <section id="persistent-classes-pojo-constructor" revision="1">
- <title>Implement a no-argument constructor</title>
+ <para><literal>Cat</literal> has a no-argument constructor. All
+ persistent classes must have a default constructor (which can be
+ non-public) so that Hibernate can instantiate them using
+ <literal>Constructor.newInstance()</literal>. It is recommended that you
+ have a default constructor with at least <emphasis>package</emphasis>
+ visibility for runtime proxy generation in Hibernate.</para>
+ </section>
- <para>
- <literal>Cat</literal> has a no-argument constructor. All persistent classes must
- have a default constructor (which can be non-public) so that Hibernate can instantiate
- them using <literal>Constructor.newInstance()</literal>. It is recommended that you have a
- default constructor with at least <emphasis>package</emphasis> visibility for runtime proxy
- generation in Hibernate.
- </para>
- </section>
+ <section id="persistent-classes-pojo-identifier" revision="2">
+ <title>Provide an identifier property (optional)</title>
- <section id="persistent-classes-pojo-identifier" revision="2">
- <title>Provide an identifier property (optional)</title>
+ <para><literal>Cat</literal> has a property called
+ <literal>id</literal>. This property maps to the primary key column of a
+ database table. The property might have been called anything, and its
+ type might have been any primitive type, any primitive "wrapper" type,
+ <literal>java.lang.String</literal> or
+ <literal>java.util.Date</literal>. If your legacy database table has
+ composite keys, you can use a user-defined class with properties of
+ these types (see the section on composite identifiers later in the
+ chapter.)</para>
- <para>
- <literal>Cat</literal> has a property called <literal>id</literal>. This property
- maps to the primary key column of a database table. The property might have been called
- anything, and its type might have been any primitive type, any primitive "wrapper"
- type, <literal>java.lang.String</literal> or <literal>java.util.Date</literal>. If
- your legacy database table has composite keys, you can use a user-defined class
- with properties of these types (see the section on composite identifiers later in the chapter.)
- </para>
+ <para>The identifier property is strictly optional. You can leave them
+ off and let Hibernate keep track of object identifiers internally. We do
+ not recommend this, however.</para>
- <para>
- The identifier property is strictly optional. You can leave them off and let Hibernate
- keep track of object identifiers internally. We do not recommend this, however.
- </para>
+ <para>In fact, some functionality is available only to classes that
+ declare an identifier property:</para>
- <para>
- In fact, some functionality is available only to classes that declare an
- identifier property:
- </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>Transitive reattachment for detached objects (cascade update
+ or cascade merge) - see <xref
+ linkend="objectstate-transitive" /></para>
+ </listitem>
- <itemizedlist spacing="compact">
- <listitem>
- <para>
- Transitive reattachment for detached objects (cascade update or cascade
- merge) - see <xref linkend="objectstate-transitive"/>
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>Session.saveOrUpdate()</literal>
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>Session.merge()</literal>
- </para>
- </listitem>
- </itemizedlist>
+ <listitem>
+ <para><literal>Session.saveOrUpdate()</literal></para>
+ </listitem>
- <para>
- We recommend that you declare consistently-named identifier properties on persistent
- classes and that you use a nullable (i.e., non-primitive) type.
- </para>
- </section>
+ <listitem>
+ <para><literal>Session.merge()</literal></para>
+ </listitem>
+ </itemizedlist>
- <section id="persistent-classes-pojo-final">
- <title>Prefer non-final classes (optional)</title>
- <para>
- A central feature of Hibernate, <emphasis>proxies</emphasis>, depends upon the
- persistent class being either non-final, or the implementation of an interface
- that declares all public methods.
- </para>
- <para>
- You can persist <literal>final</literal> classes that do not implement an interface
- with Hibernate. You will not, however, be able to use proxies for lazy association fetching which
- will ultimately limit your options for performance tuning.
- </para>
- <para>
- You should also avoid declaring <literal>public final</literal> methods on the
- non-final classes. If you want to use a class with a <literal>public final</literal>
- method, you must explicitly disable proxying by setting <literal>lazy="false"</literal>.
- </para>
- </section>
-
- <section id="persistent-classes-pojo-accessors" revision="2">
- <title>Declare accessors and mutators for persistent fields (optional)</title>
+ <para>We recommend that you declare consistently-named identifier
+ properties on persistent classes and that you use a nullable (i.e.,
+ non-primitive) type.</para>
+ </section>
- <para>
- <literal>Cat</literal> declares accessor methods for all its persistent fields.
- Many other ORM tools directly persist instance variables. It is
- better to provide an indirection between the relational schema and internal
- data structures of the class. By default, Hibernate persists JavaBeans style
- properties and recognizes method names of the form <literal>getFoo</literal>,
- <literal>isFoo</literal> and <literal>setFoo</literal>. If required, you can switch to direct
- field access for particular properties.
- </para>
+ <section id="persistent-classes-pojo-final">
+ <title>Prefer non-final classes (optional)</title>
- <para>
- Properties need <emphasis>not</emphasis> be declared public - Hibernate can
- persist a property with a default, <literal>protected</literal> or
- <literal>private</literal> get / set pair.
- </para>
+ <para>A central feature of Hibernate, <emphasis>proxies</emphasis>,
+ depends upon the persistent class being either non-final, or the
+ implementation of an interface that declares all public methods.</para>
- </section>
+ <para>You can persist <literal>final</literal> classes that do not
+ implement an interface with Hibernate. You will not, however, be able to
+ use proxies for lazy association fetching which will ultimately limit
+ your options for performance tuning.</para>
+ <para>You should also avoid declaring <literal>public final</literal>
+ methods on the non-final classes. If you want to use a class with a
+ <literal>public final</literal> method, you must explicitly disable
+ proxying by setting <literal>lazy="false"</literal>.</para>
</section>
- <section id="persistent-classes-inheritance">
- <title>Implementing inheritance</title>
+ <section id="persistent-classes-pojo-accessors" revision="2">
+ <title>Declare accessors and mutators for persistent fields
+ (optional)</title>
- <para>
- A subclass must also observe the first and second rules. It inherits its
- identifier property from the superclass, <literal>Cat</literal>. For example:
- </para>
+ <para><literal>Cat</literal> declares accessor methods for all its
+ persistent fields. Many other ORM tools directly persist instance
+ variables. It is better to provide an indirection between the relational
+ schema and internal data structures of the class. By default, Hibernate
+ persists JavaBeans style properties and recognizes method names of the
+ form <literal>getFoo</literal>, <literal>isFoo</literal> and
+ <literal>setFoo</literal>. If required, you can switch to direct field
+ access for particular properties.</para>
- <programlisting role="JAVA"><![CDATA[package eg;
+ <para>Properties need <emphasis>not</emphasis> be declared public -
+ Hibernate can persist a property with a default,
+ <literal>protected</literal> or <literal>private</literal> get / set
+ pair.</para>
+ </section>
+ </section>
+ <section id="persistent-classes-inheritance">
+ <title>Implementing inheritance</title>
+
+ <para>A subclass must also observe the first and second rules. It inherits
+ its identifier property from the superclass, <literal>Cat</literal>. For
+ example:</para>
+
+ <programlisting role="JAVA">package eg;
+
public class DomesticCat extends Cat {
private String name;
@@ -258,63 +239,59 @@
protected void setName(String name) {
this.name=name;
}
-}]]></programlisting>
- </section>
+}</programlisting>
+ </section>
- <section id="persistent-classes-equalshashcode" revision="1">
- <title>Implementing <literal>equals()</literal> and <literal>hashCode()</literal></title>
+ <section id="persistent-classes-equalshashcode" revision="1">
+ <title>Implementing <literal>equals()</literal> and
+ <literal>hashCode()</literal></title>
- <para>
- You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
- methods if you:
- </para>
- <itemizedlist spacing="compact">
- <listitem>
- <para>
- intend to put instances of persistent classes in a <literal>Set</literal>
- (the recommended way to represent many-valued associations);
- <emphasis>and</emphasis>
- </para>
- </listitem>
- <listitem>
- <para>
- intend to use reattachment of detached instances
- </para>
- </listitem>
- </itemizedlist>
+ <para>You have to override the <literal>equals()</literal> and
+ <literal>hashCode()</literal> methods if you:</para>
- <para>
- Hibernate guarantees equivalence of persistent identity (database row) and Java identity
- only inside a particular session scope. When you mix instances retrieved in
- different sessions, you must implement <literal>equals()</literal> and
- <literal>hashCode()</literal> if you wish to have meaningful semantics for
- <literal>Set</literal>s.
- </para>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>intend to put instances of persistent classes in a
+ <literal>Set</literal> (the recommended way to represent many-valued
+ associations); <emphasis>and</emphasis></para>
+ </listitem>
- <para>
- The most obvious way is to implement <literal>equals()</literal>/<literal>hashCode()</literal>
- by comparing the identifier value of both objects. If the value is the same, both must
- be the same database row, because they are equal. If both are added to a <literal>Set</literal>,
- you will only have one element in the <literal>Set</literal>). Unfortunately, you cannot use that
- approach with generated identifiers. Hibernate will only assign identifier values to objects
- that are persistent; a newly created instance will not have any identifier value. Furthermore,
- if an instance is unsaved and currently in a <literal>Set</literal>, saving it will assign
- an identifier value to the object. If <literal>equals()</literal> and <literal>hashCode()</literal>
- are based on the identifier value, the hash code would change, breaking the contract of the
- <literal>Set</literal>. See the Hibernate website for a full discussion of this problem. This is not
- a Hibernate issue, but normal Java semantics of object identity and equality.
- </para>
+ <listitem>
+ <para>intend to use reattachment of detached instances</para>
+ </listitem>
+ </itemizedlist>
- <para>
- It is recommended that you implement <literal>equals()</literal> and <literal>hashCode()</literal>
- using <emphasis>Business key equality</emphasis>. Business key equality means that the
- <literal>equals()</literal> method compares only the properties that form the business
- key. It is a key that would identify our instance in the real world (a
- <emphasis>natural</emphasis> candidate key):
- </para>
+ <para>Hibernate guarantees equivalence of persistent identity (database
+ row) and Java identity only inside a particular session scope. When you
+ mix instances retrieved in different sessions, you must implement
+ <literal>equals()</literal> and <literal>hashCode()</literal> if you wish
+ to have meaningful semantics for <literal>Set</literal>s.</para>
- <programlisting role="JAVA"><![CDATA[public class Cat {
+ <para>The most obvious way is to implement
+ <literal>equals()</literal>/<literal>hashCode()</literal> by comparing the
+ identifier value of both objects. If the value is the same, both must be
+ the same database row, because they are equal. If both are added to a
+ <literal>Set</literal>, you will only have one element in the
+ <literal>Set</literal>). Unfortunately, you cannot use that approach with
+ generated identifiers. Hibernate will only assign identifier values to
+ objects that are persistent; a newly created instance will not have any
+ identifier value. Furthermore, if an instance is unsaved and currently in
+ a <literal>Set</literal>, saving it will assign an identifier value to the
+ object. If <literal>equals()</literal> and <literal>hashCode()</literal>
+ are based on the identifier value, the hash code would change, breaking
+ the contract of the <literal>Set</literal>. See the Hibernate website for
+ a full discussion of this problem. This is not a Hibernate issue, but
+ normal Java semantics of object identity and equality.</para>
+ <para>It is recommended that you implement <literal>equals()</literal> and
+ <literal>hashCode()</literal> using <emphasis>Business key
+ equality</emphasis>. Business key equality means that the
+ <literal>equals()</literal> method compares only the properties that form
+ the business key. It is a key that would identify our instance in the real
+ world (a <emphasis>natural</emphasis> candidate key):</para>
+
+ <programlisting role="JAVA">public class Cat {
+
...
public boolean equals(Object other) {
if (this == other) return true;
@@ -335,97 +312,85 @@
return result;
}
-}]]></programlisting>
+}</programlisting>
- <para>
- A business key does not have to be as solid as a database
- primary key candidate (see <xref linkend="transactions-basics-identity"/>).
- Immutable or unique properties are usually good
- candidates for a business key.
- </para>
+ <para>A business key does not have to be as solid as a database primary
+ key candidate (see <xref linkend="transactions-basics-identity" />).
+ Immutable or unique properties are usually good candidates for a business
+ key.</para>
+ </section>
- </section>
+ <section id="persistent-classes-dynamicmodels">
+ <title>Dynamic models</title>
- <section id="persistent-classes-dynamicmodels">
- <title>Dynamic models</title>
+ <note>
+ <title>Note</title>
-
- <note><title>Note</title>
-<para>
- <emphasis>The following features are currently considered
- experimental and may change in the near future.</emphasis>
-
- </para>
-</note>
- <para>
- Persistent entities do not necessarily have to be represented as POJO classes
- or as JavaBean objects at runtime. Hibernate also supports dynamic models
- (using <literal>Map</literal>s of <literal>Map</literal>s at runtime) and the
- representation of entities as DOM4J trees. With this approach, you do not
- write persistent classes, only mapping files.
- </para>
+ <para><emphasis>The following features are currently considered
+ experimental and may change in the near future.</emphasis></para>
+ </note>
- <para>
- By default, Hibernate works in normal POJO mode. You can set a default entity
- representation mode for a particular <literal>SessionFactory</literal> using the
- <literal>default_entity_mode</literal> configuration option (see
- <xref linkend="configuration-optional-properties"/>).
- </para>
+ <para>Persistent entities do not necessarily have to be represented as
+ POJO classes or as JavaBean objects at runtime. Hibernate also supports
+ dynamic models (using <literal>Map</literal>s of <literal>Map</literal>s
+ at runtime) and the representation of entities as DOM4J trees. With this
+ approach, you do not write persistent classes, only mapping files.</para>
- <para>
- The following examples demonstrate the representation using <literal>Map</literal>s.
- First, in the mapping file an <literal>entity-name</literal> has to be declared
- instead of, or in addition to, a class name:
- </para>
+ <para>By default, Hibernate works in normal POJO mode. You can set a
+ default entity representation mode for a particular
+ <literal>SessionFactory</literal> using the
+ <literal>default_entity_mode</literal> configuration option (see <xref
+ linkend="configuration-optional-properties" />).</para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
+ <para>The following examples demonstrate the representation using
+ <literal>Map</literal>s. First, in the mapping file an
+ <literal>entity-name</literal> has to be declared instead of, or in
+ addition to, a class name:</para>
- <class entity-name="Customer">
+ <programlisting role="XML"><hibernate-mapping>
- <id name="id"
+ <class entity-name="Customer">
+
+ <id name="id"
type="long"
- column="ID">
- <generator class="sequence"/>
- </id>
+ column="ID">
+ <generator class="sequence"/>
+ </id>
- <property name="name"
+ <property name="name"
column="NAME"
- type="string"/>
+ type="string"/>
- <property name="address"
+ <property name="address"
column="ADDRESS"
- type="string"/>
+ type="string"/>
- <many-to-one name="organization"
+ <many-to-one name="organization"
column="ORGANIZATION_ID"
- class="Organization"/>
+ class="Organization"/>
- <bag name="orders"
+ <bag name="orders"
inverse="true"
lazy="false"
- cascade="all">
- <key column="CUSTOMER_ID"/>
- <one-to-many class="Order"/>
- </bag>
+ cascade="all">
+ <key column="CUSTOMER_ID"/>
+ <one-to-many class="Order"/>
+ </bag>
- </class>
+ </class>
-</hibernate-mapping>]]></programlisting>
+</hibernate-mapping></programlisting>
- <para>
+ <para>Even though associations are declared using target class names, the
+ target type of associations can also be a dynamic entity instead of a
+ POJO.</para>
- Even though associations are declared using target class names,
- the target type of associations can also be a dynamic entity instead
- of a POJO.
- </para>
+ <para>After setting the default entity mode to
+ <literal>dynamic-map</literal> for the <literal>SessionFactory</literal>,
+ you can, at runtime, work with <literal>Map</literal>s of
+ <literal>Map</literal>s:</para>
- <para>
- After setting the default entity mode to <literal>dynamic-map</literal>
- for the <literal>SessionFactory</literal>, you can, at runtime, work with
- <literal>Map</literal>s of <literal>Map</literal>s:
- </para>
-
- <programlisting role="JAVA"><![CDATA[Session s = openSession();
+ <programlisting role="JAVA">Session s = openSession();
Transaction tx = s.beginTransaction();
// Create a customer
@@ -444,22 +409,19 @@
s.save("Organization", foobar);
tx.commit();
-s.close();]]></programlisting>
+s.close();</programlisting>
- <para>
- One of the main advantages of dynamic mapping is quick turnaround time for prototyping,
- without the need for entity class implementation. However, you lose compile-time
- type checking and will likely deal with many exceptions at runtime. As a result of
- the Hibernate mapping, the database schema can easily be normalized and sound,
- allowing to add a proper domain model implementation on top later on.
- </para>
+ <para>One of the main advantages of dynamic mapping is quick turnaround
+ time for prototyping, without the need for entity class implementation.
+ However, you lose compile-time type checking and will likely deal with
+ many exceptions at runtime. As a result of the Hibernate mapping, the
+ database schema can easily be normalized and sound, allowing to add a
+ proper domain model implementation on top later on.</para>
- <para>
- Entity representation modes can also be set on a per <literal>Session</literal>
- basis:
- </para>
+ <para>Entity representation modes can also be set on a per
+ <literal>Session</literal> basis:</para>
- <programlisting role="JAVA"><![CDATA[Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
+ <programlisting role="JAVA">Session dynamicSession = pojoSession.getSession(EntityMode.MAP);
// Create a customer
Map david = new HashMap();
@@ -470,112 +432,118 @@
dynamicSession.close()
...
// Continue on pojoSession
-]]></programlisting>
+</programlisting>
+ <para>Please note that the call to <literal>getSession()</literal> using
+ an <literal>EntityMode</literal> is on the <literal>Session</literal> API,
+ not the <literal>SessionFactory</literal>. That way, the new
+ <literal>Session</literal> shares the underlying JDBC connection,
+ transaction, and other context information. This means you do not have to
+ call <literal>flush()</literal> and <literal>close()</literal> on the
+ secondary <literal>Session</literal>, and also leave the transaction and
+ connection handling to the primary unit of work.</para>
- <para>
- Please note that the call to <literal>getSession()</literal> using an
- <literal>EntityMode</literal> is on the <literal>Session</literal> API, not the
- <literal>SessionFactory</literal>. That way, the new <literal>Session</literal>
- shares the underlying JDBC connection, transaction, and other context
- information. This means you do not have to call <literal>flush()</literal>
- and <literal>close()</literal> on the secondary <literal>Session</literal>, and
- also leave the transaction and connection handling to the primary unit of work.
- </para>
+ <para>More information about the XML representation capabilities can be
+ found in <xref linkend="xml" />.</para>
+ </section>
- <para>
- More information about the XML representation capabilities can be found
- in <xref linkend="xml"/>.
- </para>
+ <section id="persistent-classes-tuplizers" revision="1">
+ <title>Tuplizers</title>
- </section>
+ <para><literal>org.hibernate.tuple.Tuplizer</literal>, and its
+ sub-interfaces, are responsible for managing a particular representation
+ of a piece of data given that representation's
+ <literal>org.hibernate.EntityMode</literal>. If a given piece of data is
+ thought of as a data structure, then a tuplizer is the thing that knows
+ how to create such a data structure and how to extract values from and
+ inject values into such a data structure. For example, for the POJO entity
+ mode, the corresponding tuplizer knows how create the POJO through its
+ constructor. It also knows how to access the POJO properties using the
+ defined property accessors.</para>
- <section id="persistent-classes-tuplizers" revision="1">
- <title>Tuplizers</title>
+ <para>There are two high-level types of Tuplizers, represented by the
+ <literal>org.hibernate.tuple.entity.EntityTuplizer</literal> and
+ <literal>org.hibernate.tuple.component.ComponentTuplizer</literal>
+ interfaces. <literal>EntityTuplizer</literal>s are responsible for
+ managing the above mentioned contracts in regards to entities, while
+ <literal>ComponentTuplizer</literal>s do the same for components.</para>
- <para>
- <literal>org.hibernate.tuple.Tuplizer</literal>, and its sub-interfaces, are responsible
- for managing a particular representation of a piece of data given that representation's
- <literal>org.hibernate.EntityMode</literal>. If a given piece of data is thought of as
- a data structure, then a tuplizer is the thing that knows how to create such a data structure
- and how to extract values from and inject values into such a data structure. For example,
- for the POJO entity mode, the corresponding tuplizer knows how create the POJO through its
- constructor. It also knows how to access the POJO properties using the defined property accessors.
- </para>
+ <para>Users can also plug in their own tuplizers. Perhaps you require that
+ a <literal>java.util.Map</literal> implementation other than
+ <literal>java.util.HashMap</literal> be used while in the dynamic-map
+ entity-mode. Or perhaps you need to define a different proxy generation
+ strategy than the one used by default. Both would be achieved by defining
+ a custom tuplizer implementation. Tuplizer definitions are attached to the
+ entity or component mapping they are meant to manage. Going back to the
+ example of our customer entity <xref
+ linkend="example-defining-tuplizer-using-annotations" /> shows how to
+ configure tuplizers using annotations whereas <xref
+ linkend="example-defining-tuplizer-using-mapping-file" /> shows how to do
+ the same thing using Hibernate mapping files.</para>
- <para>
- There are two high-level types of Tuplizers, represented by the
- <literal>org.hibernate.tuple.entity.EntityTuplizer</literal> and <literal>org.hibernate.tuple.component.ComponentTuplizer</literal>
- interfaces. <literal>EntityTuplizer</literal>s are responsible for managing the above mentioned
- contracts in regards to entities, while <literal>ComponentTuplizer</literal>s do the same for
- components.
- </para>
+ <example id="example-defining-tuplizer-using-annotations">
+ <title>Mapping custom tuplizers using annotations</title>
- <para>
- Users can also plug in their own tuplizers. Perhaps you require that a <literal>java.util.Map</literal>
- implementation other than <literal>java.util.HashMap</literal> be used while in the
- dynamic-map entity-mode. Or perhaps you need to define a different proxy generation strategy
- than the one used by default. Both would be achieved by defining a custom tuplizer
- implementation. Tuplizer definitions are attached to the entity or component mapping they
- are meant to manage. Going back to the example of our customer entity:
- </para>
+ <programlisting role="XML">@Entity
+@Tuplizer(impl = DynamicEntityTuplizer.class)
+public interface Cuisine {
+ @Id
+ @GeneratedValue
+ public Long getId();
+ public void setId(Long id);
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
- <class entity-name="Customer">
- <!--
+ public String getName();
+ public void setName(String name);
+
+ @Tuplizer(impl = DynamicComponentTuplizer.class)
+ public Country getCountry();
+ public void setCountry(Country country);
+}</programlisting>
+ </example>
+
+ <example>
+ <title id="example-defining-tuplizer-using-mapping-file">Mapping custom
+ tuplizers using mapping files</title>
+
+ <programlisting role="XML"><hibernate-mapping>
+ <class entity-name="Customer">
+ <!--
Override the dynamic-map entity-mode
tuplizer for the customer entity
- -->
- <tuplizer entity-mode="dynamic-map"
- class="CustomMapTuplizerImpl"/>
+ -->
+ <tuplizer entity-mode="dynamic-map"
+ class="CustomMapTuplizerImpl"/>
- <id name="id" type="long" column="ID">
- <generator class="sequence"/>
- </id>
+ <id name="id" type="long" column="ID">
+ <generator class="sequence"/>
+ </id>
- <!-- other properties -->
+ <!-- other properties -->
...
- </class>
-</hibernate-mapping>
+ </class>
+</hibernate-mapping>
+</programlisting>
+ </example>
+ </section>
+ <section id="persistent-classes-entity-name-resolver" revision="0">
+ <title>EntityNameResolvers</title>
-public class CustomMapTuplizerImpl
- extends org.hibernate.tuple.entity.DynamicMapEntityTuplizer {
- // override the buildInstantiator() method to plug in our custom map...
- protected final Instantiator buildInstantiator(
- org.hibernate.mapping.PersistentClass mappingInfo) {
- return new CustomMapInstantiator( mappingInfo );
- }
+ <para>The <interfacename>org.hibernate.EntityNameResolver</interfacename>
+ interface is a contract for resolving the entity name of a given entity
+ instance. The interface defines a single method
+ <methodname>resolveEntityName</methodname> which is passed the entity
+ instance and is expected to return the appropriate entity name (null is
+ allowed and would indicate that the resolver does not know how to resolve
+ the entity name of the given entity instance). Generally speaking, an
+ <interfacename>org.hibernate.EntityNameResolver</interfacename> is going
+ to be most useful in the case of dynamic models. One example might be
+ using proxied interfaces as your domain model. The hibernate test suite
+ has an example of this exact style of usage under the
+ <package>org.hibernate.test.dynamicentity.tuplizer2</package>. Here is
+ some of the code from that package for illustration.</para>
- private static final class CustomMapInstantiator
- extends org.hibernate.tuple.DynamicMapInstantitor {
- // override the generateMap() method to return our custom map...
- protected final Map generateMap() {
- return new CustomMap();
- }
- }
-}]]></programlisting>
-
-
- </section>
-
-
- <section id="persistent-classes-entity-name-resolver" revision="0">
- <title>EntityNameResolvers</title>
-
- <para>
- The <interfacename>org.hibernate.EntityNameResolver</interfacename> interface is a contract for resolving the
- entity name of a given entity instance. The interface defines a single method <methodname>resolveEntityName</methodname>
- which is passed the entity instance and is expected to return the appropriate entity name (null is allowed and
- would indicate that the resolver does not know how to resolve the entity name of the given entity instance).
- Generally speaking, an <interfacename>org.hibernate.EntityNameResolver</interfacename> is going to be most
- useful in the case of dynamic models. One example might be using proxied interfaces as your domain model. The
- hibernate test suite has an example of this exact style of usage under the
- <package>org.hibernate.test.dynamicentity.tuplizer2</package>. Here is some of the code from that package
- for illustration.
- </para>
-
- <programlisting role="JAVA">
+ <programlisting role="JAVA">
/**
* A very trivial JDK Proxy InvocationHandler implementation where we proxy an interface as
* the domain model and simply store persistent state in an internal Map. This is an extremely
@@ -686,25 +654,23 @@
}
</programlisting>
- <para>
- In order to register an <interfacename>org.hibernate.EntityNameResolver</interfacename> users must either:
- <orderedlist>
- <listitem>
- <para>
- Implement a custom <link linkend="persistent-classes-tuplizers">Tuplizer</link>, implementing
- the <methodname>getEntityNameResolvers</methodname> method.
- </para>
- </listitem>
- <listitem>
- <para>
- Register it with the <classname>org.hibernate.impl.SessionFactoryImpl</classname> (which is the
- implementation class for <interfacename>org.hibernate.SessionFactory</interfacename>) using the
- <methodname>registerEntityNameResolver</methodname> method.
- </para>
- </listitem>
- </orderedlist>
- </para>
- </section>
+ <para>In order to register an
+ <interfacename>org.hibernate.EntityNameResolver</interfacename> users must
+ either: <orderedlist>
+ <listitem>
+ <para>Implement a custom <link
+ linkend="persistent-classes-tuplizers">Tuplizer</link>, implementing
+ the <methodname>getEntityNameResolvers</methodname> method.</para>
+ </listitem>
+ <listitem>
+ <para>Register it with the
+ <classname>org.hibernate.impl.SessionFactoryImpl</classname> (which
+ is the implementation class for
+ <interfacename>org.hibernate.SessionFactory</interfacename>) using
+ the <methodname>registerEntityNameResolver</methodname>
+ method.</para>
+ </listitem>
+ </orderedlist></para>
+ </section>
</chapter>
-
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml 2010-07-16 15:24:32 UTC (rev 19960)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/query_sql.xml 2010-07-16 15:27:49 UTC (rev 19961)
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
@@ -22,13 +22,11 @@
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
-
-<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;
-
]>
-
<chapter id="querysql" revision="2">
<title>Native SQL</title>
@@ -46,8 +44,8 @@
<para>Execution of native SQL queries is controlled via the
<literal>SQLQuery</literal> interface, which is obtained by calling
- <literal>Session.createSQLQuery()</literal>. The following sections describe how
- to use this API for querying.</para>
+ <literal>Session.createSQLQuery()</literal>. The following sections
+ describe how to use this API for querying.</para>
<section>
<title>Scalar queries</title>
@@ -55,12 +53,12 @@
<para>The most basic SQL query is to get a list of scalars
(values).</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").list();
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
-]]></programlisting>
+</programlisting>
- <para>These will return a List of Object arrays (Object[]) with
- scalar values for each column in the CATS table. Hibernate will use
+ <para>These will return a List of Object arrays (Object[]) with scalar
+ values for each column in the CATS table. Hibernate will use
ResultSetMetadata to deduce the actual order and types of the returned
scalar values.</para>
@@ -68,11 +66,11 @@
<literal>ResultSetMetadata</literal>, or simply to be more explicit in
what is returned, one can use <literal>addScalar()</literal>:</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
-]]></programlisting>
+</programlisting>
<para>This query specified:</para>
@@ -97,15 +95,16 @@
<para>It is possible to leave out the type information for all or some
of the scalars.</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT * FROM CATS")
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME")
.addScalar("BIRTHDATE")
-]]></programlisting>
+</programlisting>
<para>This is essentially the same query as before, but now
- <literal>ResultSetMetaData</literal> is used to determine the type of NAME
- and BIRTHDATE, where as the type of ID is explicitly specified.</para>
+ <literal>ResultSetMetaData</literal> is used to determine the type of
+ NAME and BIRTHDATE, where as the type of ID is explicitly
+ specified.</para>
<para>How the java.sql.Types returned from ResultSetMetaData is mapped
to Hibernate types is controlled by the Dialect. If a specific type is
@@ -122,9 +121,9 @@
shows how to get entity objects from a native sql query via
<literal>addEntity()</literal>.</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
-]]></programlisting>
+</programlisting>
<para>This query specified:</para>
@@ -150,8 +149,8 @@
example for a <literal>many-to-one</literal> to a
<literal>Dog</literal>:</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
-]]></programlisting>
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
+</programlisting>
<para>This will allow cat.getDog() to function properly.</para>
</section>
@@ -164,10 +163,10 @@
done via the <literal>addJoin()</literal> method, which allows you to
join in an association or collection.</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dog");
-]]></programlisting>
+</programlisting>
<para>In this example, the returned <literal>Cat</literal>'s will have
their <literal>dog</literal> property fully initialized without any
@@ -177,16 +176,16 @@
<literal>Cat</literal> had a one-to-many to <literal>Dog</literal>
instead.</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");
-]]></programlisting>
+</programlisting>
- <para>
- At this stage you are reaching the limits of what is possible with native queries, without starting to
- enhance the sql queries to make them usable in Hibernate. Problems can arise when returning
- multiple entities of the same type or when the default alias/column names are not enough.
- </para>
+ <para>At this stage you are reaching the limits of what is possible with
+ native queries, without starting to enhance the sql queries to make them
+ usable in Hibernate. Problems can arise when returning multiple entities
+ of the same type or when the default alias/column names are not
+ enough.</para>
</section>
<section>
@@ -200,29 +199,25 @@
<para>Column alias injection is needed in the following query (which
most likely will fail):</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
-]]></programlisting>
+</programlisting>
-
+ <para>The query was intended to return two Cat instances per row: a cat
+ and its mother. The query will, however, fail because there is a
+ conflict of names; the instances are mapped to the same column names.
+ Also, on some databases the returned column aliases will most likely be
+ on the form "c.ID", "c.NAME", etc. which are not equal to the columns
+ specified in the mappings ("ID" and "NAME").</para>
- <para>
- The query was intended to return two Cat instances per
- row: a cat and its mother. The query will, however, fail because there is a conflict of
- names; the instances are mapped to the same column names. Also, on some
- databases the returned column aliases will most likely be on the form
- "c.ID", "c.NAME", etc. which are not equal to the columns specified in
- the mappings ("ID" and "NAME").
- </para>
-
<para>The following form is not vulnerable to column name
duplication:</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
-]]></programlisting>
+</programlisting>
<para>This query specified:</para>
@@ -238,35 +233,35 @@
</itemizedlist>
<para>The {cat.*} and {mother.*} notation used above is a shorthand for
- "all properties". Alternatively, you can list the columns explicitly, but
- even in this case Hibernate injects the SQL column aliases for
- each property. The placeholder for a column alias is just the property
- name qualified by the table alias. In the following example, you retrieve
+ "all properties". Alternatively, you can list the columns explicitly,
+ but even in this case Hibernate injects the SQL column aliases for each
+ property. The placeholder for a column alias is just the property name
+ qualified by the table alias. In the following example, you retrieve
Cats and their mothers from a different table (cat_log) to the one
- declared in the mapping metadata. You can even use the
- property aliases in the where clause.</para>
+ declared in the mapping metadata. You can even use the property aliases
+ in the where clause.</para>
- <programlisting role="JAVA"><![CDATA[String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
+ <programlisting role="JAVA">String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
-]]></programlisting>
+</programlisting>
<section id="querysql-aliasreferences" revision="2">
<title>Alias and property references</title>
- <para>In most cases the above alias injection is needed. For
- queries relating to more complex mappings, like composite properties,
+ <para>In most cases the above alias injection is needed. For queries
+ relating to more complex mappings, like composite properties,
inheritance discriminators, collections etc., you can use specific
aliases that allow Hibernate to inject the proper aliases.</para>
- <para>The following table shows the different ways you can use
- the alias injection. Please note that the alias names in the result are simply examples;
- each alias will have a unique and probably different name when
- used.</para>
+ <para>The following table shows the different ways you can use the
+ alias injection. Please note that the alias names in the result are
+ simply examples; each alias will have a unique and probably different
+ name when used.</para>
<table frame="topbot" id="aliasinjection-summary">
<title>Alias injection names</title>
@@ -378,13 +373,14 @@
<section>
<title>Returning non-managed entities</title>
- <para>It is possible to apply a ResultTransformer to native SQL queries, allowing it to return non-managed entities.</para>
+ <para>It is possible to apply a ResultTransformer to native SQL queries,
+ allowing it to return non-managed entities.</para>
- <programlisting role="JAVA"><![CDATA[sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
- .setResultTransformer(Transformers.aliasToBean(CatDTO.class))]]></programlisting>
-
- <para>This query specified:</para>
+ <programlisting role="JAVA">sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
+ .setResultTransformer(Transformers.aliasToBean(CatDTO.class))</programlisting>
+ <para>This query specified:</para>
+
<itemizedlist>
<listitem>
<para>the SQL query string</para>
@@ -394,19 +390,18 @@
<para>a result transformer</para>
</listitem>
</itemizedlist>
-
- <para>
- The above query will return a list of <literal>CatDTO</literal> which has been instantiated and injected the values of NAME and BIRTHNAME into its corresponding
- properties or fields.
- </para>
+
+ <para>The above query will return a list of <literal>CatDTO</literal>
+ which has been instantiated and injected the values of NAME and
+ BIRTHNAME into its corresponding properties or fields.</para>
</section>
<section>
<title>Handling inheritance</title>
- <para>Native SQL queries which query for entities that are mapped as part
- of an inheritance must include all properties for the baseclass and all
- its subclasses.</para>
+ <para>Native SQL queries which query for entities that are mapped as
+ part of an inheritance must include all properties for the baseclass and
+ all its subclasses.</para>
</section>
<section>
@@ -415,46 +410,56 @@
<para>Native SQL queries support positional as well as named
parameters:</para>
- <programlisting role="JAVA"><![CDATA[Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
+ <programlisting role="JAVA">Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
-List pusList = query.setString("name", "Pus%").list(); ]]></programlisting>
+List pusList = query.setString("name", "Pus%").list(); </programlisting>
</section>
-
-
-
</section>
<section id="querysql-namedqueries" revision="3">
<title>Named SQL queries</title>
- <para>Named SQL queries can be defined in the mapping document and called
- in exactly the same way as a named HQL query. In this case, you do
+ <para>Named SQL queries can also be defined in the mapping document and
+ called in exactly the same way as a named HQL query (see <xref
+ linkend="objectstate-querying-executing-named" />). In this case, you do
<emphasis>not</emphasis> need to call
<literal>addEntity()</literal>.</para>
- <programlisting role="XML"><![CDATA[<sql-query name="persons">
- <return alias="person" class="eg.Person"/>
+ <example>
+ <title>Named sql query using the <sql-query> maping
+ element</title>
+
+ <programlisting role="XML"><sql-query name="persons">
+ <return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
+ </example>
- <programlisting role="JAVA"><![CDATA[List people = sess.getNamedQuery("persons")
+ <example>
+ <title>Execution of a named query</title>
+
+ <programlisting role="JAVA">List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
- .list();]]></programlisting>
+ .list();</programlisting>
+ </example>
- <para>The <literal><return-join></literal> element is use to join associations and
- the <literal><load-collection></literal> element is used to define queries which initialize collections,
- </para>
+ <para>The <literal><return-join></literal> element is use to join
+ associations and the <literal><load-collection></literal> element is
+ used to define queries which initialize collections,</para>
- <programlisting role="XML"><![CDATA[<sql-query name="personsWith">
- <return alias="person" class="eg.Person"/>
- <return-join alias="address" property="person.mailingAddress"/>
+ <example>
+ <title>Named sql query with association</title>
+
+ <programlisting role="XML"><sql-query name="personsWith">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
@@ -466,31 +471,40 @@
JOIN ADDRESS address
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
+ </example>
<para>A named SQL query may return a scalar value. You must declare the
column alias and Hibernate type using the
<literal><return-scalar></literal> element:</para>
- <programlisting role="XML"><![CDATA[<sql-query name="mySqlQuery">
- <return-scalar column="name" type="string"/>
- <return-scalar column="age" type="long"/>
- SELECT p.NAME AS name,
+ <example>
+ <title>Named query returning a scalar</title>
+
+ <programlisting role="XML"><sql-query name="mySqlQuery">
+ <return-scalar column="name" type="string"/>
+ <return-scalar column="age" type="long"/>
+ SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
+ </example>
<para>You can externalize the resultset mapping information in a
- <literal><resultset></literal> element which will allow you to either reuse them across
- several named queries or through the
+ <literal><resultset></literal> element which will allow you to
+ either reuse them across several named queries or through the
<literal>setResultSetMapping()</literal> API.</para>
- <programlisting role="XML"><![CDATA[<resultset name="personAddress">
- <return alias="person" class="eg.Person"/>
- <return-join alias="address" property="person.mailingAddress"/>
-</resultset>
+ <example>
+ <title><resultset> mapping used to externalize mapping
+ information</title>
-<sql-query name="personsWith" resultset-ref="personAddress">
+ <programlisting role="XML"><resultset name="personAddress">
+ <return alias="person" class="eg.Person"/>
+ <return-join alias="address" property="person.mailingAddress"/>
+</resultset>
+
+<sql-query name="personsWith" resultset-ref="personAddress">
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
@@ -502,64 +516,308 @@
JOIN ADDRESS address
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
+ </example>
- <para>You can, alternatively, use the resultset mapping information in your
- hbm files directly in java code.</para>
+ <para>You can, alternatively, use the resultset mapping information in
+ your hbm files directly in java code.</para>
- <programlisting role="JAVA"><![CDATA[List cats = sess.createSQLQuery(
+ <example>
+ <title>Programmatically specifying the result mapping information
+ </title>
+
+ <programlisting role="JAVA">List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
- .list();]]></programlisting>
+ .list();</programlisting>
+ </example>
+ <para>So far we have only looked at externalizing SQL queries using
+ Hibernate mapping files. The same concept is also available with
+ anntations and is called named native queries. You can use
+ <classname>@NamedNativeQuery</classname>
+ (<classname>@NamedNativeQueries</classname>) in conjunction with
+ <literal>@SqlResultSetMapping</literal>
+ (<literal>@SqlResultSetMappings</literal>). Like
+ <literal>@NamedQuery</literal>, <classname>@NamedNativeQuery</classname>
+ and <literal>@SqlResultSetMapping</literal> can be defined at class level,
+ but their scope is global to the application. Lets look at a view
+ examples.</para>
+
+ <para><xref
+ linkend="example-named-native-query-annotation-with-result-set-mapping" />
+ shows how a <literal>resultSetMapping</literal> parameter is defined in
+ <literal>@NamedNativeQuery</literal>. It represents the name of a defined
+ <literal>@SqlResultSetMapping</literal>. The resultset mapping declares
+ the entities retrieved by this native query. Each field of the entity is
+ bound to an SQL alias (or column name). All fields of the entity including
+ the ones of subclasses and the foreign key columns of related entities
+ have to be present in the SQL query. Field definitions are optional
+ provided that they map to the same column name as the one declared on the
+ class property. In the example 2 entities, <literal>Night</literal> and
+ <literal>Area</literal>, are returned and each property is declared and
+ associated to a column name, actually the column name retrieved by the
+ query. </para>
+
+ <para>In <xref linkend="example-implicit-result-set-mapping" /> the result
+ set mapping is implicit. We only describe the entity class of the result
+ set mapping. The property / column mappings is done using the entity
+ mapping values. In this case the model property is bound to the model_txt
+ column. </para>
+
+ <para>Finally, if the association to a related entity involve a composite
+ primary key, a <literal>@FieldResult</literal> element should be used for
+ each foreign key column. The <literal>@FieldResult</literal> name is
+ composed of the property name for the relationship, followed by a dot
+ ("."), followed by the name or the field or property of the primary key.
+ This can be seen in <xref
+ linkend="example-field-result-annotation-with-associations" />.</para>
+
+ <example id="example-named-native-query-annotation-with-result-set-mapping">
+ <title>Named SQL query using <classname>@NamedNativeQuery</classname>
+ together with <classname>@SqlResultSetMapping</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@NamedNativeQuery(name="night&area", query="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",
+ resultSetMapping="joinMapping")
+@SqlResultSetMapping(name="joinMapping", entities={
+ @EntityResult(entityClass=Night.class, fields = {
+ @FieldResult(name="id", column="nid"),
+ @FieldResult(name="duration", column="night_duration"),
+ @FieldResult(name="date", column="night_date"),
+ @FieldResult(name="area", column="area_id"),
+ discriminatorColumn="disc"
+ }),
+ @EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {
+ @FieldResult(name="id", column="aid"),
+ @FieldResult(name="name", column="name")
+ })
+ }
+)</programlisting>
+ </example>
+
+ <example id="example-implicit-result-set-mapping">
+ <title>Implicit result set mapping</title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+@SqlResultSetMapping(name="implicit",
+ entities=(a)EntityResult(entityClass=SpaceShip.class))
+@NamedNativeQuery(name="implicitSample",
+ query="select * from SpaceShip",
+ resultSetMapping="implicit")
+public class SpaceShip {
+ private String name;
+ private String model;
+ private double speed;
+
+ @Id
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Column(name="model_txt")
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(double speed) {
+ this.speed = speed;
+ }
+}</programlisting>
+ </example>
+
+ <example id="example-field-result-annotation-with-associations">
+ <title>Using dot notation in @FieldResult for specifying associations
+ </title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+@SqlResultSetMapping(name="compositekey",
+ entities=(a)EntityResult(entityClass=SpaceShip.class,
+ fields = {
+ @FieldResult(name="name", column = "name"),
+ @FieldResult(name="model", column = "model"),
+ @FieldResult(name="speed", column = "speed"),
+ @FieldResult(name="captain.firstname", column = "firstn"),
+ @FieldResult(name="captain.lastname", column = "lastn"),
+ @FieldResult(name="dimensions.length", column = "length"),
+ @FieldResult(name="dimensions.width", column = "width")
+ }),
+ columns = { @ColumnResult(name = "surface"),
+ @ColumnResult(name = "volume") } )
+
+@NamedNativeQuery(name="compositekey",
+ query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip",
+ resultSetMapping="compositekey")
+} )
+public class SpaceShip {
+ private String name;
+ private String model;
+ private double speed;
+ private Captain captain;
+ private Dimensions dimensions;
+
+ @Id
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumns( {
+ @JoinColumn(name="fname", referencedColumnName = "firstname"),
+ @JoinColumn(name="lname", referencedColumnName = "lastname")
+ } )
+ public Captain getCaptain() {
+ return captain;
+ }
+
+ public void setCaptain(Captain captain) {
+ this.captain = captain;
+ }
+
+ public String getModel() {
+ return model;
+ }
+
+ public void setModel(String model) {
+ this.model = model;
+ }
+
+ public double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(double speed) {
+ this.speed = speed;
+ }
+
+ public Dimensions getDimensions() {
+ return dimensions;
+ }
+
+ public void setDimensions(Dimensions dimensions) {
+ this.dimensions = dimensions;
+ }
+}
+
+@Entity
+(a)IdClass(Identity.class)
+public class Captain implements Serializable {
+ private String firstname;
+ private String lastname;
+
+ @Id
+ public String getFirstname() {
+ return firstname;
+ }
+
+ public void setFirstname(String firstname) {
+ this.firstname = firstname;
+ }
+
+ @Id
+ public String getLastname() {
+ return lastname;
+ }
+
+ public void setLastname(String lastname) {
+ this.lastname = lastname;
+ }
+}
+</programlisting>
+ </example>
+
+ <tip>
+ <para>If you retrieve a single entity using the default mapping, you can
+ specify the <literal>resultClass</literal> attribute instead of
+ <literal>resultSetMapping</literal>:</para>
+
+ <programlisting language="JAVA" role="JAVA">@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultClass=SpaceShip.class)
+public class SpaceShip {</programlisting>
+ </tip>
+
+ <para>In some of your native queries, you'll have to return scalar values,
+ for example when building report queries. You can map them in the
+ <literal>@SqlResultsetMapping</literal> through
+ <literal>@ColumnResult</literal>. You actually can even mix, entities and
+ scalar returns in the same native query (this is probably not that common
+ though).</para>
+
+ <example>
+ <title>Scalar values via <classname>@ColumnResult</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))
+@NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")</programlisting>
+ </example>
+
+ <para>An other query hint specific to native queries has been introduced:
+ <literal>org.hibernate.callable</literal> which can be true or false
+ depending on whether the query is a stored procedure or not.</para>
+
<section id="propertyresults">
<title>Using return-property to explicitly specify column/alias
names</title>
- <para>You can explicitly
- tell Hibernate what column aliases to use with <literal><return-property></literal>, instead of using the
- <literal>{}</literal>-syntax to let Hibernate inject its own
- aliases.For example:</para>
+ <para>You can explicitly tell Hibernate what column aliases to use with
+ <literal><return-property></literal>, instead of using the
+ <literal>{}</literal>-syntax to let Hibernate inject its own aliases.For
+ example:</para>
- <programlisting role="XML"><![CDATA[<sql-query name="mySqlQuery">
- <return alias="person" class="eg.Person">
- <return-property name="name" column="myName"/>
- <return-property name="age" column="myAge"/>
- <return-property name="sex" column="mySex"/>
- </return>
+ <programlisting role="XML"><sql-query name="mySqlQuery">
+ <return alias="person" class="eg.Person">
+ <return-property name="name" column="myName"/>
+ <return-property name="age" column="myAge"/>
+ <return-property name="sex" column="mySex"/>
+ </return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
-</sql-query>
-]]></programlisting>
+</sql-query>
+</programlisting>
<para><literal><return-property></literal> also works with
multiple columns. This solves a limitation with the
<literal>{}</literal>-syntax which cannot allow fine grained control of
multi-column properties.</para>
- <programlisting role="XML"><![CDATA[<sql-query name="organizationCurrentEmployments">
- <return alias="emp" class="Employment">
- <return-property name="salary">
- <return-column name="VALUE"/>
- <return-column name="CURRENCY"/>
- </return-property>
- <return-property name="endDate" column="myEndDate"/>
- </return>
+ <programlisting role="XML"><sql-query name="organizationCurrentEmployments">
+ <return alias="emp" class="Employment">
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ <return-property name="endDate" column="myEndDate"/>
+ </return>
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
- <para>In this example
- <literal><return-property></literal> was used in combination with the
- <literal>{}</literal>-syntax for injection. This allows users to choose how
- they want to refer column and properties.</para>
+ <para>In this example <literal><return-property></literal> was
+ used in combination with the <literal>{}</literal>-syntax for injection.
+ This allows users to choose how they want to refer column and
+ properties.</para>
<para>If your mapping has a discriminator you must use
<literal><return-discriminator></literal> to specify the
@@ -569,13 +827,13 @@
<section id="sp_query" revision="1">
<title>Using stored procedures for querying</title>
- <para>Hibernate3 provides support for queries via stored procedures
- and functions. Most of the following documentation is equivalent for
- both. The stored procedure/function must return a resultset as the first
+ <para>Hibernate3 provides support for queries via stored procedures and
+ functions. Most of the following documentation is equivalent for both.
+ The stored procedure/function must return a resultset as the first
out-parameter to be able to work with Hibernate. An example of such a
stored function in Oracle 9 and higher is as follows:</para>
- <programlisting role="XML"><![CDATA[CREATE OR REPLACE FUNCTION selectAllEmployments
+ <programlisting role="XML">CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
@@ -586,40 +844,41 @@
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
- END;]]></programlisting>
+ END;</programlisting>
<para>To use this query in Hibernate you need to map it via a named
query.</para>
- <programlisting role="XML"><![CDATA[<sql-query name="selectAllEmployees_SP" callable="true">
- <return alias="emp" class="Employment">
- <return-property name="employee" column="EMPLOYEE"/>
- <return-property name="employer" column="EMPLOYER"/>
- <return-property name="startDate" column="STARTDATE"/>
- <return-property name="endDate" column="ENDDATE"/>
- <return-property name="regionCode" column="REGIONCODE"/>
- <return-property name="id" column="EID"/>
- <return-property name="salary">
- <return-column name="VALUE"/>
- <return-column name="CURRENCY"/>
- </return-property>
- </return>
+ <programlisting role="XML"><sql-query name="selectAllEmployees_SP" callable="true">
+ <return alias="emp" class="Employment">
+ <return-property name="employee" column="EMPLOYEE"/>
+ <return-property name="employer" column="EMPLOYER"/>
+ <return-property name="startDate" column="STARTDATE"/>
+ <return-property name="endDate" column="ENDDATE"/>
+ <return-property name="regionCode" column="REGIONCODE"/>
+ <return-property name="id" column="EID"/>
+ <return-property name="salary">
+ <return-column name="VALUE"/>
+ <return-column name="CURRENCY"/>
+ </return-property>
+ </return>
{ ? = call selectAllEmployments() }
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
- <para>Stored procedures currently only return scalars and
- entities. <literal><return-join></literal> and
+ <para>Stored procedures currently only return scalars and entities.
+ <literal><return-join></literal> and
<literal><load-collection></literal> are not supported.</para>
<section id="querysql-limits-storedprocedures" revision="1">
<title>Rules/limitations for using stored procedures</title>
- <para>You cannot use stored procedures with Hibernate unless you follow some procedure/function
- rules. If they do not follow those rules they are
- not usable with Hibernate. If you still want to use these procedures
- you have to execute them via <literal>session.connection()</literal>.
- The rules are different for each database, since database vendors have
- different stored procedure semantics/syntax.</para>
+ <para>You cannot use stored procedures with Hibernate unless you
+ follow some procedure/function rules. If they do not follow those
+ rules they are not usable with Hibernate. If you still want to use
+ these procedures you have to execute them via
+ <literal>session.connection()</literal>. The rules are different for
+ each database, since database vendors have different stored procedure
+ semantics/syntax.</para>
<para>Stored procedure queries cannot be paged with
<literal>setFirstResult()/setMaxResults()</literal>.</para>
@@ -647,10 +906,10 @@
<itemizedlist spacing="compact">
<listitem>
<para>The procedure must return a result set. Note that since
- these servers can return multiple result sets and update
- counts, Hibernate will iterate the results and take the first
- result that is a result set as its return value. Everything else
- will be discarded.</para>
+ these servers can return multiple result sets and update counts,
+ Hibernate will iterate the results and take the first result that
+ is a result set as its return value. Everything else will be
+ discarded.</para>
</listitem>
<listitem>
@@ -666,63 +925,61 @@
<section id="querysql-cud">
<title>Custom SQL for create, update and delete</title>
- <para>Hibernate3 can use custom SQL for create, update, and delete operations.
- The SQL can be overridden at the statement level or inidividual column level. This
- section describes statement overrides. For columns, see
- <xref linkend="mapping-column-read-and-write"/>.
- </para>
- <para>
- The class and collection persisters in Hibernate already contain a set of
- configuration time generated strings (insertsql, deletesql, updatesql etc.).
- The mapping tags
- <literal><sql-insert></literal>,
+ <para>Hibernate3 can use custom SQL for create, update, and delete
+ operations. The SQL can be overridden at the statement level or
+ inidividual column level. This section describes statement overrides. For
+ columns, see <xref linkend="mapping-column-read-and-write" />.</para>
+
+ <para>The class and collection persisters in Hibernate already contain a
+ set of configuration time generated strings (insertsql, deletesql,
+ updatesql etc.). The mapping tags <literal><sql-insert></literal>,
<literal><sql-delete></literal>, and
<literal><sql-update></literal> override these strings:</para>
- <programlisting role="XML"><![CDATA[<class name="Person">
- <id name="id">
- <generator class="increment"/>
- </id>
- <property name="name" not-null="true"/>
- <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
- <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
- <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
-</class>]]></programlisting>
+ <programlisting role="XML"><class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
+ <sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
+ <sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
+</class></programlisting>
- <para>The SQL is directly executed in your database, so you can
- use any dialect you like. This will reduce the portability of
- your mapping if you use database specific SQL.</para>
+ <para>The SQL is directly executed in your database, so you can use any
+ dialect you like. This will reduce the portability of your mapping if you
+ use database specific SQL.</para>
<para>Stored procedures are supported if the <literal>callable</literal>
attribute is set:</para>
- <programlisting role="XML"><![CDATA[<class name="Person">
- <id name="id">
- <generator class="increment"/>
- </id>
- <property name="name" not-null="true"/>
- <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
- <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
- <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
-</class>]]></programlisting>
+ <programlisting role="XML"><class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
+ <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
+ <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
+</class></programlisting>
- <para>The order of the positional parameters is vital, as they
- must be in the same sequence as Hibernate expects them.</para>
+ <para>The order of the positional parameters is vital, as they must be in
+ the same sequence as Hibernate expects them.</para>
<para>You can view the expected order by enabling debug logging for the
<literal>org.hibernate.persister.entity</literal> level. With this level
enabled, Hibernate will print out the static SQL that is used to create,
- update, delete etc. entities. To view the expected sequence, do
- not include your custom SQL in the mapping files, as this will override the
+ update, delete etc. entities. To view the expected sequence, do not
+ include your custom SQL in the mapping files, as this will override the
Hibernate generated static SQL.</para>
- <para>The stored procedures are in most cases
- required to return the number of rows inserted, updated and deleted, as
- Hibernate has some runtime checks for the success of the statement.
- Hibernate always registers the first statement parameter as a numeric
- output parameter for the CUD operations:</para>
+ <para>The stored procedures are in most cases required to return the
+ number of rows inserted, updated and deleted, as Hibernate has some
+ runtime checks for the success of the statement. Hibernate always
+ registers the first statement parameter as a numeric output parameter for
+ the CUD operations:</para>
- <programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
+ <programlisting>CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
@@ -734,7 +991,7 @@
return SQL%ROWCOUNT;
-END updatePerson;]]></programlisting>
+END updatePerson;</programlisting>
</section>
<section id="querysql-load">
@@ -742,59 +999,58 @@
<para>You can also declare your own SQL (or HQL) queries for entity
loading. As with inserts, updates, and deletes, this can be done at the
- individual column level as described in
- <xref linkend="mapping-column-read-and-write"/>
- or at the statement level. Here is an example of a statement level override:
- </para>
+ individual column level as described in <xref
+ linkend="mapping-column-read-and-write" /> or at the statement level. Here
+ is an example of a statement level override:</para>
- <programlisting role="XML"><![CDATA[<sql-query name="person">
- <return alias="pers" class="Person" lock-mode="upgrade"/>
+ <programlisting role="XML"><sql-query name="person">
+ <return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
<para>This is just a named query declaration, as discussed earlier. You
can reference this named query in a class mapping:</para>
- <programlisting role="XML"><![CDATA[<class name="Person">
- <id name="id">
- <generator class="increment"/>
- </id>
- <property name="name" not-null="true"/>
- <loader query-ref="person"/>
-</class>]]></programlisting>
+ <programlisting role="XML"><class name="Person">
+ <id name="id">
+ <generator class="increment"/>
+ </id>
+ <property name="name" not-null="true"/>
+ <loader query-ref="person"/>
+</class></programlisting>
<para>This even works with stored procedures.</para>
<para>You can even define a query for collection loading:</para>
- <programlisting role="XML"><![CDATA[<set name="employments" inverse="true">
- <key/>
- <one-to-many class="Employment"/>
- <loader query-ref="employments"/>
-</set>]]></programlisting>
+ <programlisting role="XML"><set name="employments" inverse="true">
+ <key/>
+ <one-to-many class="Employment"/>
+ <loader query-ref="employments"/>
+</set></programlisting>
- <programlisting role="XML"><![CDATA[<sql-query name="employments">
- <load-collection alias="emp" role="Person.employments"/>
+ <programlisting role="XML"><sql-query name="employments">
+ <load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
- <para>You can also define an entity loader that loads a collection by
- join fetching:</para>
+ <para>You can also define an entity loader that loads a collection by join
+ fetching:</para>
- <programlisting role="XML"><![CDATA[<sql-query name="person">
- <return alias="pers" class="Person"/>
- <return-join alias="emp" property="pers.employments"/>
+ <programlisting role="XML"><sql-query name="person">
+ <return alias="pers" class="Person"/>
+ <return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
-</sql-query>]]></programlisting>
+</sql-query></programlisting>
</section>
</chapter>
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/session_api.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/session_api.xml 2010-07-16 15:24:32 UTC (rev 19960)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/session_api.xml 2010-07-16 15:27:49 UTC (rev 19961)
@@ -454,22 +454,63 @@
<section id="objectstate-querying-executing-named" revision="1">
<title>Externalizing named queries</title>
- <para>You can also define named queries in the mapping document.
- Remember to use a <literal>CDATA</literal> section if your query
- contains characters that could be interpreted as markup.</para>
+ <para>Queries can also be configured as so called named queries using
+ annotations or Hibernate mapping documents.
+ <literal>@NamedQuery</literal> and <literal>@NamedQueries</literal>
+ can be defined at the class level as seen in <xref
+ linkend="example-named-query-annotation" /> . However their
+ definitions are global to the session factory/entity manager factory
+ scope. A named query is defined by its name and the actual query
+ string.</para>
- <programlisting role="XML"><query name="ByNameAndMaximumWeight"><![CDATA[
+ <example id="example-named-query-annotation">
+ <title>Defining a named query using
+ <classname>@NamedQuery</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")
+public class Night {
+ ...
+}
+
+public class MyDao {
+ doStuff() {
+ Query q = s.getNamedQuery("night.moreRecentThan");
+ q.setDate( "date", aMonthAgo );
+ List results = q.list();
+ ...
+ }
+ ...
+} </programlisting>
+ </example>
+
+ <para>Using a mapping document can be configured using the
+ <literal><query></literal> node. Remember to use a
+ <literal>CDATA</literal> section if your query contains characters
+ that could be interpreted as markup.</para>
+
+ <example>
+ <title>Defining a named query using
+ <literal><query></literal></title>
+
+ <programlisting role="XML"><query name="ByNameAndMaximumWeight"><![CDATA[
from eg.DomesticCat as cat
where cat.name = ?
and cat.weight > ?
] ]></query></programlisting>
+ </example>
- <para>Parameter binding and executing is done programatically:</para>
+ <para>Parameter binding and executing is done programatically as seen
+ in <xref linkend="example-parameter-binding-named-query" />.</para>
- <programlisting role="JAVA">Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
+ <example id="example-parameter-binding-named-query">
+ <title>Parameter binding of a named query</title>
+
+ <programlisting role="JAVA">Query q = sess.getNamedQuery("ByNameAndMaximumWeight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();</programlisting>
+ </example>
<para>The actual program code is independent of the query language
that is used. You can also define native SQL queries in metadata, or
@@ -1042,7 +1083,7 @@
<note>
<para>CascadeType.ALL also covers Hibernate specific operations like
- save-update, lock etc... </para>
+ save-update, lock etc...</para>
</note>
<para>A special cascade style, <literal>delete-orphan</literal>, applies
14 years, 4 months
Hibernate SVN: r19960 - in core/trunk/documentation/manual/src/main/docbook/en-US: content and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2010-07-16 11:24:32 -0400 (Fri, 16 Jul 2010)
New Revision: 19960
Added:
core/trunk/documentation/manual/src/main/docbook/en-US/content/additionalmodules.xml
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml
core/trunk/documentation/manual/src/main/docbook/en-US/author_group.xml
Log:
HHH-5155 Added additional modules as chapter 22
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml 2010-07-15 23:47:32 UTC (rev 19959)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml 2010-07-16 15:24:32 UTC (rev 19960)
@@ -95,6 +95,8 @@
<xi:include href="content/toolset_guide.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
+ <xi:include href="content/additionalmodules.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
+
<xi:include href="content/example_parentchild.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="content/example_weblog.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
<xi:include href="content/example_mappings.xml" xmlns:xi="http://www.w3.org/2001/XInclude" />
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/author_group.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/author_group.xml 2010-07-15 23:47:32 UTC (rev 19959)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/author_group.xml 2010-07-16 15:24:32 UTC (rev 19960)
@@ -46,6 +46,10 @@
<firstname>Steve</firstname>
<surname>Ebersole</surname>
</author>
+ <author>
+ <firstname>Hardy</firstname>
+ <surname>Ferentschik</surname>
+ </author>
<othercredit>
<firstname>James</firstname>
Added: core/trunk/documentation/manual/src/main/docbook/en-US/content/additionalmodules.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/additionalmodules.xml (rev 0)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/additionalmodules.xml 2010-07-16 15:24:32 UTC (rev 19960)
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Hibernate, Relational Persistence for Idiomatic Java
+ ~
+ ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ ~ indicated by the @author tags or express copyright attribution
+ ~ statements applied by the authors. All third-party contributions are
+ ~ distributed under license by Red Hat Middleware LLC.
+ ~
+ ~ This copyrighted material is made available to anyone wishing to use, modify,
+ ~ copy, or redistribute it subject to the terms and conditions of the GNU
+ ~ Lesser General Public License, as published by the Free Software Foundation.
+ ~
+ ~ This program is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<chapter id="additionalmodules">
+ <title>Additional modules</title>
+
+ <para>Hibernate Core also offers integration with some external
+ modules/projects. This includes Hibernate Validator the reference
+ implementation of Bean Validation (JSR 303) and Hibernate Search. </para>
+
+ <section>
+ <title>Bean Validation</title>
+
+ <para>Bean Validation standardizes how to define and declare domain model
+ level constraints. You can, for example, express that a property should
+ never be null, that the account balance should be strictly positive, etc.
+ These domain model constraints are declared in the bean itself by
+ annotating its properties. Bean Validation can then read them and check
+ for constraint violations. The validation mechanism can be executed in
+ different layers in your application without having to duplicate any of
+ these rules (presentation layer, data access layer). Following the DRY
+ principle, Bean Validation and its reference implementation Hibernate
+ Validator has been designed for that purpose.</para>
+
+ <para>The integration between Hibernate and Bean Validation works at two
+ levels. First, it is able to check in-memory instances of a class for
+ constraint violations. Second, it can apply the constraints to the
+ Hibernate metamodel and incorporate them into the generated database
+ schema.</para>
+
+ <para>Each constraint annotation is associated to a validator
+ implementation responsible for checking the constraint on the entity
+ instance. A validator can also (optionally) apply the constraint to the
+ Hibernate metamodel, allowing Hibernate to generate DDL that expresses the
+ constraint. With the appropriate event listener, you can execute the
+ checking operation on inserts, updates and deletes done by
+ Hibernate.</para>
+
+ <para>When checking instances at runtime, Hibernate Validator returns
+ information about constraint violations in a set of
+ <classname>ConstraintViolation</classname>s. Among other information, the
+ <classname>ConstraintViolation</classname> contains an error description
+ message that can embed the parameter values bundle with the annotation
+ (eg. size limit), and message strings that may be externalized to a
+ <classname>ResourceBundle</classname>.</para>
+
+ <section>
+ <title>Adding Bean Validation</title>
+
+ <para>To enable Hibernate's Bean Validation integration, simply add a
+ Bean Validation provider (preferably Hibernate Validation 4) on your
+ classpath.</para>
+ </section>
+
+ <section>
+ <title>Configuration</title>
+
+ <para>By default, no configuration is necessary.</para>
+
+ <para>The <classname>Default</classname> group is validated on entity
+ insert and update and the database model is updated accordingly based on
+ the <classname>Default</classname> group as well.</para>
+
+ <para>You can customize the Bean Validation integration by setting the
+ validation mode. Use the
+ <literal>javax.persistence.validation.mode</literal> property and set it
+ up for example in your <filename>persistence.xml</filename> file or your
+ <filename>hibernate.cfg.xml</filename> file. Several options are
+ possible:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>auto</literal> (default): enable integration between
+ Bean Validation and Hibernate (callback and ddl generation) only if
+ Bean Validation is present in the classpath.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>none</literal>: disable all integration between Bean
+ Validation and Hibernate</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>callback</literal>: only validate entities when they
+ are either inserted, updated or deleted. An exception is raised if
+ no Bean Validation provider is present in the classpath.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>ddl</literal>: only apply constraints to the database
+ schema when generated by Hibernate. An exception is raised if no
+ Bean Validation provider is present in the classpath. This value is
+ not defined by the Java Persistence spec and is specific to
+ Hibernate.</para>
+ </listitem>
+ </itemizedlist>
+
+ <note>
+ <para>You can use both <literal>callback</literal> and
+ <literal>ddl</literal> together by setting the property to
+ <literal>callback, dll</literal></para>
+
+ <programlisting language="XML" role="XML"><persistence ...>
+ <persistence-unit ...>
+ ...
+ <properties>
+ <property name="javax.persistence.validation.mode"
+ value="callback, ddl"/>
+ </properties>
+ </persistence-unit>
+</persistence></programlisting>
+
+ <para>This is equivalent to <literal>auto</literal> except that if no
+ Bean Validation provider is present, an exception is raised.</para>
+ </note>
+
+ <para>If you want to validate different groups during insertion, update
+ and deletion, use:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>javax.persistence.validation.group.pre-persist</literal>:
+ groups validated when an entity is about to be persisted (default to
+ <classname>Default</classname>)</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>javax.persistence.validation.group.pre-update</literal>:
+ groups validated when an entity is about to be updated (default to
+ <classname>Default</classname>)</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>javax.persistence.validation.group.pre-remove</literal>:
+ groups validated when an entity is about to be deleted (default to
+ no group)</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>org.hibernate.validator.group.ddl</literal>: groups
+ considered when applying constraints on the database schema (default
+ to <classname>Default</classname>)</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Each property accepts the fully qualified class names of the
+ groups validated separated by a comma (,)</para>
+
+ <example>
+ <title>Using custom groups for validation</title>
+
+ <programlisting language="XML" role="XML"><persistence ...>
+ <persistence-unit ...>
+ ...
+ <properties>
+ <property name="javax.persistence.validation.group.pre-update"
+ value="javax.validation.group.Default, com.acme.group.Strict"/>
+ <property name="javax.persistence.validation.group.pre-remove"
+ value="com.acme.group.OnDelete"/>
+ <property name="org.hibernate.validator.group.ddl"
+ value="com.acme.group.DDL"/>
+ </properties>
+ </persistence-unit>
+</persistence></programlisting>
+ </example>
+
+ <note>
+ <para>You can set these properties in
+ <filename>hibernate.cfg.xml</filename>,
+ <filename>hibernate.properties</filename> or programmatically.</para>
+ </note>
+ </section>
+
+ <section>
+ <title>Catching violations</title>
+
+ <para>If an entity is found to be invalid, the list of constraint
+ violations is propagated by the
+ <classname>ConstraintViolationException</classname> which exposes the
+ set of <classname>ConstraintViolation</classname>s.</para>
+
+ <para>This exception is wrapped in a
+ <classname>RollbackException</classname> when the violation happens at
+ commit time. Otherwise the
+ <classname>ConstraintViolationException</classname> is returned (for
+ example when calling <methodname>flush()</methodname>. Note that
+ generally, catchable violations are validated at a higher level (for
+ example in Seam / JSF 2 via the JSF - Bean Validation integration or in
+ your business layer by explicitly calling Bean Validation).</para>
+
+ <para>An application code will rarely be looking for a
+ <classname>ConstraintViolationException</classname> raised by Hibernate.
+ This exception should be treated as fatal and the persistence context
+ should be discarded (<classname>EntityManager</classname> or
+ <classname>Session</classname>).</para>
+ </section>
+
+ <section>
+ <title>Database schema</title>
+
+ <para>Hibernate uses Bean Validation constraints to generate an accurate
+ database schema:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><classname>@NotNull</classname> leads to a not null column
+ (unless it conflicts with components or table inheritance)</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>@Size.max</classname> leads to a
+ <literal>varchar(max)</literal> definition for Strings</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>@Min</classname>, <classname>@Max</classname> lead
+ to column checks (like <code>value <= max</code>)</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>@Digits</classname> leads to the definition of
+ precision and scale (ever wondered which is which? It's easy now
+ with <classname>@Digits</classname> :) )</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>These constraints can be declared directly on the entity
+ properties or indirectly by using constraint composition.</para>
+
+ <para>For more information check the Hibernate Validator <ulink
+ url="http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html/">reference
+ documentation</ulink>.</para>
+ </section>
+ </section>
+
+ <section>
+ <title>Hibernate Search</title>
+
+ <section>
+ <title>Description</title>
+
+ <para>Full text search engines like <productname>Apache
+ Lucene</productname> are a very powerful technology to bring free
+ text/efficient queries to applications. If suffers several mismatches
+ when dealing with a object domain model (keeping the index up to date,
+ mismatch between the index structure and the domain model, querying
+ mismatch...) Hibernate Search indexes your domain model thanks to a few
+ annotations, takes care of the database / index synchronization and
+ brings you back regular managed objects from free text queries.
+ Hibernate Search is using <ulink url="http://lucene.apache.org">Apache
+ Lucene</ulink> under the cover.</para>
+ </section>
+
+ <section>
+ <title>Integration with Hibernate Annotations</title>
+
+ <para>Hibernate Search integrates with Hibernate Core transparently
+ provided that the Hibernate Search jar is present on the classpath. If
+ you do not wish to automatically register Hibernate Search event
+ listeners, you can set
+ <literal>hibernate.search.autoregister_listeners</literal> to false.
+ Such a need is very uncommon and not recommended.</para>
+
+ <para>Check the Hibernate Search <ulink
+ url="http://docs.jboss.org/hibernate/stable/search/reference/en-US/html/">reference
+ documentation</ulink> for more information.</para>
+ </section>
+ </section>
+</chapter>
14 years, 4 months
Hibernate SVN: r19959 - core/trunk/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional.
by hibernate-commits@lists.jboss.org
Author: gbadner
Date: 2010-07-15 19:47:32 -0400 (Thu, 15 Jul 2010)
New Revision: 19959
Modified:
core/trunk/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java
Log:
HHH-5267 : Added FailureExpected test for updating one-to-one mapped as delete-orphan while detached
Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java 2010-07-15 23:35:54 UTC (rev 19958)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java 2010-07-15 23:47:32 UTC (rev 19959)
@@ -89,4 +89,44 @@
cleanupData();
}
+
+ public void testOrphanedWhileDetachedFailureExpected() {
+ createData();
+
+ Session session = openSession();
+ session.beginTransaction();
+ List results = session.createQuery( "from EmployeeInfo" ).list();
+ assertEquals( 1, results.size() );
+ results = session.createQuery( "from Employee" ).list();
+ assertEquals( 1, results.size() );
+ Employee emp = ( Employee ) results.get( 0 );
+ assertNotNull( emp.getInfo() );
+
+ //only fails if the object is detached
+ session.getTransaction().commit();
+ session.close();
+ session = openSession();
+ session.beginTransaction();
+
+ emp.setInfo( null );
+
+ //save using the new session (this used to work prior to 3.5.x)
+ session.saveOrUpdate(emp);
+
+ session.getTransaction().commit();
+ session.close();
+
+ session = openSession();
+ session.beginTransaction();
+ emp = ( Employee ) session.get( Employee.class, emp.getId() );
+ assertNull( emp.getInfo() );
+ results = session.createQuery( "from EmployeeInfo" ).list();
+ assertEquals( 0, results.size() );
+ results = session.createQuery( "from Employee" ).list();
+ assertEquals( 1, results.size() );
+ session.getTransaction().commit();
+ session.close();
+
+ cleanupData();
+ }
}
\ No newline at end of file
14 years, 4 months
Hibernate SVN: r19958 - core/branches/Branch_3_5/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional.
by hibernate-commits@lists.jboss.org
Author: gbadner
Date: 2010-07-15 19:35:54 -0400 (Thu, 15 Jul 2010)
New Revision: 19958
Modified:
core/branches/Branch_3_5/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java
Log:
HHH-5267 : Added FailureExpected test for updating one-to-one mapped as delete-orphan while detached
Modified: core/branches/Branch_3_5/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java
===================================================================
--- core/branches/Branch_3_5/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java 2010-07-15 17:19:20 UTC (rev 19957)
+++ core/branches/Branch_3_5/testsuite/src/test/java/org/hibernate/test/orphan/one2one/fk/reversed/unidirectional/DeleteOneToOneOrphansTest.java 2010-07-15 23:35:54 UTC (rev 19958)
@@ -89,4 +89,44 @@
cleanupData();
}
+
+ public void testOrphanedWhileDetachedFailureExpected() {
+ createData();
+
+ Session session = openSession();
+ session.beginTransaction();
+ List results = session.createQuery( "from EmployeeInfo" ).list();
+ assertEquals( 1, results.size() );
+ results = session.createQuery( "from Employee" ).list();
+ assertEquals( 1, results.size() );
+ Employee emp = ( Employee ) results.get( 0 );
+ assertNotNull( emp.getInfo() );
+
+ //only fails if the object is detached
+ session.getTransaction().commit();
+ session.close();
+ session = openSession();
+ session.beginTransaction();
+
+ emp.setInfo( null );
+
+ //save using the new session (this used to work prior to 3.5.x)
+ session.saveOrUpdate(emp);
+
+ session.getTransaction().commit();
+ session.close();
+
+ session = openSession();
+ session.beginTransaction();
+ emp = ( Employee ) session.get( Employee.class, emp.getId() );
+ assertNull( emp.getInfo() );
+ results = session.createQuery( "from EmployeeInfo" ).list();
+ assertEquals( 0, results.size() );
+ results = session.createQuery( "from Employee" ).list();
+ assertEquals( 1, results.size() );
+ session.getTransaction().commit();
+ session.close();
+
+ cleanupData();
+ }
}
\ No newline at end of file
14 years, 4 months
Hibernate SVN: r19957 - in core/branches/Branch_3_5/envers: src/main/java/org/hibernate/envers/synchronization and 4 other directories.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2010-07-15 13:19:20 -0400 (Thu, 15 Jul 2010)
New Revision: 19957
Added:
core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/
core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java
core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java
Removed:
core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java
core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java
Modified:
core/branches/Branch_3_5/envers/pom.xml
core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java
core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java
core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java
core/branches/Branch_3_5/envers/src/test/resources/hibernate.test.cfg.xml
core/branches/Branch_3_5/envers/src/test/resources/testng.xml
Log:
HHH-5315:
- work units executed both in the before tx completion process and in the synchronization
- each test class has its own H2 database
- tests in a JTA environment
Modified: core/branches/Branch_3_5/envers/pom.xml
===================================================================
--- core/branches/Branch_3_5/envers/pom.xml 2010-07-15 15:58:43 UTC (rev 19956)
+++ core/branches/Branch_3_5/envers/pom.xml 2010-07-15 17:19:20 UTC (rev 19957)
@@ -122,6 +122,12 @@
<artifactId>javassist</artifactId>
<scope>test</scope>
</dependency>
+ <!-- For JTA tests -->
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-testing</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<dependencyManagement>
@@ -143,6 +149,11 @@
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
+ <artifactId>hibernate-testing</artifactId>
+ <version>${version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
<artifactId>hibernate-tools</artifactId>
<version>3.2.0.ga</version>
</dependency>
Modified: core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java
===================================================================
--- core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java 2010-07-15 15:58:43 UTC (rev 19956)
+++ core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcess.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -37,11 +37,14 @@
import org.hibernate.FlushMode;
import org.hibernate.Session;
+import javax.transaction.Synchronization;
+
/**
* @author Adam Warski (adam at warski dot org)
*/
-public class AuditProcess implements BeforeTransactionCompletionProcess {
+public class AuditProcess implements BeforeTransactionCompletionProcess, Synchronization {
private final RevisionInfoGenerator revisionInfoGenerator;
+ private final SessionImplementor session;
private final LinkedList<AuditWorkUnit> workUnits;
private final Queue<AuditWorkUnit> undoQueue;
@@ -49,8 +52,9 @@
private Object revisionData;
- public AuditProcess(RevisionInfoGenerator revisionInfoGenerator) {
+ public AuditProcess(RevisionInfoGenerator revisionInfoGenerator, SessionImplementor session) {
this.revisionInfoGenerator = revisionInfoGenerator;
+ this.session = session;
workUnits = new LinkedList<AuditWorkUnit>();
undoQueue = new LinkedList<AuditWorkUnit>();
@@ -153,4 +157,12 @@
session.flush();
}
}
+
+ // Synchronization methods
+
+ public void beforeCompletion() {
+ doBeforeTransactionCompletion(session);
+ }
+
+ public void afterCompletion(int status) { }
}
Modified: core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java
===================================================================
--- core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java 2010-07-15 15:58:43 UTC (rev 19956)
+++ core/branches/Branch_3_5/envers/src/main/java/org/hibernate/envers/synchronization/AuditProcessManager.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -52,10 +52,22 @@
AuditProcess auditProcess = auditProcesses.get(transaction);
if (auditProcess == null) {
// No worries about registering a transaction twice - a transaction is single thread
- auditProcess = new AuditProcess(revisionInfoGenerator);
+ auditProcess = new AuditProcess(revisionInfoGenerator, session);
auditProcesses.put(transaction, auditProcess);
+ /*
+ * HHH-5315: the process must be both a BeforeTransactionCompletionProcess and a TX Synchronization.
+ *
+ * In a resource-local tx env, the process is called after the flush, and populates the audit tables.
+ * Also, any exceptions that occur during that are propagated (if a Synchronization was used, the exceptions
+ * would be eaten).
+ *
+ * In a JTA env, the before transaction completion is called before the flush, so not all changes are yet
+ * written. However, Synchronization-s do propagate exceptions, so they can be safely used.
+ */
session.getActionQueue().registerProcess(auditProcess);
+ session.getTransaction().registerSynchronization(auditProcess);
+
session.getActionQueue().registerProcess(new AfterTransactionCompletionProcess() {
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
auditProcesses.remove(transaction);
Modified: core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java
===================================================================
--- core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java 2010-07-15 15:58:43 UTC (rev 19956)
+++ core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/AbstractEntityTest.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -27,12 +27,14 @@
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
+import org.hibernate.cfg.Environment;
+import org.hibernate.ejb.AvailableSettings;
import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory;
import org.hibernate.envers.event.AuditEventListener;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.AfterClass;
+import org.hibernate.test.tm.ConnectionProviderImpl;
+import org.hibernate.test.tm.TransactionManagerLookupImpl;
+import org.testng.annotations.*;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.event.*;
@@ -89,7 +91,12 @@
if (audited) {
initListeners();
}
+
cfg.configure("hibernate.test.cfg.xml");
+
+ // Separate database for each test class
+ cfg.setProperty("hibernate.connection.url", "jdbc:h2:mem:" + this.getClass().getName());
+
configure(cfg);
emf = cfg.buildEntityManagerFactory();
@@ -113,4 +120,10 @@
public Ejb3Configuration getCfg() {
return cfg;
}
+
+ protected void addJTAConfig(Ejb3Configuration cfg) {
+ cfg.setProperty("connection.provider_class", ConnectionProviderImpl.class.getName());
+ cfg.setProperty(Environment.TRANSACTION_MANAGER_STRATEGY, TransactionManagerLookupImpl.class.getName());
+ cfg.setProperty(AvailableSettings.TRANSACTION_TYPE, "JTA");
+ }
}
Copied: core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta (from rev 19929, core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/jta)
Deleted: core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java 2010-07-10 13:48:15 UTC (rev 19929)
+++ core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -1,74 +0,0 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- */
-package org.hibernate.envers.test.integration.jta;
-
-import org.hibernate.ejb.Ejb3Configuration;
-import org.hibernate.envers.test.AbstractEntityTest;
-import org.hibernate.envers.test.entities.StrTestEntity;
-import org.hibernate.envers.test.integration.reventity.ExceptionListenerRevEntity;
-import org.hibernate.testing.tm.SimpleJtaTransactionManagerImpl;
-import org.testng.annotations.Test;
-
-import javax.persistence.EntityManager;
-
-/**
- * Same as {@link org.hibernate.envers.test.integration.reventity.ExceptionListener}, but in a JTA environment.
- * @author Adam Warski (adam at warski dot org)
- */
-public class JtaExceptionListener extends AbstractEntityTest {
- public void configure(Ejb3Configuration cfg) {
- cfg.addAnnotatedClass(StrTestEntity.class);
- cfg.addAnnotatedClass(ExceptionListenerRevEntity.class);
-
- addJTAConfig(cfg);
- }
-
- @Test(expectedExceptions = RuntimeException.class)
- public void testTransactionRollback() throws Exception {
- SimpleJtaTransactionManagerImpl.getInstance().begin();
-
- // Trying to persist an entity - however the listener should throw an exception, so the entity
- // shouldn't be persisted
- newEntityManager();
- EntityManager em = getEntityManager();
- em.getTransaction().begin();
- StrTestEntity te = new StrTestEntity("x");
- em.persist(te);
-
- SimpleJtaTransactionManagerImpl.getInstance().commit();
- }
-
- @Test(dependsOnMethods = "testTransactionRollback")
- public void testDataNotPersisted() throws Exception {
- SimpleJtaTransactionManagerImpl.getInstance().begin();
-
- // Checking if the entity became persisted
- newEntityManager();
- EntityManager em = getEntityManager();
- Long count = (Long) em.createQuery("select count(s) from StrTestEntity s where s.str = 'x'").getSingleResult();
- assert count == 0l;
-
- SimpleJtaTransactionManagerImpl.getInstance().commit();
- }
-}
Copied: core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java (from rev 19929, core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java)
===================================================================
--- core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java (rev 0)
+++ core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaExceptionListener.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -0,0 +1,74 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.envers.test.integration.jta;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.envers.test.AbstractEntityTest;
+import org.hibernate.envers.test.entities.StrTestEntity;
+import org.hibernate.envers.test.integration.reventity.ExceptionListenerRevEntity;
+import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+
+/**
+ * Same as {@link org.hibernate.envers.test.integration.reventity.ExceptionListener}, but in a JTA environment.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JtaExceptionListener extends AbstractEntityTest {
+ public void configure(Ejb3Configuration cfg) {
+ cfg.addAnnotatedClass(StrTestEntity.class);
+ cfg.addAnnotatedClass(ExceptionListenerRevEntity.class);
+
+ addJTAConfig(cfg);
+ }
+
+ @Test(expectedExceptions = RuntimeException.class)
+ public void testTransactionRollback() throws Exception {
+ SimpleJtaTransactionManagerImpl.getInstance().begin();
+
+ // Trying to persist an entity - however the listener should throw an exception, so the entity
+ // shouldn't be persisted
+ newEntityManager();
+ EntityManager em = getEntityManager();
+ em.getTransaction().begin();
+ StrTestEntity te = new StrTestEntity("x");
+ em.persist(te);
+
+ SimpleJtaTransactionManagerImpl.getInstance().commit();
+ }
+
+ @Test(dependsOnMethods = "testTransactionRollback")
+ public void testDataNotPersisted() throws Exception {
+ SimpleJtaTransactionManagerImpl.getInstance().begin();
+
+ // Checking if the entity became persisted
+ newEntityManager();
+ EntityManager em = getEntityManager();
+ Long count = (Long) em.createQuery("select count(s) from StrTestEntity s where s.str = 'x'").getSingleResult();
+ assert count == 0l;
+
+ SimpleJtaTransactionManagerImpl.getInstance().commit();
+ }
+}
Deleted: core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java 2010-07-10 13:48:15 UTC (rev 19929)
+++ core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -1,63 +0,0 @@
-package org.hibernate.envers.test.integration.jta;
-
-import org.hibernate.ejb.Ejb3Configuration;
-import org.hibernate.envers.test.AbstractEntityTest;
-import org.hibernate.envers.test.entities.IntTestEntity;
-import org.hibernate.testing.tm.SimpleJtaTransactionManagerImpl;
-import org.testng.annotations.Test;
-
-import javax.persistence.EntityManager;
-import java.util.Arrays;
-
-/**
- * Same as {@link org.hibernate.envers.test.integration.basic.Simple}, but in a JTA environment.
- * @author Adam Warski (adam at warski dot org)
- */
-public class JtaTransaction extends AbstractEntityTest {
- private Integer id1;
-
- public void configure(Ejb3Configuration cfg) {
- cfg.addAnnotatedClass(IntTestEntity.class);
-
- addJTAConfig(cfg);
- }
-
- @Test
- public void initData() throws Exception {
- SimpleJtaTransactionManagerImpl.getInstance().begin();
-
- newEntityManager();
- EntityManager em = getEntityManager();
- em.joinTransaction();
- IntTestEntity ite = new IntTestEntity(10);
- em.persist(ite);
- id1 = ite.getId();
-
- SimpleJtaTransactionManagerImpl.getInstance().commit();
-
- //
-
- SimpleJtaTransactionManagerImpl.getInstance().begin();
-
- newEntityManager();
- em = getEntityManager();
- ite = em.find(IntTestEntity.class, id1);
- ite.setNumber(20);
-
- SimpleJtaTransactionManagerImpl.getInstance().commit();
- }
-
- @Test(dependsOnMethods = "initData")
- public void testRevisionsCounts() throws Exception {
- assert Arrays.asList(1, 2).equals(getAuditReader().getRevisions(IntTestEntity.class, id1));
- }
-
- @Test(dependsOnMethods = "initData")
- public void testHistoryOfId1() {
- IntTestEntity ver1 = new IntTestEntity(10, id1);
- IntTestEntity ver2 = new IntTestEntity(20, id1);
-
- assert getAuditReader().find(IntTestEntity.class, id1, 1).equals(ver1);
- assert getAuditReader().find(IntTestEntity.class, id1, 2).equals(ver2);
- }
-}
Copied: core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java (from rev 19929, core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java)
===================================================================
--- core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java (rev 0)
+++ core/branches/Branch_3_5/envers/src/test/java/org/hibernate/envers/test/integration/jta/JtaTransaction.java 2010-07-15 17:19:20 UTC (rev 19957)
@@ -0,0 +1,63 @@
+package org.hibernate.envers.test.integration.jta;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.envers.test.AbstractEntityTest;
+import org.hibernate.envers.test.entities.IntTestEntity;
+import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * Same as {@link org.hibernate.envers.test.integration.basic.Simple}, but in a JTA environment.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JtaTransaction extends AbstractEntityTest {
+ private Integer id1;
+
+ public void configure(Ejb3Configuration cfg) {
+ cfg.addAnnotatedClass(IntTestEntity.class);
+
+ addJTAConfig(cfg);
+ }
+
+ @Test
+ public void initData() throws Exception {
+ SimpleJtaTransactionManagerImpl.getInstance().begin();
+
+ newEntityManager();
+ EntityManager em = getEntityManager();
+ em.joinTransaction();
+ IntTestEntity ite = new IntTestEntity(10);
+ em.persist(ite);
+ id1 = ite.getId();
+
+ SimpleJtaTransactionManagerImpl.getInstance().commit();
+
+ //
+
+ SimpleJtaTransactionManagerImpl.getInstance().begin();
+
+ newEntityManager();
+ em = getEntityManager();
+ ite = em.find(IntTestEntity.class, id1);
+ ite.setNumber(20);
+
+ SimpleJtaTransactionManagerImpl.getInstance().commit();
+ }
+
+ @Test(dependsOnMethods = "initData")
+ public void testRevisionsCounts() throws Exception {
+ assert Arrays.asList(1, 2).equals(getAuditReader().getRevisions(IntTestEntity.class, id1));
+ }
+
+ @Test(dependsOnMethods = "initData")
+ public void testHistoryOfId1() {
+ IntTestEntity ver1 = new IntTestEntity(10, id1);
+ IntTestEntity ver2 = new IntTestEntity(20, id1);
+
+ assert getAuditReader().find(IntTestEntity.class, id1, 1).equals(ver1);
+ assert getAuditReader().find(IntTestEntity.class, id1, 2).equals(ver2);
+ }
+}
Modified: core/branches/Branch_3_5/envers/src/test/resources/hibernate.test.cfg.xml
===================================================================
--- core/branches/Branch_3_5/envers/src/test/resources/hibernate.test.cfg.xml 2010-07-15 15:58:43 UTC (rev 19956)
+++ core/branches/Branch_3_5/envers/src/test/resources/hibernate.test.cfg.xml 2010-07-15 17:19:20 UTC (rev 19957)
@@ -12,10 +12,10 @@
<property name="format_sql">true</property>
<property name="dialect">org.hibernate.dialect.H2Dialect</property>
- <property name="connection.url">jdbc:h2:mem:envers</property>
<property name="connection.driver_class">org.h2.Driver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
+ <!-- The connection URL is set in AbstractEntityTest -->
<!--<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>-->
<!--<property name="connection.url">jdbc:mysql:///hibernate_tests?useUnicode=true&characterEncoding=UTF-8</property>-->
@@ -44,4 +44,4 @@
<listener class="org.hibernate.envers.event.AuditEventListener" />
</event>-->
</session-factory>
-</hibernate-configuration>
\ No newline at end of file
+</hibernate-configuration>
Modified: core/branches/Branch_3_5/envers/src/test/resources/testng.xml
===================================================================
--- core/branches/Branch_3_5/envers/src/test/resources/testng.xml 2010-07-15 15:58:43 UTC (rev 19956)
+++ core/branches/Branch_3_5/envers/src/test/resources/testng.xml 2010-07-15 17:19:20 UTC (rev 19957)
@@ -41,6 +41,7 @@
<package name="org.hibernate.envers.test.integration.interfaces.hbm.propertiesAudited2.subclass" />
<package name="org.hibernate.envers.test.integration.interfaces.hbm.propertiesAudited2.joined" />
<package name="org.hibernate.envers.test.integration.interfaces.hbm.propertiesAudited2.union" />
+ <package name="org.hibernate.envers.test.integration.jta" />
<package name="org.hibernate.envers.test.integration.manytomany" />
<package name="org.hibernate.envers.test.integration.manytomany.biowned" />
<package name="org.hibernate.envers.test.integration.manytomany.inverseToSuperclass" />
14 years, 4 months
Hibernate SVN: r19956 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2010-07-15 11:58:43 -0400 (Thu, 15 Jul 2010)
New Revision: 19956
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml
Log:
HHH-5380 Added annotations in the cache configuration mix
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml 2010-07-15 09:27:56 UTC (rev 19955)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/performance.xml 2010-07-15 15:58:43 UTC (rev 19956)
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
@@ -22,150 +22,128 @@
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
-
-<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;
-
]>
-
<chapter id="performance">
- <title>Improving performance</title>
+ <title>Improving performance</title>
- <section id="performance-fetching" revision="2">
- <title>Fetching strategies</title>
+ <section id="performance-fetching" revision="2">
+ <title>Fetching strategies</title>
- <para>
- Hibernate uses a <emphasis>fetching strategy</emphasis> to
- retrieve associated objects if the application needs to navigate the association.
- Fetch strategies can be declared in the O/R mapping metadata, or over-ridden by a
- particular HQL or <literal>Criteria</literal> query.
- </para>
+ <para>Hibernate uses a <emphasis>fetching strategy</emphasis> to retrieve
+ associated objects if the application needs to navigate the association.
+ Fetch strategies can be declared in the O/R mapping metadata, or
+ over-ridden by a particular HQL or <literal>Criteria</literal>
+ query.</para>
- <para>
- Hibernate3 defines the following fetching strategies:
- </para>
+ <para>Hibernate3 defines the following fetching strategies:</para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>Join fetching</emphasis>: Hibernate retrieves the
- associated instance or collection in the same <literal>SELECT</literal>,
- using an <literal>OUTER JOIN</literal>.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Select fetching</emphasis>: a second <literal>SELECT</literal>
- is used to retrieve the associated entity or collection. Unless
- you explicitly disable lazy fetching by specifying <literal>lazy="false"</literal>,
- this second select will only be executed when you access the
- association.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Subselect fetching</emphasis>: a second <literal>SELECT</literal>
- is used to retrieve the associated collections for all entities retrieved in a
- previous query or fetch. Unless you explicitly disable lazy fetching by specifying
- <literal>lazy="false"</literal>, this second select will only be executed when you
- access the association.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Batch fetching</emphasis>: an optimization strategy
- for select fetching. Hibernate retrieves a batch of entity instances
- or collections in a single <literal>SELECT</literal> by specifying
- a list of primary or foreign keys.
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- Hibernate also distinguishes between:
- </para>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>Join fetching</emphasis>: Hibernate retrieves the
+ associated instance or collection in the same
+ <literal>SELECT</literal>, using an <literal>OUTER
+ JOIN</literal>.</para>
+ </listitem>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis>Immediate fetching</emphasis>: an association, collection or
- attribute is fetched immediately when the owner is loaded.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Lazy collection fetching</emphasis>: a collection is fetched
- when the application invokes an operation upon that collection. This
- is the default for collections.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>"Extra-lazy" collection fetching</emphasis>: individual
- elements of the collection are accessed from the database as needed.
- Hibernate tries not to fetch the whole collection into memory unless
- absolutely needed. It is suitable for large collections.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Proxy fetching</emphasis>: a single-valued association is
- fetched when a method other than the identifier getter is invoked
- upon the associated object.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>"No-proxy" fetching</emphasis>: a single-valued association is
- fetched when the instance variable is accessed. Compared to proxy fetching,
- this approach is less lazy; the association is fetched even when only the
- identifier is accessed. It is also more transparent, since no proxy is visible to
- the application. This approach requires buildtime bytecode instrumentation
- and is rarely necessary.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis>Lazy attribute fetching</emphasis>: an attribute or single
- valued association is fetched when the instance variable is accessed.
- This approach requires buildtime bytecode instrumentation and is rarely
- necessary.
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- We have two orthogonal notions here: <emphasis>when</emphasis> is the association
- fetched and <emphasis>how</emphasis> is it fetched. It is important that you do not
- confuse them. We use <literal>fetch</literal> to tune performance. We can use
- <literal>lazy</literal> to define a contract for what data is always available
- in any detached instance of a particular class.
- </para>
-
- <section id="performance-fetching-lazy">
- <title>Working with lazy associations</title>
-
- <para>
- By default, Hibernate3 uses lazy select fetching for collections and lazy proxy
- fetching for single-valued associations. These defaults make sense for most
- associations in the majority of applications.
- </para>
-
- <para>
- If you set
- <literal>hibernate.default_batch_fetch_size</literal>, Hibernate will use the
- batch fetch optimization for lazy fetching. This optimization can also be enabled
- at a more granular level.
- </para>
-
- <para>
- Please be aware that access to a
- lazy association outside of the context of an open Hibernate session will result
- in an exception. For example:
- </para>
-
- <programlisting role="JAVA"><![CDATA[s = sessions.openSession();
+ <listitem>
+ <para><emphasis>Select fetching</emphasis>: a second
+ <literal>SELECT</literal> is used to retrieve the associated entity or
+ collection. Unless you explicitly disable lazy fetching by specifying
+ <literal>lazy="false"</literal>, this second select will only be
+ executed when you access the association.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>Subselect fetching</emphasis>: a second
+ <literal>SELECT</literal> is used to retrieve the associated
+ collections for all entities retrieved in a previous query or fetch.
+ Unless you explicitly disable lazy fetching by specifying
+ <literal>lazy="false"</literal>, this second select will only be
+ executed when you access the association.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>Batch fetching</emphasis>: an optimization strategy
+ for select fetching. Hibernate retrieves a batch of entity instances
+ or collections in a single <literal>SELECT</literal> by specifying a
+ list of primary or foreign keys.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Hibernate also distinguishes between:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>Immediate fetching</emphasis>: an association,
+ collection or attribute is fetched immediately when the owner is
+ loaded.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>Lazy collection fetching</emphasis>: a collection is
+ fetched when the application invokes an operation upon that
+ collection. This is the default for collections.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>"Extra-lazy" collection fetching</emphasis>:
+ individual elements of the collection are accessed from the database
+ as needed. Hibernate tries not to fetch the whole collection into
+ memory unless absolutely needed. It is suitable for large
+ collections.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>Proxy fetching</emphasis>: a single-valued association
+ is fetched when a method other than the identifier getter is invoked
+ upon the associated object.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>"No-proxy" fetching</emphasis>: a single-valued
+ association is fetched when the instance variable is accessed.
+ Compared to proxy fetching, this approach is less lazy; the
+ association is fetched even when only the identifier is accessed. It
+ is also more transparent, since no proxy is visible to the
+ application. This approach requires buildtime bytecode instrumentation
+ and is rarely necessary.</para>
+ </listitem>
+
+ <listitem>
+ <para><emphasis>Lazy attribute fetching</emphasis>: an attribute or
+ single valued association is fetched when the instance variable is
+ accessed. This approach requires buildtime bytecode instrumentation
+ and is rarely necessary.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>We have two orthogonal notions here: <emphasis>when</emphasis> is
+ the association fetched and <emphasis>how</emphasis> is it fetched. It is
+ important that you do not confuse them. We use <literal>fetch</literal> to
+ tune performance. We can use <literal>lazy</literal> to define a contract
+ for what data is always available in any detached instance of a particular
+ class.</para>
+
+ <section id="performance-fetching-lazy">
+ <title>Working with lazy associations</title>
+
+ <para>By default, Hibernate3 uses lazy select fetching for collections
+ and lazy proxy fetching for single-valued associations. These defaults
+ make sense for most associations in the majority of applications.</para>
+
+ <para>If you set <literal>hibernate.default_batch_fetch_size</literal>,
+ Hibernate will use the batch fetch optimization for lazy fetching. This
+ optimization can also be enabled at a more granular level.</para>
+
+ <para>Please be aware that access to a lazy association outside of the
+ context of an open Hibernate session will result in an exception. For
+ example:</para>
+
+ <programlisting role="JAVA">s = sessions.openSession();
Transaction tx = s.beginTransaction();
User u = (User) s.createQuery("from User u where u.name=:userName")
@@ -175,1260 +153,1383 @@
tx.commit();
s.close();
-Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
+Integer accessLevel = (Integer) permissions.get("accounts"); // Error!</programlisting>
- <para>
- Since the permissions collection was not initialized when the
- <literal>Session</literal> was closed, the collection will not be able to
- load its state. <emphasis>Hibernate does not support lazy initialization
- for detached objects</emphasis>. This can be fixed by moving the code that reads
- from the collection to just before the transaction is committed.
- </para>
-
- <para>
- Alternatively, you can use a non-lazy collection or association,
- by specifying <literal>lazy="false"</literal> for the association mapping.
- However, it is intended that lazy initialization be used for almost all
- collections and associations. If you define too many non-lazy associations
- in your object model, Hibernate will fetch the entire
- database into memory in every transaction.
- </para>
-
- <para>
- On the other hand, you can use join fetching, which is non-lazy by
- nature, instead of select fetching in a particular transaction. We will now explain
- how to customize the fetching strategy. In Hibernate3, the mechanisms for
- choosing a fetch strategy are identical for single-valued associations and
- collections.
- </para>
-
- </section>
-
- <section id="performance-fetching-custom" revision="4">
- <title>Tuning fetch strategies</title>
-
- <para>
- Select fetching (the default) is extremely vulnerable to N+1 selects problems,
- so we might want to enable join fetching in the mapping document:
- </para>
-
- <programlisting role="XML"><![CDATA[<set name="permissions"
- fetch="join">
- <key column="userId"/>
- <one-to-many class="Permission"/>
-</set]]></programlisting>
+ <para>Since the permissions collection was not initialized when the
+ <literal>Session</literal> was closed, the collection will not be able
+ to load its state. <emphasis>Hibernate does not support lazy
+ initialization for detached objects</emphasis>. This can be fixed by
+ moving the code that reads from the collection to just before the
+ transaction is committed.</para>
- <programlisting role="XML"><![CDATA[<many-to-one name="mother" class="Cat" fetch="join"/>]]></programlisting>
+ <para>Alternatively, you can use a non-lazy collection or association,
+ by specifying <literal>lazy="false"</literal> for the association
+ mapping. However, it is intended that lazy initialization be used for
+ almost all collections and associations. If you define too many non-lazy
+ associations in your object model, Hibernate will fetch the entire
+ database into memory in every transaction.</para>
- <para>
- The <literal>fetch</literal> strategy defined in the mapping document affects:
- </para>
-
- <itemizedlist>
- <listitem>
- <para>
- retrieval via <literal>get()</literal> or <literal>load()</literal>
- </para>
- </listitem>
- <listitem>
- <para>
- retrieval that happens implicitly when an association is navigated
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>Criteria</literal> queries
- </para>
- </listitem>
- <listitem>
- <para>
- HQL queries if <literal>subselect</literal> fetching is used
- </para>
- </listitem>
- </itemizedlist>
+ <para>On the other hand, you can use join fetching, which is non-lazy by
+ nature, instead of select fetching in a particular transaction. We will
+ now explain how to customize the fetching strategy. In Hibernate3, the
+ mechanisms for choosing a fetch strategy are identical for single-valued
+ associations and collections.</para>
+ </section>
- <para>
- Irrespective of the fetching strategy you use, the defined non-lazy graph is guaranteed
- to be loaded into memory. This might, however, result in several immediate selects
- being used to execute a particular HQL query.
- </para>
+ <section id="performance-fetching-custom" revision="4">
+ <title>Tuning fetch strategies</title>
- <para>
- Usually, the mapping document is not used to customize fetching. Instead, we
- keep the default behavior, and override it for a particular transaction, using
- <literal>left join fetch</literal> in HQL. This tells Hibernate to fetch
- the association eagerly in the first select, using an outer join. In the
- <literal>Criteria</literal> query API, you would use
- <literal>setFetchMode(FetchMode.JOIN)</literal>.
- </para>
-
- <para>
- If you want to change the fetching strategy used by
- <literal>get()</literal> or <literal>load()</literal>, you can use a
- <literal>Criteria</literal> query. For example:
- </para>
-
- <programlisting role="JAVA"><![CDATA[User user = (User) session.createCriteria(User.class)
+ <para>Select fetching (the default) is extremely vulnerable to N+1
+ selects problems, so we might want to enable join fetching in the
+ mapping document:</para>
+
+ <programlisting role="XML"><set name="permissions"
+ fetch="join">
+ <key column="userId"/>
+ <one-to-many class="Permission"/>
+</set</programlisting>
+
+ <programlisting role="XML"><many-to-one name="mother" class="Cat" fetch="join"/></programlisting>
+
+ <para>The <literal>fetch</literal> strategy defined in the mapping
+ document affects:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>retrieval via <literal>get()</literal> or
+ <literal>load()</literal></para>
+ </listitem>
+
+ <listitem>
+ <para>retrieval that happens implicitly when an association is
+ navigated</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>Criteria</literal> queries</para>
+ </listitem>
+
+ <listitem>
+ <para>HQL queries if <literal>subselect</literal> fetching is
+ used</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>Irrespective of the fetching strategy you use, the defined
+ non-lazy graph is guaranteed to be loaded into memory. This might,
+ however, result in several immediate selects being used to execute a
+ particular HQL query.</para>
+
+ <para>Usually, the mapping document is not used to customize fetching.
+ Instead, we keep the default behavior, and override it for a particular
+ transaction, using <literal>left join fetch</literal> in HQL. This tells
+ Hibernate to fetch the association eagerly in the first select, using an
+ outer join. In the <literal>Criteria</literal> query API, you would use
+ <literal>setFetchMode(FetchMode.JOIN)</literal>.</para>
+
+ <para>If you want to change the fetching strategy used by
+ <literal>get()</literal> or <literal>load()</literal>, you can use a
+ <literal>Criteria</literal> query. For example:</para>
+
+ <programlisting role="JAVA">User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
- .uniqueResult();]]></programlisting>
-
- <para>
- This is Hibernate's equivalent of what some ORM solutions call a "fetch plan".
- </para>
+ .uniqueResult();</programlisting>
- <para>
- A completely different approach to problems with N+1 selects is to use the
- second-level cache.
- </para>
+ <para>This is Hibernate's equivalent of what some ORM solutions call a
+ "fetch plan".</para>
- </section>
+ <para>A completely different approach to problems with N+1 selects is to
+ use the second-level cache.</para>
+ </section>
- <section id="performance-fetching-proxies" revision="2">
- <title>Single-ended association proxies</title>
+ <section id="performance-fetching-proxies" revision="2">
+ <title>Single-ended association proxies</title>
- <para>
- Lazy fetching for collections is implemented using Hibernate's own implementation
- of persistent collections. However, a different mechanism is needed for lazy
- behavior in single-ended associations. The target entity of the association must
- be proxied. Hibernate implements lazy initializing proxies for persistent objects
- using runtime bytecode enhancement which is accessed via the CGLIB library.
- </para>
+ <para>Lazy fetching for collections is implemented using Hibernate's own
+ implementation of persistent collections. However, a different mechanism
+ is needed for lazy behavior in single-ended associations. The target
+ entity of the association must be proxied. Hibernate implements lazy
+ initializing proxies for persistent objects using runtime bytecode
+ enhancement which is accessed via the CGLIB library.</para>
- <para>
- At startup, Hibernate3 generates proxies by default for all persistent classes
- and uses them to enable lazy fetching of <literal>many-to-one</literal> and
- <literal>one-to-one</literal> associations.
- </para>
+ <para>At startup, Hibernate3 generates proxies by default for all
+ persistent classes and uses them to enable lazy fetching of
+ <literal>many-to-one</literal> and <literal>one-to-one</literal>
+ associations.</para>
- <para>
- The mapping file may declare an interface to use as the proxy interface for that
- class, with the <literal>proxy</literal> attribute. By default, Hibernate uses a subclass
- of the class. <emphasis>The proxied class must implement a default constructor
- with at least package visibility. This constructor is recommended for all persistent classes</emphasis>.
- </para>
+ <para>The mapping file may declare an interface to use as the proxy
+ interface for that class, with the <literal>proxy</literal> attribute.
+ By default, Hibernate uses a subclass of the class. <emphasis>The
+ proxied class must implement a default constructor with at least package
+ visibility. This constructor is recommended for all persistent
+ classes</emphasis>.</para>
- <para>
- There are potential problems to note when extending this approach to polymorphic classes.For example:
- </para>
+ <para>There are potential problems to note when extending this approach
+ to polymorphic classes.For example:</para>
- <programlisting role="XML"><![CDATA[<class name="Cat" proxy="Cat">
+ <programlisting role="XML"><class name="Cat" proxy="Cat">
......
- <subclass name="DomesticCat">
+ <subclass name="DomesticCat">
.....
- </subclass>
-</class>]]></programlisting>
+ </subclass>
+</class></programlisting>
- <para>
- Firstly, instances of <literal>Cat</literal> will never be castable to
- <literal>DomesticCat</literal>, even if the underlying instance is an
- instance of <literal>DomesticCat</literal>:
- </para>
+ <para>Firstly, instances of <literal>Cat</literal> will never be
+ castable to <literal>DomesticCat</literal>, even if the underlying
+ instance is an instance of <literal>DomesticCat</literal>:</para>
- <programlisting role="JAVA"><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
+ <programlisting role="JAVA">Cat cat = (Cat) session.load(Cat.class, id); // instantiate a proxy (does not hit the db)
if ( cat.isDomesticCat() ) { // hit the db to initialize the proxy
DomesticCat dc = (DomesticCat) cat; // Error!
....
-}]]></programlisting>
+}</programlisting>
- <para>
- Secondly, it is possible to break proxy <literal>==</literal>:
- </para>
+ <para>Secondly, it is possible to break proxy
+ <literal>==</literal>:</para>
- <programlisting role="JAVA"><![CDATA[Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
+ <programlisting role="JAVA">Cat cat = (Cat) session.load(Cat.class, id); // instantiate a Cat proxy
DomesticCat dc =
(DomesticCat) session.load(DomesticCat.class, id); // acquire new DomesticCat proxy!
-System.out.println(cat==dc); // false]]></programlisting>
+System.out.println(cat==dc); // false</programlisting>
- <para>
- However, the situation is not quite as bad as it looks. Even though we now have two references
- to different proxy objects, the underlying instance will still be the same object:
- </para>
+ <para>However, the situation is not quite as bad as it looks. Even
+ though we now have two references to different proxy objects, the
+ underlying instance will still be the same object:</para>
- <programlisting role="JAVA"><![CDATA[cat.setWeight(11.0); // hit the db to initialize the proxy
-System.out.println( dc.getWeight() ); // 11.0]]></programlisting>
+ <programlisting role="JAVA">cat.setWeight(11.0); // hit the db to initialize the proxy
+System.out.println( dc.getWeight() ); // 11.0</programlisting>
- <para>
- Third, you cannot use a CGLIB proxy for a <literal>final</literal> class or a class
- with any <literal>final</literal> methods.
- </para>
+ <para>Third, you cannot use a CGLIB proxy for a <literal>final</literal>
+ class or a class with any <literal>final</literal> methods.</para>
- <para>
- Finally, if your persistent object acquires any resources upon instantiation (e.g. in
- initializers or default constructor), then those resources will also be acquired by
- the proxy. The proxy class is an actual subclass of the persistent class.
- </para>
+ <para>Finally, if your persistent object acquires any resources upon
+ instantiation (e.g. in initializers or default constructor), then those
+ resources will also be acquired by the proxy. The proxy class is an
+ actual subclass of the persistent class.</para>
- <para>
- These problems are all due to fundamental limitations in Java's single inheritance model.
- To avoid these problems your persistent classes must each implement an interface
- that declares its business methods. You should specify these interfaces in the mapping file where
- <literal>CatImpl</literal> implements the interface <literal>Cat</literal> and <literal>DomesticCatImpl</literal>
- implements the interface <literal>DomesticCat</literal>. For example:
- </para>
+ <para>These problems are all due to fundamental limitations in Java's
+ single inheritance model. To avoid these problems your persistent
+ classes must each implement an interface that declares its business
+ methods. You should specify these interfaces in the mapping file where
+ <literal>CatImpl</literal> implements the interface
+ <literal>Cat</literal> and <literal>DomesticCatImpl</literal> implements
+ the interface <literal>DomesticCat</literal>. For example:</para>
- <programlisting role="XML"><![CDATA[<class name="CatImpl" proxy="Cat">
+ <programlisting role="XML"><class name="CatImpl" proxy="Cat">
......
- <subclass name="DomesticCatImpl" proxy="DomesticCat">
+ <subclass name="DomesticCatImpl" proxy="DomesticCat">
.....
- </subclass>
-</class>]]></programlisting>
+ </subclass>
+</class></programlisting>
- <para>
- Then proxies for instances of <literal>Cat</literal> and <literal>DomesticCat</literal> can be returned
- by <literal>load()</literal> or <literal>iterate()</literal>.
- </para>
+ <para>Then proxies for instances of <literal>Cat</literal> and
+ <literal>DomesticCat</literal> can be returned by
+ <literal>load()</literal> or <literal>iterate()</literal>.</para>
- <programlisting role="JAVA"><![CDATA[Cat cat = (Cat) session.load(CatImpl.class, catid);
+ <programlisting role="JAVA">Cat cat = (Cat) session.load(CatImpl.class, catid);
Iterator iter = session.createQuery("from CatImpl as cat where cat.name='fritz'").iterate();
-Cat fritz = (Cat) iter.next();]]></programlisting>
+Cat fritz = (Cat) iter.next();</programlisting>
-
- <note><title>Note</title>
- <para>
- <literal>list()</literal> does not usually return proxies.
- </para>
- </note>
-
- <para>
- Relationships are also lazily initialized. This means you must declare any properties to be of
- type <literal>Cat</literal>, not <literal>CatImpl</literal>.
- </para>
+ <note>
+ <title>Note</title>
- <para>
- Certain operations do <emphasis>not</emphasis> require proxy initialization:
- </para>
+ <para><literal>list()</literal> does not usually return
+ proxies.</para>
+ </note>
- <itemizedlist spacing="compact">
- <listitem>
- <para>
- <literal>equals()</literal>: if the persistent class does not override
- <literal>equals()</literal>
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>hashCode()</literal>: if the persistent class does not override
- <literal>hashCode()</literal>
- </para>
- </listitem>
- <listitem>
- <para>
- The identifier getter method
- </para>
- </listitem>
- </itemizedlist>
+ <para>Relationships are also lazily initialized. This means you must
+ declare any properties to be of type <literal>Cat</literal>, not
+ <literal>CatImpl</literal>.</para>
- <para>
- Hibernate will detect persistent classes that override <literal>equals()</literal> or
- <literal>hashCode()</literal>.
- </para>
-
- <para>
- By choosing <literal>lazy="no-proxy"</literal> instead of the default
- <literal>lazy="proxy"</literal>, you can avoid problems associated with typecasting.
- However, buildtime bytecode instrumentation is required, and all operations
- will result in immediate proxy initialization.
- </para>
+ <para>Certain operations do <emphasis>not</emphasis> require proxy
+ initialization:</para>
- </section>
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para><literal>equals()</literal>: if the persistent class does not
+ override <literal>equals()</literal></para>
+ </listitem>
- <section id="performance-fetching-initialization" revision="1">
- <title>Initializing collections and proxies</title>
+ <listitem>
+ <para><literal>hashCode()</literal>: if the persistent class does
+ not override <literal>hashCode()</literal></para>
+ </listitem>
- <para>
- A <literal>LazyInitializationException</literal> will be thrown by Hibernate if an uninitialized
- collection or proxy is accessed outside of the scope of the <literal>Session</literal>, i.e., when
- the entity owning the collection or having the reference to the proxy is in the detached state.
- </para>
+ <listitem>
+ <para>The identifier getter method</para>
+ </listitem>
+ </itemizedlist>
- <para>
- Sometimes a proxy or collection needs to be initialized before closing the
- <literal>Session</literal>. You can force initialization by calling
- <literal>cat.getSex()</literal> or <literal>cat.getKittens().size()</literal>, for example.
- However, this can be confusing to readers of the code and it is not convenient for generic code.
- </para>
+ <para>Hibernate will detect persistent classes that override
+ <literal>equals()</literal> or <literal>hashCode()</literal>.</para>
- <para>
- The static methods <literal>Hibernate.initialize()</literal> and <literal>Hibernate.isInitialized()</literal>,
- provide the application with a convenient way of working with lazily initialized collections or
- proxies. <literal>Hibernate.initialize(cat)</literal> will force the initialization of a proxy,
- <literal>cat</literal>, as long as its <literal>Session</literal> is still open.
- <literal>Hibernate.initialize( cat.getKittens() )</literal> has a similar effect for the collection
- of kittens.
- </para>
+ <para>By choosing <literal>lazy="no-proxy"</literal> instead of the
+ default <literal>lazy="proxy"</literal>, you can avoid problems
+ associated with typecasting. However, buildtime bytecode instrumentation
+ is required, and all operations will result in immediate proxy
+ initialization.</para>
+ </section>
- <para>
- Another option is to keep the <literal>Session</literal> open until all required
- collections and proxies have been loaded. In some application architectures,
- particularly where the code that accesses data using Hibernate, and the code that
- uses it are in different application layers or different physical processes, it
- can be a problem to ensure that the <literal>Session</literal> is open when a
- collection is initialized. There are two basic ways to deal with this issue:
- </para>
+ <section id="performance-fetching-initialization" revision="1">
+ <title>Initializing collections and proxies</title>
- <itemizedlist>
- <listitem>
- <para>
- In a web-based application, a servlet filter can be used to close the
- <literal>Session</literal> only at the end of a user request, once
- the rendering of the view is complete (the <emphasis>Open Session in
- View</emphasis> pattern). Of course, this places heavy demands on the
- correctness of the exception handling of your application infrastructure.
- It is vitally important that the <literal>Session</literal> is closed and the
- transaction ended before returning to the user, even when an exception occurs
- during rendering of the view. See the Hibernate Wiki for examples of this
- "Open Session in View" pattern.
- </para>
- </listitem>
- <listitem>
- <para>
- In an application with a separate business tier, the business logic must
- "prepare" all collections that the web tier needs before
- returning. This means that the business tier should load all the data and
- return all the data already initialized to the presentation/web tier that
- is required for a particular use case. Usually, the application calls
- <literal>Hibernate.initialize()</literal> for each collection that will
- be needed in the web tier (this call must occur before the session is closed)
- or retrieves the collection eagerly using a Hibernate query with a
- <literal>FETCH</literal> clause or a <literal>FetchMode.JOIN</literal> in
- <literal>Criteria</literal>. This is usually easier if you adopt the
- <emphasis>Command</emphasis> pattern instead of a <emphasis>Session Facade</emphasis>.
- </para>
- </listitem>
- <listitem>
- <para>
- You can also attach a previously loaded object to a new <literal>Session</literal>
- with <literal>merge()</literal> or <literal>lock()</literal> before
- accessing uninitialized collections or other proxies. Hibernate does not,
- and certainly <emphasis>should</emphasis> not, do this automatically since it
- would introduce impromptu transaction semantics.
- </para>
- </listitem>
- </itemizedlist>
+ <para>A <literal>LazyInitializationException</literal> will be thrown by
+ Hibernate if an uninitialized collection or proxy is accessed outside of
+ the scope of the <literal>Session</literal>, i.e., when the entity
+ owning the collection or having the reference to the proxy is in the
+ detached state.</para>
- <para>
- Sometimes you do not want to initialize a large collection, but still need some
- information about it, like its size, for example, or a subset of the data.
- </para>
+ <para>Sometimes a proxy or collection needs to be initialized before
+ closing the <literal>Session</literal>. You can force initialization by
+ calling <literal>cat.getSex()</literal> or
+ <literal>cat.getKittens().size()</literal>, for example. However, this
+ can be confusing to readers of the code and it is not convenient for
+ generic code.</para>
- <para>
- You can use a collection filter to get the size of a collection without initializing it:
- </para>
+ <para>The static methods <literal>Hibernate.initialize()</literal> and
+ <literal>Hibernate.isInitialized()</literal>, provide the application
+ with a convenient way of working with lazily initialized collections or
+ proxies. <literal>Hibernate.initialize(cat)</literal> will force the
+ initialization of a proxy, <literal>cat</literal>, as long as its
+ <literal>Session</literal> is still open. <literal>Hibernate.initialize(
+ cat.getKittens() )</literal> has a similar effect for the collection of
+ kittens.</para>
- <programlisting role="JAVA"><![CDATA[( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()]]></programlisting>
+ <para>Another option is to keep the <literal>Session</literal> open
+ until all required collections and proxies have been loaded. In some
+ application architectures, particularly where the code that accesses
+ data using Hibernate, and the code that uses it are in different
+ application layers or different physical processes, it can be a problem
+ to ensure that the <literal>Session</literal> is open when a collection
+ is initialized. There are two basic ways to deal with this issue:</para>
- <para>
- The <literal>createFilter()</literal> method is also used to efficiently retrieve subsets
- of a collection without needing to initialize the whole collection:
- </para>
+ <itemizedlist>
+ <listitem>
+ <para>In a web-based application, a servlet filter can be used to
+ close the <literal>Session</literal> only at the end of a user
+ request, once the rendering of the view is complete (the
+ <emphasis>Open Session in View</emphasis> pattern). Of course, this
+ places heavy demands on the correctness of the exception handling of
+ your application infrastructure. It is vitally important that the
+ <literal>Session</literal> is closed and the transaction ended
+ before returning to the user, even when an exception occurs during
+ rendering of the view. See the Hibernate Wiki for examples of this
+ "Open Session in View" pattern.</para>
+ </listitem>
- <programlisting role="JAVA"><![CDATA[s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();]]></programlisting>
+ <listitem>
+ <para>In an application with a separate business tier, the business
+ logic must "prepare" all collections that the web tier needs before
+ returning. This means that the business tier should load all the
+ data and return all the data already initialized to the
+ presentation/web tier that is required for a particular use case.
+ Usually, the application calls
+ <literal>Hibernate.initialize()</literal> for each collection that
+ will be needed in the web tier (this call must occur before the
+ session is closed) or retrieves the collection eagerly using a
+ Hibernate query with a <literal>FETCH</literal> clause or a
+ <literal>FetchMode.JOIN</literal> in <literal>Criteria</literal>.
+ This is usually easier if you adopt the <emphasis>Command</emphasis>
+ pattern instead of a <emphasis>Session Facade</emphasis>.</para>
+ </listitem>
- </section>
+ <listitem>
+ <para>You can also attach a previously loaded object to a new
+ <literal>Session</literal> with <literal>merge()</literal> or
+ <literal>lock()</literal> before accessing uninitialized collections
+ or other proxies. Hibernate does not, and certainly
+ <emphasis>should</emphasis> not, do this automatically since it
+ would introduce impromptu transaction semantics.</para>
+ </listitem>
+ </itemizedlist>
- <section id="performance-fetching-batch">
- <title>Using batch fetching</title>
+ <para>Sometimes you do not want to initialize a large collection, but
+ still need some information about it, like its size, for example, or a
+ subset of the data.</para>
- <para>
- Using batch fetching, Hibernate can load several uninitialized
- proxies if one proxy is accessed. Batch fetching is an optimization of the lazy select
- fetching strategy. There are two ways you can configure batch fetching: on the class level and the collection level.
- </para>
+ <para>You can use a collection filter to get the size of a collection
+ without initializing it:</para>
- <para>
- Batch fetching for classes/entities is easier to understand. Consider the following example:
- at runtime you have 25 <literal>Cat</literal> instances loaded in a <literal>Session</literal>, and each
- <literal>Cat</literal> has a reference to its <literal>owner</literal>, a <literal>Person</literal>.
- The <literal>Person</literal> class is mapped with a proxy, <literal>lazy="true"</literal>. If you now
- iterate through all cats and call <literal>getOwner()</literal> on each, Hibernate will, by default,
- execute 25 <literal>SELECT</literal> statements to retrieve the proxied owners. You can tune this
- behavior by specifying a <literal>batch-size</literal> in the mapping of <literal>Person</literal>:
- </para>
+ <programlisting role="JAVA">( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()</programlisting>
- <programlisting role="XML"><![CDATA[<class name="Person" batch-size="10">...</class>]]></programlisting>
+ <para>The <literal>createFilter()</literal> method is also used to
+ efficiently retrieve subsets of a collection without needing to
+ initialize the whole collection:</para>
- <para>
- Hibernate will now execute only three queries: the pattern is 10, 10, 5.
- </para>
+ <programlisting role="JAVA">s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();</programlisting>
+ </section>
- <para>
- You can also enable batch fetching of collections. For example, if each <literal>Person</literal> has
- a lazy collection of <literal>Cat</literal>s, and 10 persons are currently loaded in the
- <literal>Session</literal>, iterating through all persons will generate 10 <literal>SELECT</literal>s,
- one for every call to <literal>getCats()</literal>. If you enable batch fetching for the
- <literal>cats</literal> collection in the mapping of <literal>Person</literal>, Hibernate can pre-fetch
- collections:
- </para>
+ <section id="performance-fetching-batch">
+ <title>Using batch fetching</title>
- <programlisting role="XML"><![CDATA[<class name="Person">
- <set name="cats" batch-size="3">
+ <para>Using batch fetching, Hibernate can load several uninitialized
+ proxies if one proxy is accessed. Batch fetching is an optimization of
+ the lazy select fetching strategy. There are two ways you can configure
+ batch fetching: on the class level and the collection level.</para>
+
+ <para>Batch fetching for classes/entities is easier to understand.
+ Consider the following example: at runtime you have 25
+ <literal>Cat</literal> instances loaded in a <literal>Session</literal>,
+ and each <literal>Cat</literal> has a reference to its
+ <literal>owner</literal>, a <literal>Person</literal>. The
+ <literal>Person</literal> class is mapped with a proxy,
+ <literal>lazy="true"</literal>. If you now iterate through all cats and
+ call <literal>getOwner()</literal> on each, Hibernate will, by default,
+ execute 25 <literal>SELECT</literal> statements to retrieve the proxied
+ owners. You can tune this behavior by specifying a
+ <literal>batch-size</literal> in the mapping of
+ <literal>Person</literal>:</para>
+
+ <programlisting role="XML"><class name="Person" batch-size="10">...</class></programlisting>
+
+ <para>Hibernate will now execute only three queries: the pattern is 10,
+ 10, 5.</para>
+
+ <para>You can also enable batch fetching of collections. For example, if
+ each <literal>Person</literal> has a lazy collection of
+ <literal>Cat</literal>s, and 10 persons are currently loaded in the
+ <literal>Session</literal>, iterating through all persons will generate
+ 10 <literal>SELECT</literal>s, one for every call to
+ <literal>getCats()</literal>. If you enable batch fetching for the
+ <literal>cats</literal> collection in the mapping of
+ <literal>Person</literal>, Hibernate can pre-fetch collections:</para>
+
+ <programlisting role="XML"><class name="Person">
+ <set name="cats" batch-size="3">
...
- </set>
-</class>]]></programlisting>
+ </set>
+</class></programlisting>
- <para>
- With a <literal>batch-size</literal> of 3, Hibernate will load 3, 3, 3, 1 collections in four
- <literal>SELECT</literal>s. Again, the value of the attribute depends on the expected number of
- uninitialized collections in a particular <literal>Session</literal>.
- </para>
+ <para>With a <literal>batch-size</literal> of 3, Hibernate will load 3,
+ 3, 3, 1 collections in four <literal>SELECT</literal>s. Again, the value
+ of the attribute depends on the expected number of uninitialized
+ collections in a particular <literal>Session</literal>.</para>
- <para>
- Batch fetching of collections is particularly useful if you have a nested tree of items, i.e.
- the typical bill-of-materials pattern. However, a <emphasis>nested set</emphasis> or a
- <emphasis>materialized path</emphasis> might be a better option for read-mostly trees.
- </para>
+ <para>Batch fetching of collections is particularly useful if you have a
+ nested tree of items, i.e. the typical bill-of-materials pattern.
+ However, a <emphasis>nested set</emphasis> or a <emphasis>materialized
+ path</emphasis> might be a better option for read-mostly trees.</para>
+ </section>
- </section>
+ <section id="performance-fetching-subselect">
+ <title>Using subselect fetching</title>
- <section id="performance-fetching-subselect">
- <title>Using subselect fetching</title>
+ <para>If one lazy collection or single-valued proxy has to be fetched,
+ Hibernate will load all of them, re-running the original query in a
+ subselect. This works in the same way as batch-fetching but without the
+ piecemeal loading.</para>
- <para>
- If one lazy collection or single-valued proxy has to be fetched, Hibernate will load all of
- them, re-running the original query in a subselect. This works in the same way as
- batch-fetching but without the piecemeal loading.
- </para>
-
- <!-- TODO: Write more about this -->
+ <!-- TODO: Write more about this -->
+ </section>
- </section>
+ <section id="performance-fetching-profiles">
+ <title>Fetch profiles</title>
- <section id="performance-fetching-profiles">
- <title>Fetch profiles</title>
+ <para>Another way to affect the fetching strategy for loading associated
+ objects is through something called a fetch profile, which is a named
+ configuration associated with the
+ <interfacename>org.hibernate.SessionFactory</interfacename> but enabled,
+ by name, on the <interfacename>org.hibernate.Session</interfacename>.
+ Once enabled on a <interfacename>org.hibernate.Session</interfacename>,
+ the fetch profile wull be in affect for that
+ <interfacename>org.hibernate.Session</interfacename> until it is
+ explicitly disabled.</para>
- <para>
- Another way to affect the fetching strategy for loading associated objects is through something
- called a fetch profile, which is a named configuration associated with the
- <interfacename>org.hibernate.SessionFactory</interfacename> but enabled, by name, on the
- <interfacename>org.hibernate.Session</interfacename>. Once enabled on a
- <interfacename>org.hibernate.Session</interfacename>, the fetch profile wull be in affect for
- that <interfacename>org.hibernate.Session</interfacename> until it is explicitly disabled.
- </para>
- <para>
- So what does that mean? Well lets explain that by way of an example. Say we have
- the following mappings:
- </para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
- <class name="Customer">
+ <para>So what does that mean? Well lets explain that by way of an
+ example. Say we have the following mappings:</para>
+
+ <programlisting role="XML"><hibernate-mapping>
+ <class name="Customer">
...
- <set name="orders" inverse="true">
- <key column="cust_id"/>
- <one-to-many class="Order"/>
- </set>
- </class>
- <class name="Order">
+ <set name="orders" inverse="true">
+ <key column="cust_id"/>
+ <one-to-many class="Order"/>
+ </set>
+ </class>
+ <class name="Order">
...
- </class>
-</hibernate-mapping>]]></programlisting>
- <para>
- Now normally when you get a reference to a particular customer, that customer's set of
- orders will be lazy meaning we will not yet have loaded those orders from the database.
- Normally this is a good thing. Now lets say that you have a certain use case where
- it is more efficient to load the customer and their orders together. One way certainly is
- to use "dynamic fetching" strategies via an HQL or criteria queries. But another option is
- to use a fetch profile to achieve that. Just add the following to your mapping:
- </para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
+ </class>
+</hibernate-mapping></programlisting>
+
+ <para>Now normally when you get a reference to a particular customer,
+ that customer's set of orders will be lazy meaning we will not yet have
+ loaded those orders from the database. Normally this is a good thing.
+ Now lets say that you have a certain use case where it is more efficient
+ to load the customer and their orders together. One way certainly is to
+ use "dynamic fetching" strategies via an HQL or criteria queries. But
+ another option is to use a fetch profile to achieve that. Just add the
+ following to your mapping:</para>
+
+ <programlisting role="XML"><hibernate-mapping>
...
- <fetch-profile name="customer-with-orders">
- <fetch entity="Customer" association="orders" style="join"/>
- </fetch-profile>
-</hibernate-mapping>]]></programlisting>
- <para>
- or even:
- </para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
- <class name="Customer">
+ <fetch-profile name="customer-with-orders">
+ <fetch entity="Customer" association="orders" style="join"/>
+ </fetch-profile>
+</hibernate-mapping></programlisting>
+
+ <para>or even:</para>
+
+ <programlisting role="XML"><hibernate-mapping>
+ <class name="Customer">
...
- <fetch-profile name="customer-with-orders">
- <fetch association="orders" style="join"/>
- </fetch-profile>
- </class>
+ <fetch-profile name="customer-with-orders">
+ <fetch association="orders" style="join"/>
+ </fetch-profile>
+ </class>
...
-</hibernate-mapping>]]></programlisting>
- <para>
- Now the following code will actually load both the customer <emphasis>and their orders</emphasis>:
- </para>
- <programlisting role="JAVA"><![CDATA[
+</hibernate-mapping></programlisting>
+
+ <para>Now the following code will actually load both the customer
+ <emphasis>and their orders</emphasis>:</para>
+
+ <programlisting role="JAVA">
Session session = ...;
session.enableFetchProfile( "customer-with-orders" ); // name matches from mapping
Customer customer = (Customer) session.get( Customer.class, customerId );
-]]></programlisting>
- <para>
- Currently only join style fetch profiles are supported, but they plan is to support additional
- styles. See <ulink url="http://opensource.atlassian.com/projects/hibernate/browse/HHH-3414">HHH-3414</ulink>
- for details.
- </para>
- </section>
+</programlisting>
- <section id="performance-fetching-lazyproperties">
- <title>Using lazy property fetching</title>
+ <para>Currently only join style fetch profiles are supported, but they
+ plan is to support additional styles. See <ulink
+ url="http://opensource.atlassian.com/projects/hibernate/browse/HHH-3414">HHH-3414</ulink>
+ for details.</para>
+ </section>
- <para>
- Hibernate3 supports the lazy fetching of individual properties. This optimization technique
- is also known as <emphasis>fetch groups</emphasis>. Please note that this is mostly a
- marketing feature; optimizing row reads is much more important than
- optimization of column reads. However, only loading some properties of a class could
- be useful in extreme cases. For example, when legacy tables have hundreds of columns and the data model
- cannot be improved.
- </para>
+ <section id="performance-fetching-lazyproperties">
+ <title>Using lazy property fetching</title>
- <para>
- To enable lazy property loading, set the <literal>lazy</literal> attribute on your
- particular property mappings:
- </para>
+ <para>Hibernate3 supports the lazy fetching of individual properties.
+ This optimization technique is also known as <emphasis>fetch
+ groups</emphasis>. Please note that this is mostly a marketing feature;
+ optimizing row reads is much more important than optimization of column
+ reads. However, only loading some properties of a class could be useful
+ in extreme cases. For example, when legacy tables have hundreds of
+ columns and the data model cannot be improved.</para>
- <programlisting role="XML"><![CDATA[<class name="Document">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name" not-null="true" length="50"/>
- <property name="summary" not-null="true" length="200" lazy="true"/>
- <property name="text" not-null="true" length="2000" lazy="true"/>
-</class>]]></programlisting>
+ <para>To enable lazy property loading, set the <literal>lazy</literal>
+ attribute on your particular property mappings:</para>
- <para>
- Lazy property loading requires buildtime bytecode instrumentation. If your persistent
- classes are not enhanced, Hibernate will ignore lazy property settings and
- return to immediate fetching.
- </para>
+ <programlisting role="XML"><class name="Document">
+ <id name="id">
+ <generator class="native"/>
+ </id>
+ <property name="name" not-null="true" length="50"/>
+ <property name="summary" not-null="true" length="200" lazy="true"/>
+ <property name="text" not-null="true" length="2000" lazy="true"/>
+</class></programlisting>
- <para>
- For bytecode instrumentation, use the following Ant task:
- </para>
+ <para>Lazy property loading requires buildtime bytecode instrumentation.
+ If your persistent classes are not enhanced, Hibernate will ignore lazy
+ property settings and return to immediate fetching.</para>
- <programlisting role="XML"><![CDATA[<target name="instrument" depends="compile">
- <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
- <classpath path="${jar.path}"/>
- <classpath path="${classes.dir}"/>
- <classpath refid="lib.class.path"/>
- </taskdef>
+ <para>For bytecode instrumentation, use the following Ant task:</para>
- <instrument verbose="true">
- <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
- <include name="*.class"/>
- </fileset>
- </instrument>
-</target>]]></programlisting>
+ <programlisting role="XML"><target name="instrument" depends="compile">
+ <taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
+ <classpath path="${jar.path}"/>
+ <classpath path="${classes.dir}"/>
+ <classpath refid="lib.class.path"/>
+ </taskdef>
- <para>
- A different way of avoiding unnecessary column reads, at least for
- read-only transactions, is to use the projection features of HQL or Criteria
- queries. This avoids the need for buildtime bytecode processing and is
- certainly a preferred solution.
- </para>
-
- <para>
- You can force the usual eager fetching of properties using <literal>fetch all
- properties</literal> in HQL.
- </para>
+ <instrument verbose="true">
+ <fileset dir="${testclasses.dir}/org/hibernate/auction/model">
+ <include name="*.class"/>
+ </fileset>
+ </instrument>
+</target></programlisting>
- </section>
+ <para>A different way of avoiding unnecessary column reads, at least for
+ read-only transactions, is to use the projection features of HQL or
+ Criteria queries. This avoids the need for buildtime bytecode processing
+ and is certainly a preferred solution.</para>
+
+ <para>You can force the usual eager fetching of properties using
+ <literal>fetch all properties</literal> in HQL.</para>
</section>
+ </section>
- <section id="performance-cache" revision="1">
- <title>The Second Level Cache</title>
+ <section id="performance-cache" revision="1">
+ <title>The Second Level Cache</title>
- <para>
- A Hibernate <literal>Session</literal> is a transaction-level cache of persistent data. It is
- possible to configure a cluster or JVM-level (<literal>SessionFactory</literal>-level) cache on
- a class-by-class and collection-by-collection basis. You can even plug in a clustered cache. Be
- aware that caches are not aware of changes made to the persistent store by another application.
- They can, however, be configured to regularly expire cached data.
- </para>
-
- <para revision="1">
- You have the option to tell Hibernate which caching implementation to use by
- specifying the name of a class that implements <literal>org.hibernate.cache.CacheProvider</literal>
- using the property <literal>hibernate.cache.provider_class</literal>. Hibernate
- is bundled with a number of built-in integrations with the open-source cache providers
- that are listed below. You can also implement your own and plug it in as
- outlined above. Note that versions prior to 3.2 use EhCache as the default
- cache provider.
- </para>
+ <para>A Hibernate <literal>Session</literal> is a transaction-level cache
+ of persistent data. It is possible to configure a cluster or JVM-level
+ (<literal>SessionFactory</literal>-level) cache on a class-by-class and
+ collection-by-collection basis. You can even plug in a clustered cache. Be
+ aware that caches are not aware of changes made to the persistent store by
+ another application. They can, however, be configured to regularly expire
+ cached data.</para>
- <table frame="topbot" id="cacheproviders" revision="1">
- <title>Cache Providers</title>
- <tgroup cols='5' align='left' colsep='1' rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="3*"/>
- <colspec colname='c3' colwidth="1*"/>
- <colspec colname='c4' colwidth="1*"/>
- <colspec colname='c5' colwidth="1*"/>
- <thead>
+ <para revision="1">You have the option to tell Hibernate which caching
+ implementation to use by specifying the name of a class that implements
+ <literal>org.hibernate.cache.CacheProvider</literal> using the property
+ <literal>hibernate.cache.provider_class</literal>. Hibernate is bundled
+ with a number of built-in integrations with the open-source cache
+ providers that are listed in <xref linkend="cacheproviders" />. You can
+ also implement your own and plug it in as outlined above. Note that
+ versions prior to Hibernate 3.2 use EhCache as the default cache
+ provider.</para>
+
+ <table frame="topbot" id="cacheproviders" revision="1">
+ <title>Cache Providers</title>
+
+ <tgroup align="left" cols="5" colsep="1" rowsep="1">
+ <colspec colname="c1" colwidth="1*" />
+
+ <colspec colname="c2" colwidth="3*" />
+
+ <colspec colname="c3" colwidth="1*" />
+
+ <colspec colname="c4" colwidth="1*" />
+
+ <colspec colname="c5" colwidth="1*" />
+
+ <thead>
+ <row>
+ <entry>Cache</entry>
+
+ <entry>Provider class</entry>
+
+ <entry>Type</entry>
+
+ <entry>Cluster Safe</entry>
+
+ <entry>Query Cache Supported</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>Hashtable (not intended for production use)</entry>
+
+ <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
+
+ <entry>memory</entry>
+
+ <entry></entry>
+
+ <entry>yes</entry>
+ </row>
+
+ <row>
+ <entry>EHCache</entry>
+
+ <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
+
+ <entry>memory, disk</entry>
+
+ <entry></entry>
+
+ <entry>yes</entry>
+ </row>
+
+ <row>
+ <entry>OSCache</entry>
+
+ <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
+
+ <entry>memory, disk</entry>
+
+ <entry></entry>
+
+ <entry>yes</entry>
+ </row>
+
+ <row>
+ <entry>SwarmCache</entry>
+
+ <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
+
+ <entry>clustered (ip multicast)</entry>
+
+ <entry>yes (clustered invalidation)</entry>
+
+ <entry></entry>
+ </row>
+
+ <row>
+ <entry>JBoss Cache 1.x</entry>
+
+ <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
+
+ <entry>clustered (ip multicast), transactional</entry>
+
+ <entry>yes (replication)</entry>
+
+ <entry>yes (clock sync req.)</entry>
+ </row>
+
+ <row>
+ <entry>JBoss Cache 2</entry>
+
+ <entry><literal>org.hibernate.cache.jbc.JBossCacheRegionFactory</literal></entry>
+
+ <entry>clustered (ip multicast), transactional</entry>
+
+ <entry>yes (replication or invalidation)</entry>
+
+ <entry>yes (clock sync req.)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <section id="performance-cache-mapping" revision="2">
+ <title>Cache mappings</title>
+
+ <para>As we have done in previous chapters we are looking at the two
+ different possibiltites to configure caching. First configuration via
+ annotations and then via Hibernate mapping files.</para>
+
+ <para>By default, entities are not part of the second level cache and we
+ recommend you to stick to this setting. However, you can override this
+ by setting the <literal>shared-cache-mode</literal> element in your
+ <filename>persistence.xml</filename> file or by using the
+ <literal>javax.persistence.sharedCache.mode </literal>property in your
+ configuration. The following values are possible:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>ENABLE_SELECTIVE</literal> (Default and recommended
+ value): entities are not cached unless explicitly marked as
+ cacheable.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>DISABLE_SELECTIVE</literal>: entities are cached
+ unless explicitly marked as not cacheable.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>ALL</literal>: all entities are always cached even if
+ marked as non cacheable.</para>
+ </listitem>
+
+ <listitem>
+ <para><literal>NONE</literal>: no entity are cached even if marked
+ as cacheable. This option can make sense to disable second-level
+ cache altogether.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>The cache concurrency strategy used by default can be set globaly
+ via the
+ <literal>hibernate.cache.default_cache_concurrency_strategy</literal>
+ configuration property. The values for this property are:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para><literal>read-only</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><literal>read-write</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><literal>nonstrict-read-write</literal></para>
+ </listitem>
+
+ <listitem>
+ <para><literal>transactional</literal></para>
+ </listitem>
+ </itemizedlist>
+
+ <note>
+ <para>It is recommended to define the cache concurrency strategy per
+ entity rather than using a global one. Use the
+ <classname>@org.hibernate.annotations.Cache</classname> annotation for
+ that.</para>
+ </note>
+
+ <example id="example-cache-concurrency-with-cache-annotation">
+ <title>Definition of cache concurrency strategy via
+ <classname>@Cache</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+@Cacheable
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public class Forest { ... }</programlisting>
+ </example>
+
+ <para>Hibernate also let's you cache the content of a collection or the
+ identifiers if the collection contains other entities. Use the
+ <classname>@Cache</classname> annotation on the collection
+ property.</para>
+
+ <example>
+ <title>Caching collections using annotations</title>
+
+ <programlisting language="JAVA" role="JAVA">@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
+@JoinColumn(name="CUST_ID")
+@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
+public SortedSet<Ticket> getTickets() {
+ return tickets;
+}</programlisting>
+ </example>
+
+ <para><xref linkend="example-cache-annotation-with-attributes" />shows
+ the<literal> @org.hibernate.annotations.Cache</literal> annotations with
+ its attributes. It allows you to define the caching strategy and region
+ of a given second level cache.</para>
+
+ <example id="example-cache-annotation-with-attributes">
+ <title><classname>@Cache</classname> annotation with
+ attributes</title>
+
+ <programlistingco>
+ <areaspec>
+ <area coords="2" id="cache-hm1" />
+
+ <area coords="3" id="cache-hm2" />
+
+ <area coords="4" id="cache-hm3" />
+ </areaspec>
+
+ <programlisting>@Cache(
+ CacheConcurrencyStrategy usage();
+ String region() default "";
+ String include() default "all";
+)</programlisting>
+
+ <calloutlist>
+ <callout arearefs="cache-hm1">
+ <para>usage: the given cache concurrency strategy (NONE,
+ READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE,
+ TRANSACTIONAL)</para>
+ </callout>
+
+ <callout arearefs="cache-hm2">
+ <para>region (optional): the cache region (default to the fqcn
+ of the class or the fq role name of the collection)</para>
+ </callout>
+
+ <callout arearefs="cache-hm3">
+ <para><literal>include</literal> (optional): all to include all
+ properties, non-lazy to only include non lazy properties
+ (default all).</para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </example>
+
+ <para>Let's now take a look at Hibernate mapping files. There the
+ <literal><cache></literal> element of a class or collection
+ mapping is used to configure the second level cache. Looking at <xref
+ linkend="example-hibernate-cache-mapping-element" /> the parallels to
+ anotations is obvious.</para>
+
+ <example id="example-hibernate-cache-mapping-element">
+ <title>The Hibernate <literal><cache></literal> mapping
+ element</title>
+
+ <programlistingco>
+ <areaspec>
+ <area coords="2" id="cache1" />
+
+ <area coords="3" id="cache2" />
+
+ <area coords="4" id="cache3" />
+ </areaspec>
+
+ <programlisting><cache
+ usage="transactional|read-write|nonstrict-read-write|read-only"
+ region="RegionName"
+ include="all|non-lazy"
+/></programlisting>
+
+ <calloutlist>
+ <callout arearefs="cache1">
+ <para><literal>usage</literal> (required) specifies the caching
+ strategy: <literal>transactional</literal>,
+ <literal>read-write</literal>,
+ <literal>nonstrict-read-write</literal> or
+ <literal>read-only</literal></para>
+ </callout>
+
+ <callout arearefs="cache2">
+ <para><literal>region</literal> (optional: defaults to the class
+ or collection role name): specifies the name of the second level
+ cache region</para>
+ </callout>
+
+ <callout arearefs="cache3">
+ <para><literal>include</literal> (optional: defaults to
+ <literal>all</literal>) <literal>non-lazy</literal>: specifies
+ that properties of the entity mapped with
+ <literal>lazy="true"</literal> cannot be cached when
+ attribute-level lazy fetching is enabled</para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </example>
+
+ <para>Alternatively to <literal><cache></literal>, you can use
+ <literal><class-cache></literal> and
+ <literal><collection-cache></literal> elements in
+ <literal>hibernate.cfg.xml</literal>.</para>
+
+ <para>Let's now have a closer look at the different usage
+ strategies</para>
+ </section>
+
+ <section id="performance-cache-readonly">
+ <title>Strategy: read only</title>
+
+ <para>If your application needs to read, but not modify, instances of a
+ persistent class, a <literal>read-only</literal> cache can be used. This
+ is the simplest and optimal performing strategy. It is even safe for use
+ in a cluster.</para>
+ </section>
+
+ <section id="performance-cache-readwrite">
+ <title>Strategy: read/write</title>
+
+ <para>If the application needs to update data, a
+ <literal>read-write</literal> cache might be appropriate. This cache
+ strategy should never be used if serializable transaction isolation
+ level is required. If the cache is used in a JTA environment, you must
+ specify the property
+ <literal>hibernate.transaction.manager_lookup_class</literal> and naming
+ a strategy for obtaining the JTA <literal>TransactionManager</literal>.
+ In other environments, you should ensure that the transaction is
+ completed when <literal>Session.close()</literal> or
+ <literal>Session.disconnect()</literal> is called. If you want to use
+ this strategy in a cluster, you should ensure that the underlying cache
+ implementation supports locking. The built-in cache providers
+ <emphasis>do not</emphasis> support locking.</para>
+ </section>
+
+ <section id="performance-cache-nonstrict">
+ <title>Strategy: nonstrict read/write</title>
+
+ <para>If the application only occasionally needs to update data (i.e. if
+ it is extremely unlikely that two transactions would try to update the
+ same item simultaneously), and strict transaction isolation is not
+ required, a <literal>nonstrict-read-write</literal> cache might be
+ appropriate. If the cache is used in a JTA environment, you must specify
+ <literal>hibernate.transaction.manager_lookup_class</literal>. In other
+ environments, you should ensure that the transaction is completed when
+ <literal>Session.close()</literal> or
+ <literal>Session.disconnect()</literal> is called.</para>
+ </section>
+
+ <section id="performance-cache-transactional">
+ <title>Strategy: transactional</title>
+
+ <para>The <literal>transactional</literal> cache strategy provides
+ support for fully transactional cache providers such as JBoss TreeCache.
+ Such a cache can only be used in a JTA environment and you must specify
+ <literal>hibernate.transaction.manager_lookup_class</literal>.</para>
+ </section>
+
+ <section id="performance-cache-compat-matrix">
+ <title>Cache-provider/concurrency-strategy compatibility</title>
+
+ <important>
+ <para>None of the cache providers support all of the cache concurrency
+ strategies.</para>
+ </important>
+
+ <para>The following table shows which providers are compatible with
+ which concurrency strategies.</para>
+
+ <table frame="topbot">
+ <title>Cache Concurrency Strategy Support</title>
+
+ <tgroup align="left" cols="5" colsep="1" rowsep="1">
+ <colspec colname="c1" colwidth="1*" />
+
+ <colspec colname="c2" colwidth="1*" />
+
+ <colspec colname="c3" colwidth="1*" />
+
+ <colspec colname="c4" colwidth="1*" />
+
+ <colspec colname="c5" colwidth="1*" />
+
+ <thead>
<row>
<entry>Cache</entry>
- <entry>Provider class</entry>
- <entry>Type</entry>
- <entry>Cluster Safe</entry>
- <entry>Query Cache Supported</entry>
+
+ <entry>read-only</entry>
+
+ <entry>nonstrict-read-write</entry>
+
+ <entry>read-write</entry>
+
+ <entry>transactional</entry>
</row>
- </thead>
- <tbody>
+ </thead>
+
+ <tbody>
<row>
- <entry>Hashtable (not intended for production use)</entry>
- <entry><literal>org.hibernate.cache.HashtableCacheProvider</literal></entry>
- <entry>memory</entry>
- <entry> </entry>
- <entry>yes</entry>
+ <entry>Hashtable (not intended for production use)</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry></entry>
</row>
+
<row>
- <entry>EHCache</entry>
- <entry><literal>org.hibernate.cache.EhCacheProvider</literal></entry>
- <entry>memory, disk</entry>
- <entry> </entry>
- <entry>yes</entry>
+ <entry>EHCache</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry></entry>
</row>
+
<row>
- <entry>OSCache</entry>
- <entry><literal>org.hibernate.cache.OSCacheProvider</literal></entry>
- <entry>memory, disk</entry>
- <entry> </entry>
- <entry>yes</entry>
+ <entry>OSCache</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry></entry>
</row>
+
<row>
- <entry>SwarmCache</entry>
- <entry><literal>org.hibernate.cache.SwarmCacheProvider</literal></entry>
- <entry>clustered (ip multicast)</entry>
- <entry>yes (clustered invalidation)</entry>
- <entry> </entry>
+ <entry>SwarmCache</entry>
+
+ <entry>yes</entry>
+
+ <entry>yes</entry>
+
+ <entry></entry>
+
+ <entry></entry>
</row>
+
<row>
- <entry>JBoss Cache 1.x</entry>
- <entry><literal>org.hibernate.cache.TreeCacheProvider</literal></entry>
- <entry>clustered (ip multicast), transactional</entry>
- <entry>yes (replication)</entry>
- <entry>yes (clock sync req.)</entry>
+ <entry>JBoss Cache 1.x</entry>
+
+ <entry>yes</entry>
+
+ <entry></entry>
+
+ <entry></entry>
+
+ <entry>yes</entry>
</row>
+
<row>
- <entry>JBoss Cache 2</entry>
- <entry><literal>org.hibernate.cache.jbc.JBossCacheRegionFactory</literal></entry>
- <entry>clustered (ip multicast), transactional</entry>
- <entry>yes (replication or invalidation)</entry>
- <entry>yes (clock sync req.)</entry>
+ <entry>JBoss Cache 2</entry>
+
+ <entry>yes</entry>
+
+ <entry></entry>
+
+ <entry></entry>
+
+ <entry>yes</entry>
</row>
- </tbody>
- </tgroup>
- </table>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
+ </section>
- <section id="performance-cache-mapping" revision="2">
- <title>Cache mappings</title>
+ <section id="performance-sessioncache" revision="2">
+ <title>Managing the caches</title>
- <para>
- The <literal><cache></literal> element of a class or collection mapping has the
- following form:
- </para>
+ <para>Whenever you pass an object to <literal>save()</literal>,
+ <literal>update()</literal> or <literal>saveOrUpdate()</literal>, and
+ whenever you retrieve an object using <literal>load()</literal>,
+ <literal>get()</literal>, <literal>list()</literal>,
+ <literal>iterate()</literal> or <literal>scroll()</literal>, that object
+ is added to the internal cache of the <literal>Session</literal>.</para>
- <programlistingco>
- <areaspec>
- <area id="cache1" coords="2"/>
- <area id="cache2" coords="3"/>
- <area id="cache3" coords="4"/>
- </areaspec>
- <programlisting><![CDATA[<cache
- usage="transactional|read-write|nonstrict-read-write|read-only"
- region="RegionName"
- include="all|non-lazy"
-/>]]></programlisting>
- <calloutlist>
- <callout arearefs="cache1">
- <para>
- <literal>usage</literal> (required) specifies the caching strategy:
- <literal>transactional</literal>,
- <literal>read-write</literal>,
- <literal>nonstrict-read-write</literal> or
- <literal>read-only</literal>
- </para>
- </callout>
- <callout arearefs="cache2">
- <para>
- <literal>region</literal> (optional: defaults to the class or
- collection role name): specifies the name of the second level cache
- region
- </para>
- </callout>
- <callout arearefs="cache3">
- <para>
- <literal>include</literal> (optional: defaults to <literal>all</literal>)
- <literal>non-lazy</literal>: specifies that properties of the entity mapped
- with <literal>lazy="true"</literal> cannot be cached when attribute-level
- lazy fetching is enabled
- </para>
- </callout>
- </calloutlist>
- </programlistingco>
-
- <para>
- Alternatively, you can specify <literal><class-cache></literal> and
- <literal><collection-cache></literal> elements in <literal>hibernate.cfg.xml</literal>.
- </para>
-
- <para>
- The <literal>usage</literal> attribute specifies a <emphasis>cache concurrency strategy</emphasis>.
- </para>
+ <para>When <literal>flush()</literal> is subsequently called, the state of
+ that object will be synchronized with the database. If you do not want
+ this synchronization to occur, or if you are processing a huge number of
+ objects and need to manage memory efficiently, the
+ <literal>evict()</literal> method can be used to remove the object and its
+ collections from the first-level cache.</para>
- </section>
+ <example>
+ <title>Explcitly evicting a cached instance from the first level cache
+ using <methodname>Session.evict()</methodname></title>
- <section id="performance-cache-readonly">
- <title>Strategy: read only</title>
+ <programlisting role="JAVA">ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
+while ( cats.next() ) {
+ Cat cat = (Cat) cats.get(0);
+ doSomethingWithACat(cat);
+ sess.evict(cat);
+}</programlisting>
+ </example>
- <para>
- If your application needs to read, but not modify, instances of a persistent class, a
- <literal>read-only</literal> cache can be used. This is the simplest and optimal performing
- strategy. It is even safe for use in a cluster.
- </para>
+ <para>The <literal>Session</literal> also provides a
+ <literal>contains()</literal> method to determine if an instance belongs
+ to the session cache.</para>
- <programlisting role="XML"><![CDATA[<class name="eg.Immutable" mutable="false">
- <cache usage="read-only"/>
- ....
-</class>]]></programlisting>
+ <para>To evict all objects from the session cache, call
+ <literal>Session.clear()</literal></para>
- </section>
+ <para>For the second-level cache, there are methods defined on
+ <literal>SessionFactory</literal> for evicting the cached state of an
+ instance, entire class, collection instance or entire collection
+ role.</para>
+ <example>
+ <title>Second-level cache eviction via
+ <methodname>SessionFactoty.evict() </methodname>and
+ <methodname>SessionFacyory.evictCollection()</methodname></title>
- <section id="performance-cache-readwrite">
- <title>Strategy: read/write</title>
+ <programlisting role="JAVA">sessionFactory.evict(Cat.class, catId); //evict a particular Cat
+sessionFactory.evict(Cat.class); //evict all Cats
+sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
+sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections</programlisting>
+ </example>
- <para>
- If the application needs to update data, a <literal>read-write</literal> cache might be appropriate.
- This cache strategy should never be used if serializable transaction isolation level is required.
- If the cache is used in a JTA environment, you must specify the property
- <literal>hibernate.transaction.manager_lookup_class</literal> and naming a strategy for obtaining the
- JTA <literal>TransactionManager</literal>. In other environments, you should ensure that the transaction
- is completed when <literal>Session.close()</literal> or <literal>Session.disconnect()</literal> is called.
- If you want to use this strategy in a cluster, you should ensure that the underlying cache implementation
- supports locking. The built-in cache providers <emphasis>do not</emphasis> support locking.
- </para>
+ <para>The <literal>CacheMode</literal> controls how a particular session
+ interacts with the second-level cache:</para>
- <programlisting role="XML"><![CDATA[<class name="eg.Cat" .... >
- <cache usage="read-write"/>
- ....
- <set name="kittens" ... >
- <cache usage="read-write"/>
- ....
- </set>
-</class>]]></programlisting>
+ <itemizedlist>
+ <listitem>
+ <para><literal>CacheMode.NORMAL</literal>: will read items from and
+ write items to the second-level cache</para>
+ </listitem>
- </section>
+ <listitem>
+ <para><literal>CacheMode.GET</literal>: will read items from the
+ second-level cache. Do not write to the second-level cache except when
+ updating data</para>
+ </listitem>
- <section id="performance-cache-nonstrict">
- <title>Strategy: nonstrict read/write</title>
+ <listitem>
+ <para><literal>CacheMode.PUT</literal>: will write items to the
+ second-level cache. Do not read from the second-level cache</para>
+ </listitem>
- <para>
- If the application only occasionally needs to update data (i.e. if it is extremely unlikely that two
- transactions would try to update the same item simultaneously), and strict transaction isolation is
- not required, a <literal>nonstrict-read-write</literal> cache might be appropriate. If the cache is
- used in a JTA environment, you must specify <literal>hibernate.transaction.manager_lookup_class</literal>.
- In other environments, you should ensure that the transaction is completed when
- <literal>Session.close()</literal> or <literal>Session.disconnect()</literal> is called.
- </para>
+ <listitem>
+ <para><literal>CacheMode.REFRESH</literal>: will write items to the
+ second-level cache. Do not read from the second-level cache. Bypass
+ the effect of <literal>hibernate.cache.use_minimal_puts</literal>
+ forcing a refresh of the second-level cache for all items read from
+ the database</para>
+ </listitem>
+ </itemizedlist>
- </section>
+ <para>To browse the contents of a second-level or query cache region, use
+ the <literal>Statistics</literal> API:</para>
- <section id="performance-cache-transactional">
- <title>Strategy: transactional</title>
+ <example>
+ <title>Browsing the second-level cache entries via the
+ <classname>Statistics</classname> API</title>
- <para>
- The <literal>transactional</literal> cache strategy provides support for fully transactional cache
- providers such as JBoss TreeCache. Such a cache can only be used in a JTA environment and you must
- specify <literal>hibernate.transaction.manager_lookup_class</literal>.
- </para>
+ <programlisting role="JAVA">Map cacheEntries = sessionFactory.getStatistics()
+ .getSecondLevelCacheStatistics(regionName)
+ .getEntries();</programlisting>
+ </example>
- </section>
+ <para>You will need to enable statistics and, optionally, force Hibernate
+ to keep the cache entries in a more readable format:</para>
- <section id="performance-cache-compat-matrix">
- <title>Cache-provider/concurrency-strategy compatibility</title>
+ <example>
+ <title>Enabling Hibernate statistics</title>
- <important>
- <para>
- None of the cache providers support all of the cache concurrency strategies.
- </para>
- </important>
+ <programlisting>hibernate.generate_statistics true
+hibernate.cache.use_structured_entries true</programlisting>
+ </example>
+ </section>
- <para>
- The following table shows which providers are compatible with which concurrency strategies.
- </para>
+ <section id="performance-querycache" revision="1">
+ <title>The Query Cache</title>
- <table frame="topbot">
- <title>Cache Concurrency Strategy Support</title>
- <tgroup cols='5' align='left' colsep='1' rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <colspec colname='c3' colwidth="1*"/>
- <colspec colname='c4' colwidth="1*"/>
- <colspec colname='c5' colwidth="1*"/>
- <thead>
- <row>
- <entry>Cache</entry>
- <entry>read-only</entry>
- <entry>nonstrict-read-write</entry>
- <entry>read-write</entry>
- <entry>transactional</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>Hashtable (not intended for production use)</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry> </entry>
- </row>
- <row>
- <entry>EHCache</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry> </entry>
- </row>
- <row>
- <entry>OSCache</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry> </entry>
- </row>
- <row>
- <entry>SwarmCache</entry>
- <entry>yes</entry>
- <entry>yes</entry>
- <entry> </entry>
- <entry> </entry>
- </row>
- <row>
- <entry>JBoss Cache 1.x</entry>
- <entry>yes</entry>
- <entry> </entry>
- <entry> </entry>
- <entry>yes</entry>
- </row>
- <row>
- <entry>JBoss Cache 2</entry>
- <entry>yes</entry>
- <entry> </entry>
- <entry> </entry>
- <entry>yes</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
+ <para>Query result sets can also be cached. This is only useful for
+ queries that are run frequently with the same parameters.</para>
- </section>
+ <section id="performance-querycache-enable">
+ <title>Enabling query caching</title>
- </section>
+ <para>Caching of query results introduces some overhead in terms of your
+ applications normal transactional processing. For example, if you cache
+ results of a query against Person Hibernate will need to keep track of
+ when those results should be invalidated because changes have been
+ committed against Person. That, coupled with the fact that most
+ applications simply gain no benefit from caching query results, leads
+ Hibernate to disable caching of query results by default. To use query
+ caching, you will first need to enable the query cache:</para>
- <section id="performance-sessioncache" revision="2">
- <title>Managing the caches</title>
+ <programlisting>hibernate.cache.use_query_cache true</programlisting>
- <para>
- Whenever you pass an object to <literal>save()</literal>, <literal>update()</literal>
- or <literal>saveOrUpdate()</literal>, and whenever you retrieve an object using
- <literal>load()</literal>, <literal>get()</literal>, <literal>list()</literal>,
- <literal>iterate()</literal> or <literal>scroll()</literal>, that object is added
- to the internal cache of the <literal>Session</literal>.
- </para>
- <para>
- When <literal>flush()</literal> is subsequently called, the state of that object will
- be synchronized with the database. If you do not want this synchronization to occur, or
- if you are processing a huge number of objects and need to manage memory efficiently,
- the <literal>evict()</literal> method can be used to remove the object and its collections
- from the first-level cache.
- </para>
-
- <programlisting role="JAVA"><![CDATA[ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result set
-while ( cats.next() ) {
- Cat cat = (Cat) cats.get(0);
- doSomethingWithACat(cat);
- sess.evict(cat);
-}]]></programlisting>
-
- <para>
- The <literal>Session</literal> also provides a <literal>contains()</literal> method to determine
- if an instance belongs to the session cache.
- </para>
-
- <para>
- To evict all objects from the session cache, call <literal>Session.clear()</literal>
- </para>
-
- <para>
- For the second-level cache, there are methods defined on <literal>SessionFactory</literal> for
- evicting the cached state of an instance, entire class, collection instance or entire collection
- role.
- </para>
-
- <programlisting role="JAVA"><![CDATA[sessionFactory.evict(Cat.class, catId); //evict a particular Cat
-sessionFactory.evict(Cat.class); //evict all Cats
-sessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittens
-sessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections]]></programlisting>
+ <para>This setting creates two new cache regions: <itemizedlist>
+ <listitem>
+ <para><classname>org.hibernate.cache.StandardQueryCache</classname>,
+ holding the cached query results</para>
+ </listitem>
- <para>
- The <literal>CacheMode</literal> controls how a particular session interacts with the second-level
- cache:
- </para>
-
- <itemizedlist>
- <listitem>
- <para>
- <literal>CacheMode.NORMAL</literal>: will read items from and write items to the second-level cache
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>CacheMode.GET</literal>: will read items from the second-level cache. Do not write to
- the second-level cache except when updating data
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>CacheMode.PUT</literal>: will write items to the second-level cache. Do not read from
- the second-level cache
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>CacheMode.REFRESH</literal>: will write items to the second-level cache. Do not read from
- the second-level cache. Bypass the effect of <literal>hibernate.cache.use_minimal_puts</literal> forcing
- a refresh of the second-level cache for all items read from the database
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- To browse the contents of a second-level or query cache region, use the <literal>Statistics</literal>
- API:
- </para>
-
- <programlisting role="JAVA"><![CDATA[Map cacheEntries = sessionFactory.getStatistics()
- .getSecondLevelCacheStatistics(regionName)
- .getEntries();]]></programlisting>
-
- <para>
- You will need to enable statistics and, optionally, force Hibernate to keep the cache entries in a
- more readable format:
- </para>
-
- <programlisting><![CDATA[hibernate.generate_statistics true
-hibernate.cache.use_structured_entries true]]></programlisting>
-
+ <listitem>
+ <para><classname>org.hibernate.cache.UpdateTimestampsCache</classname>,
+ holding timestamps of the most recent updates to queryable tables.
+ These are used to validate the results as they are served from the
+ query cache.</para>
+ </listitem>
+ </itemizedlist></para>
+
+ <important>
+ <para>If you configure your underlying cache implementation to use
+ expiry or timeouts is very important that the cache timeout of the
+ underlying cache region for the UpdateTimestampsCache be set to a
+ higher value than the timeouts of any of the query caches. In fact, we
+ recommend that the the UpdateTimestampsCache region not be configured
+ for expiry at all. Note, in particular, that an LRU cache expiry
+ policy is never appropriate.</para>
+ </important>
+
+ <para>As mentioned above, most queries do not benefit from caching or
+ their results. So by default, individual queries are not cached even
+ after enabling query caching. To enable results caching for a particular
+ query, call <literal>org.hibernate.Query.setCacheable(true)</literal>.
+ This call allows the query to look for existing cache results or add its
+ results to the cache when it is executed.</para>
+
+ <note>
+ <para>The query cache does not cache the state of the actual entities
+ in the cache; it caches only identifier values and results of value
+ type. For this reaso, the query cache should always be used in
+ conjunction with the second-level cache for those entities expected to
+ be cached as part of a query result cache (just as with collection
+ caching).</para>
+ </note>
</section>
- <section id="performance-querycache" revision="1">
- <title>The Query Cache</title>
+ <section id="performance-querycache-regions">
+ <title>Query cache regions</title>
- <para>
- Query result sets can also be cached. This is only useful for queries that are run
- frequently with the same parameters.
- </para>
+ <para>If you require fine-grained control over query cache expiration
+ policies, you can specify a named cache region for a particular query by
+ calling <literal>Query.setCacheRegion()</literal>.</para>
- <section id="performance-querycache-enable">
- <title>Enabling query caching</title>
- <para>
- Caching of query results introduces some overhead in terms of your applications normal
- transactional processing. For example, if you cache results of a query against Person
- Hibernate will need to keep track of when those results should be invalidated because
- changes have been committed against Person. That, coupled with the fact that most
- applications simply gain no benefit from caching query results, leads Hibernate to
- disable caching of query results by default. To use query caching, you will first
- need to enable the query cache:
- </para>
- <programlisting><![CDATA[hibernate.cache.use_query_cache true]]></programlisting>
- <para>
- This setting creates two new cache regions:
- <itemizedlist>
- <listitem>
- <para>
- <classname>org.hibernate.cache.StandardQueryCache</classname>, holding
- the cached query results
- </para>
- </listitem>
- <listitem>
- <para>
- <classname>org.hibernate.cache.UpdateTimestampsCache</classname>, holding
- timestamps of the most recent updates to queryable tables. These are used
- to validate the results as they are served from the query cache.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <important>
- <para>
- If you configure your underlying cache implementation to use expiry or
- timeouts is very important that the cache timeout of the underlying
- cache region for the UpdateTimestampsCache be set to a higher value than
- the timeouts of any of the query caches. In fact, we recommend that the
- the UpdateTimestampsCache region not be configured for expiry at all. Note,
- in particular, that an LRU cache expiry policy is never appropriate.
- </para>
- </important>
- <para>
- As mentioned above, most queries do not benefit from caching or their results. So by
- default, individual queries are not cached even after enabling query caching. To enable
- results caching for a particular query, call
- <literal>org.hibernate.Query.setCacheable(true)</literal>. This call allows the query
- to look for existing cache results or add its results to the cache when it is executed.
- </para>
- <note>
- <para>
- The query cache does not cache the state of the actual entities in the cache; it
- caches only identifier values and results of value type. For this reaso, the query
- cache should always be used in conjunction with the second-level cache for those
- entities expected to be cached as part of a query result cache (just as with
- collection caching).
- </para>
- </note>
- </section>
-
- <section id="performance-querycache-regions">
- <title>Query cache regions</title>
- <para>
- If you require fine-grained control over query cache expiration policies, you can
- specify a named cache region for a particular query by calling
- <literal>Query.setCacheRegion()</literal>.
- </para>
- <programlisting role="JAVA"><![CDATA[List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
+ <programlisting role="JAVA">List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger")
.setEntity("blogger", blogger)
.setMaxResults(15)
.setCacheable(true)
.setCacheRegion("frontpages")
- .list();]]></programlisting>
+ .list();</programlisting>
- <para>
- If you want to force the query cache to refresh one of its regions (disregard any
- cached results it finds there) you can use
- <literal>org.hibernate.Query.setCacheMode(CacheMode.REFRESH)</literal>. In conjunction
- with the region you have defined for the given query, Hibernate will selectively force
- the results cached in that particular region to be refreshed. This is particularly useful
- in cases where underlying data may have been updated via a separate process and is a far more
- efficient alternative to bulk eviction of the region via
- <literal>org.hibernate.SessionFactory.evictQueries()</literal>.
- </para>
- </section>
+ <para>If you want to force the query cache to refresh one of its regions
+ (disregard any cached results it finds there) you can use
+ <literal>org.hibernate.Query.setCacheMode(CacheMode.REFRESH)</literal>.
+ In conjunction with the region you have defined for the given query,
+ Hibernate will selectively force the results cached in that particular
+ region to be refreshed. This is particularly useful in cases where
+ underlying data may have been updated via a separate process and is a
+ far more efficient alternative to bulk eviction of the region via
+ <literal>org.hibernate.SessionFactory.evictQueries()</literal>.</para>
</section>
+ </section>
- <section id="performance-collections">
- <title>Understanding Collection performance</title>
+ <section id="performance-collections">
+ <title>Understanding Collection performance</title>
- <para>
- In the previous sections we have covered collections and their applications.
- In this section we explore some more issues in relation to
- collections at runtime.
- </para>
+ <para>In the previous sections we have covered collections and their
+ applications. In this section we explore some more issues in relation to
+ collections at runtime.</para>
- <section id="performance-collections-taxonomy">
- <title>Taxonomy</title>
+ <section id="performance-collections-taxonomy">
+ <title>Taxonomy</title>
- <para>Hibernate defines three basic kinds of collections:</para>
+ <para>Hibernate defines three basic kinds of collections:</para>
- <itemizedlist>
- <listitem>
- <para>collections of values</para>
- </listitem>
- <listitem>
- <para>one-to-many associations</para>
- </listitem>
- <listitem>
- <para>many-to-many associations</para>
- </listitem>
- </itemizedlist>
+ <itemizedlist>
+ <listitem>
+ <para>collections of values</para>
+ </listitem>
- <para>
- This classification distinguishes the various table and foreign key
- relationships but does not tell us quite everything we need to know
- about the relational model. To fully understand the relational structure
- and performance characteristics, we must also consider the structure of
- the primary key that is used by Hibernate to update or delete collection
- rows. This suggests the following classification:
- </para>
+ <listitem>
+ <para>one-to-many associations</para>
+ </listitem>
- <itemizedlist>
- <listitem>
- <para>indexed collections</para>
- </listitem>
- <listitem>
- <para>sets</para>
- </listitem>
- <listitem>
- <para>bags</para>
- </listitem>
- </itemizedlist>
+ <listitem>
+ <para>many-to-many associations</para>
+ </listitem>
+ </itemizedlist>
- <para>
- All indexed collections (maps, lists, and arrays) have a primary key consisting
- of the <literal><key></literal> and <literal><index></literal>
- columns. In this case, collection updates are extremely efficient.
- The primary key can be efficiently indexed and a particular row can be efficiently
- located when Hibernate tries to update or delete it.
- </para>
-
- <para>
- Sets have a primary key consisting of <literal><key></literal> and element
- columns. This can be less efficient for some types of collection element, particularly
- composite elements or large text or binary fields, as the database may not be able to index
- a complex primary key as efficiently. However, for one-to-many or many-to-many
- associations, particularly in the case of synthetic identifiers, it is likely to be just
- as efficient. If you want <literal>SchemaExport</literal> to actually create
- the primary key of a <literal><set></literal>, you must declare all columns
- as <literal>not-null="true"</literal>.
- </para>
+ <para>This classification distinguishes the various table and foreign
+ key relationships but does not tell us quite everything we need to know
+ about the relational model. To fully understand the relational structure
+ and performance characteristics, we must also consider the structure of
+ the primary key that is used by Hibernate to update or delete collection
+ rows. This suggests the following classification:</para>
- <para>
- <literal><idbag></literal> mappings define a surrogate key, so they are
- efficient to update. In fact, they are the best case.
- </para>
-
- <para>
- Bags are the worst case since they permit duplicate element values and, as they have no
- index column, no primary key can be defined. Hibernate has no way of distinguishing
- between duplicate rows. Hibernate resolves this problem by completely removing
- in a single <literal>DELETE</literal> and recreating the collection whenever it
- changes. This can be inefficient.
- </para>
+ <itemizedlist>
+ <listitem>
+ <para>indexed collections</para>
+ </listitem>
- <para>
- For a one-to-many association, the "primary key" may not be the physical
- primary key of the database table. Even in this case, the above classification
- is still useful. It reflects how Hibernate "locates" individual rows of the
- collection.
- </para>
+ <listitem>
+ <para>sets</para>
+ </listitem>
- </section>
+ <listitem>
+ <para>bags</para>
+ </listitem>
+ </itemizedlist>
- <section id="performance-collections-mostefficientupdate">
- <title>Lists, maps, idbags and sets are the most efficient collections to update</title>
+ <para>All indexed collections (maps, lists, and arrays) have a primary
+ key consisting of the <literal><key></literal> and
+ <literal><index></literal> columns. In this case, collection
+ updates are extremely efficient. The primary key can be efficiently
+ indexed and a particular row can be efficiently located when Hibernate
+ tries to update or delete it.</para>
- <para>
- From the discussion above, it should be clear that indexed collections
- and sets allow the most efficient operation in terms of adding,
- removing and updating elements.
- </para>
+ <para>Sets have a primary key consisting of
+ <literal><key></literal> and element columns. This can be less
+ efficient for some types of collection element, particularly composite
+ elements or large text or binary fields, as the database may not be able
+ to index a complex primary key as efficiently. However, for one-to-many
+ or many-to-many associations, particularly in the case of synthetic
+ identifiers, it is likely to be just as efficient. If you want
+ <literal>SchemaExport</literal> to actually create the primary key of a
+ <literal><set></literal>, you must declare all columns as
+ <literal>not-null="true"</literal>.</para>
- <para>
- There is, arguably, one more advantage that indexed collections have over sets for
- many-to-many associations or collections of values. Because of the structure of a
- <literal>Set</literal>, Hibernate does not <literal>UPDATE</literal> a row when
- an element is "changed". Changes to a <literal>Set</literal> always work via
- <literal>INSERT</literal> and <literal>DELETE</literal> of individual rows. Once
- again, this consideration does not apply to one-to-many associations.
- </para>
+ <para><literal><idbag></literal> mappings define a surrogate key,
+ so they are efficient to update. In fact, they are the best case.</para>
- <para>
- After observing that arrays cannot be lazy, you can conclude that lists, maps and
- idbags are the most performant (non-inverse) collection types, with sets not far
- behind. You can expect sets to be the most common kind of collection in Hibernate
- applications. This is because the "set" semantics are most natural in the relational
- model.
- </para>
+ <para>Bags are the worst case since they permit duplicate element values
+ and, as they have no index column, no primary key can be defined.
+ Hibernate has no way of distinguishing between duplicate rows. Hibernate
+ resolves this problem by completely removing in a single
+ <literal>DELETE</literal> and recreating the collection whenever it
+ changes. This can be inefficient.</para>
- <para>
- However, in well-designed Hibernate domain models, most collections
- are in fact one-to-many associations with <literal>inverse="true"</literal>. For these
- associations, the update is handled by the many-to-one end of the association, and so
- considerations of collection update performance simply do not apply.
- </para>
+ <para>For a one-to-many association, the "primary key" may not be the
+ physical primary key of the database table. Even in this case, the above
+ classification is still useful. It reflects how Hibernate "locates"
+ individual rows of the collection.</para>
+ </section>
- </section>
+ <section id="performance-collections-mostefficientupdate">
+ <title>Lists, maps, idbags and sets are the most efficient collections
+ to update</title>
- <section id="performance-collections-mostefficentinverse">
- <title>Bags and lists are the most efficient inverse collections</title>
+ <para>From the discussion above, it should be clear that indexed
+ collections and sets allow the most efficient operation in terms of
+ adding, removing and updating elements.</para>
- <para>
- There is a particular case, however, in which bags, and also lists,
- are much more performant than sets. For a collection with <literal>inverse="true"</literal>,
- the standard bidirectional one-to-many relationship idiom, for example, we can add elements
- to a bag or list without needing to initialize (fetch) the bag elements. This is because, unlike a <literal>set</literal>,
- <literal>Collection.add()</literal> or <literal>Collection.addAll()</literal> must always
- return true for a bag or <literal>List</literal>. This can
- make the following common code much faster:
- </para>
+ <para>There is, arguably, one more advantage that indexed collections
+ have over sets for many-to-many associations or collections of values.
+ Because of the structure of a <literal>Set</literal>, Hibernate does not
+ <literal>UPDATE</literal> a row when an element is "changed". Changes to
+ a <literal>Set</literal> always work via <literal>INSERT</literal> and
+ <literal>DELETE</literal> of individual rows. Once again, this
+ consideration does not apply to one-to-many associations.</para>
- <programlisting role="JAVA"><![CDATA[Parent p = (Parent) sess.load(Parent.class, id);
+ <para>After observing that arrays cannot be lazy, you can conclude that
+ lists, maps and idbags are the most performant (non-inverse) collection
+ types, with sets not far behind. You can expect sets to be the most
+ common kind of collection in Hibernate applications. This is because the
+ "set" semantics are most natural in the relational model.</para>
+
+ <para>However, in well-designed Hibernate domain models, most
+ collections are in fact one-to-many associations with
+ <literal>inverse="true"</literal>. For these associations, the update is
+ handled by the many-to-one end of the association, and so considerations
+ of collection update performance simply do not apply.</para>
+ </section>
+
+ <section id="performance-collections-mostefficentinverse">
+ <title>Bags and lists are the most efficient inverse collections</title>
+
+ <para>There is a particular case, however, in which bags, and also
+ lists, are much more performant than sets. For a collection with
+ <literal>inverse="true"</literal>, the standard bidirectional
+ one-to-many relationship idiom, for example, we can add elements to a
+ bag or list without needing to initialize (fetch) the bag elements. This
+ is because, unlike a <literal>set</literal>,
+ <literal>Collection.add()</literal> or
+ <literal>Collection.addAll()</literal> must always return true for a bag
+ or <literal>List</literal>. This can make the following common code much
+ faster:</para>
+
+ <programlisting role="JAVA">Parent p = (Parent) sess.load(Parent.class, id);
Child c = new Child();
c.setParent(p);
p.getChildren().add(c); //no need to fetch the collection!
-sess.flush();]]></programlisting>
+sess.flush();</programlisting>
+ </section>
- </section>
+ <section id="performance-collections-oneshotdelete">
+ <title>One shot delete</title>
- <section id="performance-collections-oneshotdelete">
- <title>One shot delete</title>
+ <para>Deleting collection elements one by one can sometimes be extremely
+ inefficient. Hibernate knows not to do that in the case of an
+ newly-empty collection (if you called <literal>list.clear()</literal>,
+ for example). In this case, Hibernate will issue a single
+ <literal>DELETE</literal>.</para>
- <para>
- Deleting collection elements one by one can sometimes be extremely inefficient. Hibernate
- knows not to do that in the case of an newly-empty collection
- (if you called <literal>list.clear()</literal>, for example). In this case, Hibernate will
- issue a single <literal>DELETE</literal>.
- </para>
+ <para>Suppose you added a single element to a collection of size twenty
+ and then remove two elements. Hibernate will issue one
+ <literal>INSERT</literal> statement and two <literal>DELETE</literal>
+ statements, unless the collection is a bag. This is certainly
+ desirable.</para>
- <para>
- Suppose you added a single element to a collection of size twenty and then remove two elements.
- Hibernate will issue one <literal>INSERT</literal> statement and two <literal>DELETE</literal>
- statements, unless the collection is a bag. This is certainly desirable.
- </para>
+ <para>However, suppose that we remove eighteen elements, leaving two and
+ then add thee new elements. There are two possible ways to
+ proceed</para>
- <para>
- However, suppose that we remove eighteen elements, leaving two and then add thee new elements.
- There are two possible ways to proceed
- </para>
+ <itemizedlist>
+ <listitem>
+ <para>delete eighteen rows one by one and then insert three
+ rows</para>
+ </listitem>
- <itemizedlist>
- <listitem>
- <para>delete eighteen rows one by one and then insert three rows</para>
- </listitem>
- <listitem>
- <para>remove the whole collection in one SQL <literal>DELETE</literal> and insert
- all five current elements one by one</para>
- </listitem>
- </itemizedlist>
+ <listitem>
+ <para>remove the whole collection in one SQL
+ <literal>DELETE</literal> and insert all five current elements one
+ by one</para>
+ </listitem>
+ </itemizedlist>
- <para>
- Hibernate cannot know that the second option is probably quicker.
- It would probably be undesirable for Hibernate to be that intuitive as such behavior might
- confuse database triggers, etc.
- </para>
+ <para>Hibernate cannot know that the second option is probably quicker.
+ It would probably be undesirable for Hibernate to be that intuitive as
+ such behavior might confuse database triggers, etc.</para>
- <para>
- Fortunately, you can force this behavior (i.e. the second strategy) at any time by discarding
- (i.e. dereferencing) the original collection and returning a newly instantiated collection with
- all the current elements.
- </para>
-
- <para>
- One-shot-delete does not apply to collections mapped <literal>inverse="true"</literal>.
- </para>
+ <para>Fortunately, you can force this behavior (i.e. the second
+ strategy) at any time by discarding (i.e. dereferencing) the original
+ collection and returning a newly instantiated collection with all the
+ current elements.</para>
- </section>
-
+ <para>One-shot-delete does not apply to collections mapped
+ <literal>inverse="true"</literal>.</para>
</section>
+ </section>
- <section id="performance-monitoring" revision="1">
- <title>Monitoring performance</title>
+ <section id="performance-monitoring" revision="1">
+ <title>Monitoring performance</title>
- <para>
- Optimization is not much use without monitoring and access to performance numbers.
- Hibernate provides a full range of figures about its internal operations.
- Statistics in Hibernate are available per <literal>SessionFactory</literal>.
- </para>
+ <para>Optimization is not much use without monitoring and access to
+ performance numbers. Hibernate provides a full range of figures about its
+ internal operations. Statistics in Hibernate are available per
+ <literal>SessionFactory</literal>.</para>
- <section id="performance-monitoring-sf" revision="2">
- <title>Monitoring a SessionFactory</title>
+ <section id="performance-monitoring-sf" revision="2">
+ <title>Monitoring a SessionFactory</title>
- <para>
- You can access <literal>SessionFactory</literal> metrics in two ways.
- Your first option is to call <literal>sessionFactory.getStatistics()</literal> and
- read or display the <literal>Statistics</literal> yourself.
- </para>
+ <para>You can access <literal>SessionFactory</literal> metrics in two
+ ways. Your first option is to call
+ <literal>sessionFactory.getStatistics()</literal> and read or display
+ the <literal>Statistics</literal> yourself.</para>
- <para>
- Hibernate can also use JMX to publish metrics if you enable the
- <literal>StatisticsService</literal> MBean. You can enable a single MBean for all your
- <literal>SessionFactory</literal> or one per factory. See the following code for
- minimalistic configuration examples:
- </para>
+ <para>Hibernate can also use JMX to publish metrics if you enable the
+ <literal>StatisticsService</literal> MBean. You can enable a single
+ MBean for all your <literal>SessionFactory</literal> or one per factory.
+ See the following code for minimalistic configuration examples:</para>
- <programlisting role="JAVA"><![CDATA[// MBean service registration for a specific SessionFactory
+ <programlisting role="JAVA">// MBean service registration for a specific SessionFactory
Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "myFinancialApp");
@@ -1436,93 +1537,86 @@
StatisticsService stats = new StatisticsService(); // MBean implementation
stats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactory
-server.registerMBean(stats, on); // Register the Mbean on the server]]></programlisting>
+server.registerMBean(stats, on); // Register the Mbean on the server</programlisting>
-
-<programlisting role="JAVA"><![CDATA[// MBean service registration for all SessionFactory's
+ <programlisting role="JAVA">// MBean service registration for all SessionFactory's
Hashtable tb = new Hashtable();
tb.put("type", "statistics");
tb.put("sessionFactory", "all");
ObjectName on = new ObjectName("hibernate", tb); // MBean object name
StatisticsService stats = new StatisticsService(); // MBean implementation
-server.registerMBean(stats, on); // Register the MBean on the server]]></programlisting>
+server.registerMBean(stats, on); // Register the MBean on the server</programlisting>
- <para>
- You can activate and deactivate the monitoring for a <literal>SessionFactory</literal>:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- at configuration time, set <literal>hibernate.generate_statistics</literal> to <literal>false</literal>
- </para>
- </listitem>
- </itemizedlist>
- <itemizedlist>
- <listitem>
- <para>
- at runtime: <literal>sf.getStatistics().setStatisticsEnabled(true)</literal>
- or <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal>
- </para>
- </listitem>
- </itemizedlist>
+ <para>You can activate and deactivate the monitoring for a
+ <literal>SessionFactory</literal>:</para>
- <para>
- Statistics can be reset programmatically using the <literal>clear()</literal> method.
- A summary can be sent to a logger (info level) using the <literal>logSummary()</literal>
- method.
- </para>
+ <itemizedlist>
+ <listitem>
+ <para>at configuration time, set
+ <literal>hibernate.generate_statistics</literal> to
+ <literal>false</literal></para>
+ </listitem>
+ </itemizedlist>
- </section>
+ <itemizedlist>
+ <listitem>
+ <para>at runtime:
+ <literal>sf.getStatistics().setStatisticsEnabled(true)</literal> or
+ <literal>hibernateStatsBean.setStatisticsEnabled(true)</literal></para>
+ </listitem>
+ </itemizedlist>
- <section id="performance-monitoring-metrics" revision="1">
- <title>Metrics</title>
+ <para>Statistics can be reset programmatically using the
+ <literal>clear()</literal> method. A summary can be sent to a logger
+ (info level) using the <literal>logSummary()</literal> method.</para>
+ </section>
- <para>
- Hibernate provides a number of metrics, from basic information to more specialized information
- that is only relevant in certain scenarios. All available counters are described in the
- <literal>Statistics</literal> interface API, in three categories:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- Metrics related to the general <literal>Session</literal> usage, such as
- number of open sessions, retrieved JDBC connections, etc.
- </para>
- </listitem>
- <listitem>
- <para>
- Metrics related to the entities, collections, queries, and caches as a
- whole (aka global metrics).
- </para>
- </listitem>
- <listitem>
- <para>
- Detailed metrics related to a particular entity, collection, query or
- cache region.
- </para>
- </listitem>
- </itemizedlist>
+ <section id="performance-monitoring-metrics" revision="1">
+ <title>Metrics</title>
- <para>
- For example, you can check the cache hit, miss, and put ratio of entities, collections
- and queries, and the average time a query needs. Be aware that the number of milliseconds
- is subject to approximation in Java. Hibernate is tied to the JVM precision and on some
- platforms this might only be accurate to 10 seconds.
- </para>
+ <para>Hibernate provides a number of metrics, from basic information to
+ more specialized information that is only relevant in certain scenarios.
+ All available counters are described in the
+ <literal>Statistics</literal> interface API, in three categories:</para>
- <para>
- Simple getters are used to access the global metrics (i.e. not tied to a particular entity,
- collection, cache region, etc.). You can access the metrics of a particular entity, collection
- or cache region through its name, and through its HQL or SQL representation for queries. Please
- refer to the <literal>Statistics</literal>, <literal>EntityStatistics</literal>,
- <literal>CollectionStatistics</literal>, <literal>SecondLevelCacheStatistics</literal>,
- and <literal>QueryStatistics</literal> API Javadoc for more information. The following
- code is a simple example:
- </para>
+ <itemizedlist>
+ <listitem>
+ <para>Metrics related to the general <literal>Session</literal>
+ usage, such as number of open sessions, retrieved JDBC connections,
+ etc.</para>
+ </listitem>
- <programlisting role="JAVA"><![CDATA[Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+ <listitem>
+ <para>Metrics related to the entities, collections, queries, and
+ caches as a whole (aka global metrics).</para>
+ </listitem>
+ <listitem>
+ <para>Detailed metrics related to a particular entity, collection,
+ query or cache region.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>For example, you can check the cache hit, miss, and put ratio of
+ entities, collections and queries, and the average time a query needs.
+ Be aware that the number of milliseconds is subject to approximation in
+ Java. Hibernate is tied to the JVM precision and on some platforms this
+ might only be accurate to 10 seconds.</para>
+
+ <para>Simple getters are used to access the global metrics (i.e. not
+ tied to a particular entity, collection, cache region, etc.). You can
+ access the metrics of a particular entity, collection or cache region
+ through its name, and through its HQL or SQL representation for queries.
+ Please refer to the <literal>Statistics</literal>,
+ <literal>EntityStatistics</literal>,
+ <literal>CollectionStatistics</literal>,
+ <literal>SecondLevelCacheStatistics</literal>, and
+ <literal>QueryStatistics</literal> API Javadoc for more information. The
+ following code is a simple example:</para>
+
+ <programlisting role="JAVA">Statistics stats = HibernateUtil.sessionFactory.getStatistics();
+
double queryCacheHitCount = stats.getQueryCacheHitCount();
double queryCacheMissCount = stats.getQueryCacheMissCount();
double queryCacheHitRatio =
@@ -1536,18 +1630,14 @@
entityStats.getInsertCount()
+ entityStats.getUpdateCount()
+ entityStats.getDeleteCount();
-log.info(Cat.class.getName() + " changed " + changes + "times" );]]></programlisting>
+log.info(Cat.class.getName() + " changed " + changes + "times" );</programlisting>
- <para>
- You can work on all entities, collections, queries and region caches, by retrieving
- the list of names of entities, collections, queries and region caches using the
- following methods: <literal>getQueries()</literal>, <literal>getEntityNames()</literal>,
- <literal>getCollectionRoleNames()</literal>, and
- <literal>getSecondLevelCacheRegionNames()</literal>.
- </para>
-
- </section>
-
+ <para>You can work on all entities, collections, queries and region
+ caches, by retrieving the list of names of entities, collections,
+ queries and region caches using the following methods:
+ <literal>getQueries()</literal>, <literal>getEntityNames()</literal>,
+ <literal>getCollectionRoleNames()</literal>, and
+ <literal>getSecondLevelCacheRegionNames()</literal>.</para>
</section>
-
+ </section>
</chapter>
14 years, 4 months