Hibernate SVN: r16744 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2009-06-10 14:33:44 -0400 (Wed, 10 Jun 2009)
New Revision: 16744
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/transactions.xml
Log:
added a definition of UoW
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/transactions.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/transactions.xml 2009-06-10 18:21:29 UTC (rev 16743)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/transactions.xml 2009-06-10 18:33:44 UTC (rev 16744)
@@ -92,6 +92,23 @@
<title>Unit of work</title>
<para>
+ First, let's define a unit of work. A unit of work is a
+ design pattern described by Martin Fowler as
+ <quote>
+ [maintaining] a list of objects affected by a business
+ transaction and coordinates the writing out of changes
+ and the resolution of concurrency problems.
+ </quote><citation>PoEAA</citation>
+ In other words, its a series of operations we wish to carry out
+ against the database together. Basically, it is a transaction,
+ though fulfilling a unit of work will often span multiple
+ physical database transactions (see <xref linkend="transactions-basics-apptx"/>).
+ So really we are talking about a more abstract notion of a
+ transaction. The term "business transaction" is also sometimes
+ used in lieu of unit of work.
+ </para>
+
+ <para>
Do not use the <emphasis>session-per-operation</emphasis> antipattern:
do not open and close a <literal>Session</literal> for every simple database call in
a single thread. The same is true for database transactions. Database calls
15 years, 7 months
Hibernate SVN: r16743 - validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-06-10 14:21:29 -0400 (Wed, 10 Jun 2009)
New Revision: 16743
Modified:
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertFalse.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertTrue.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMax.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMin.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Digits.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Future.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Max.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Min.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/NotNull.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Null.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Past.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Pattern.java
beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Size.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/Length.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/NotEmpty.java
validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages.properties
validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_de.properties
validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_fr.properties
validator/trunk/hibernate-validator/src/test/resources/ValidationMessages_es.properties
Log:
BVAL-160 rename message template key as [f.q.c.n of the constraint].message
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertFalse.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertFalse.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertFalse.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -39,7 +39,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface AssertFalse {
- String message() default "{constraint.assertFalse}";
+ String message() default "{javax.validation.constraints.AssertFalse.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertTrue.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertTrue.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/AssertTrue.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -39,7 +39,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface AssertTrue {
- String message() default "{constraint.assertTrue}";
+ String message() default "{javax.validation.constraints.AssertTrue.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMax.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMax.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMax.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -50,7 +50,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface DecimalMax {
- String message() default "{constraint.decimalmax}";
+ String message() default "{javax.validation.constraints.DecimalMax.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMin.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMin.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/DecimalMin.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -50,7 +50,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface DecimalMin {
- String message() default "{constraint.decimalmin}";
+ String message() default "{javax.validation.constraints.DecimalMin.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Digits.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Digits.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Digits.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -47,7 +47,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Digits {
- String message() default "{constraint.digits}";
+ String message() default "{javax.validation.constraints.Digits.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Future.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Future.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Future.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -47,7 +47,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Future {
- String message() default "{constraint.future}";
+ String message() default "{javax.validation.constraints.Future.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Max.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Max.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Max.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -49,7 +49,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Max {
- String message() default "{constraint.max}";
+ String message() default "{javax.validation.constraints.Max.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Min.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Min.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Min.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -49,7 +49,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Min {
- String message() default "{constraint.min}";
+ String message() default "{javax.validation.constraints.Min.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/NotNull.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/NotNull.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/NotNull.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -37,7 +37,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface NotNull {
- String message() default "{constraint.notNull}";
+ String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Null.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Null.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Null.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -37,7 +37,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Null {
- String message() default "{constraint.null}";
+ String message() default "{javax.validation.constraints.Null.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Past.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Past.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Past.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -47,7 +47,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Past {
- String message() default "{constraint.past}";
+ String message() default "{javax.validation.constraints.Past.message}";
Class<?>[] groups() default { };
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Pattern.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Pattern.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Pattern.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -53,7 +53,7 @@
/**
* @return The error message template.
*/
- String message() default "{constraint.pattern}";
+ String message() default "{javax.validation.constraints.Pattern.message}";
/**
* @return The groups the constraint belongs to.
Modified: beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Size.java
===================================================================
--- beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Size.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ beanvalidation/trunk/validation-api/src/main/java/javax/validation/constraints/Size.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -45,7 +45,7 @@
@Documented
@Constraint(validatedBy = {})
public @interface Size {
- String message() default "{constraint.size}";
+ String message() default "{javax.validation.constraints.Size.message}";
Class<?>[] groups() default {};
/**
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/Length.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/Length.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/Length.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -40,7 +40,7 @@
int max() default Integer.MAX_VALUE;
- String message() default "{constraint.length}";
+ String message() default "{org.hibernate.validation.constraints.Length.message}";
Class<?>[] groups() default { };
}
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/NotEmpty.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/NotEmpty.java 2009-06-10 17:18:13 UTC (rev 16742)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/constraints/NotEmpty.java 2009-06-10 18:21:29 UTC (rev 16743)
@@ -33,7 +33,7 @@
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
public @interface NotEmpty {
- String message() default "{constraint.notEmpty}";
+ String message() default "{org.hibernate.validation.constraints.NotEmpty.message}";
Class<?>[] groups() default { };
}
\ No newline at end of file
Modified: validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages.properties
===================================================================
--- validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages.properties 2009-06-10 17:18:13 UTC (rev 16742)
+++ validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages.properties 2009-06-10 18:21:29 UTC (rev 16743)
@@ -1,14 +1,14 @@
# $Id$
-constraint.notNull=may not be null
-constraint.size=size must be between {min} and {max}
-constraint.length=length must be between {min} and {max}
-constraint.notEmpty=may not be empty
-constraint.pattern=must match "{regexp}"
-constraint.min=must be greater than or equal to {value}
-constraint.max=must be less than or equal to {value}
-constraint.null=must be null
-constraint.past=must be in the past
-constraint.future=must be in the future
-constraint.assertTrue=must be true
-constraint.assertFalse=must be false
-constraint.digits=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
+javax.validation.constraints.NotNull.message=may not be null
+javax.validation.constraints.Size.message=size must be between {min} and {max}
+org.hibernate.validation.constraints.Length.message=length must be between {min} and {max}
+org.hibernate.validation.constraints.NotEmpty.message=may not be empty
+javax.validation.constraints.Pattern.message=must match "{regexp}"
+javax.validation.constraints.Min.message=must be greater than or equal to {value}
+javax.validation.constraints.Max.message=must be less than or equal to {value}
+javax.validation.constraints.Null.message=must be null
+javax.validation.constraints.Past.message=must be in the past
+javax.validation.constraints.Future.message=must be in the future
+javax.validation.constraints.AssertTrue.message=must be true
+javax.validation.constraints.AssertFalse.message=must be false
+javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
Modified: validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_de.properties
===================================================================
--- validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_de.properties 2009-06-10 17:18:13 UTC (rev 16742)
+++ validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_de.properties 2009-06-10 18:21:29 UTC (rev 16743)
@@ -1,14 +1,14 @@
# $Id$
-constraint.notNull=kann nicht null sein
-constraint.size=muss zwischen {min} und {max} liegen
-constraint.length=muss zwischen {min} und {max} liegen
-constraint.notEmpty=kann nicht leer sein
-constraint.pattern=muss auf Ausdruck "{regexp}" passen
-constraint.min=muss gr\u00F6ssergleich {value} sein
-constraint.max=muss kleinergleich {value} sein
-constraint.null=muss null sein
-constraint.past=muss in der Vergangenheit liegen
-constraint.future=muss in der Zukunft liegen
-constraint.assertTrue=muss wahr sein
-constraint.assertFalse=muss falsch sein
-constraint.digits=numerischer Wert aus\u00DFerhalb erlaubten Wertebereichs (<{integer} Ziffern>.<{fraction} Ziffern> erwarted)
+javax.validation.constraints.NotNull.message=kann nicht null sein
+javax.validation.constraints.Size.message=muss zwischen {min} und {max} liegen
+org.hibernate.validation.constraints.Length.message=muss zwischen {min} und {max} liegen
+org.hibernate.validation.constraints.NotEmpty.message=kann nicht leer sein
+javax.validation.constraints.Pattern.message=muss auf Ausdruck "{regexp}" passen
+javax.validation.constraints.Min.message=muss gr\u00F6ssergleich {value} sein
+javax.validation.constraints.Max.message=muss kleinergleich {value} sein
+javax.validation.constraints.Null.message=muss null sein
+javax.validation.constraints.Past.message=muss in der Vergangenheit liegen
+javax.validation.constraints.Future.message=muss in der Zukunft liegen
+javax.validation.constraints.AssertTrue.message=muss wahr sein
+javax.validation.constraints.AssertFalse.message=muss falsch sein
+javax.validation.constraints.Digits.message=numerischer Wert aus\u00DFerhalb erlaubten Wertebereichs (<{integer} Ziffern>.<{fraction} Ziffern> erwarted)
\ No newline at end of file
Modified: validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_fr.properties
===================================================================
--- validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_fr.properties 2009-06-10 17:18:13 UTC (rev 16742)
+++ validator/trunk/hibernate-validator/src/main/resources/org/hibernate/validation/ValidationMessages_fr.properties 2009-06-10 18:21:29 UTC (rev 16743)
@@ -1,13 +1,14 @@
# $Id$
-constraint.notNull=ne peut pas \u00EAtre nul
-constraint.size=la taille doit \u00EAtre entre {min} et {max}
-constraint.length=length must be between {min} and {max}
-constraint.notEmpty=ne peut pas \u00EAtre vide
-constraint.pattern=doit suivre "{regexp}"
-constraint.min=doit \u00EAtre plus grand que {value}
-constraint.max=doit \u00EAtre plus petit que {value}
-constraint.null=doit \u00EAtre nul
-constraint.past=doit \u00EAtre dans le pass\u00E9
-constraint.future=doit \u00EAtre dans le futur
-constraint.assertTrue=doit \u00EAtre vrai
-constraint.assertFalse=doit \u00EAtre faux
+javax.validation.constraints.NotNull.message=ne peut pas \u00EAtre nul
+javax.validation.constraints.Size.message=la taille doit \u00EAtre entre {min} et {max}
+org.hibernate.validation.constraints.Length.message=length must be between {min} and {max}
+org.hibernate.validation.constraints.NotEmpty.message=ne peut pas \u00EAtre vide
+javax.validation.constraints.Pattern.message=doit suivre "{regexp}"
+javax.validation.constraints.Min.message=doit \u00EAtre plus grand que {value}
+javax.validation.constraints.Max.message=doit \u00EAtre plus petit que {value}
+javax.validation.constraints.Null.message=doit \u00EAtre nul
+javax.validation.constraints.Past.message=doit \u00EAtre dans le pass\u00E9
+javax.validation.constraints.Future.message=doit \u00EAtre dans le futur
+javax.validation.constraints.AssertTrue.message=doit \u00EAtre vrai
+javax.validation.constraints.AssertFalse.message=doit \u00EAtre faux
+javax.validation.constraints.Digits.message=Valeur num\u00E9rique hors limite (<{integer} chiffres>.<{fraction} chiffres> attendus)
Modified: validator/trunk/hibernate-validator/src/test/resources/ValidationMessages_es.properties
===================================================================
--- validator/trunk/hibernate-validator/src/test/resources/ValidationMessages_es.properties 2009-06-10 17:18:13 UTC (rev 16742)
+++ validator/trunk/hibernate-validator/src/test/resources/ValidationMessages_es.properties 2009-06-10 18:21:29 UTC (rev 16743)
@@ -1 +1 @@
-constraint.notNull=no puede ser null
\ No newline at end of file
+javax.validation.constraints.NotNull.message=no puede ser null
\ No newline at end of file
15 years, 7 months
Hibernate SVN: r16742 - core/branches/Branch_3_3/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2009-06-10 13:18:13 -0400 (Wed, 10 Jun 2009)
New Revision: 16742
Modified:
core/branches/Branch_3_3/documentation/manual/src/main/docbook/en-US/content/tutorial.xml
Log:
HHH-3953 - Update tutorial chapter
Modified: core/branches/Branch_3_3/documentation/manual/src/main/docbook/en-US/content/tutorial.xml
===================================================================
--- core/branches/Branch_3_3/documentation/manual/src/main/docbook/en-US/content/tutorial.xml 2009-06-10 17:17:48 UTC (rev 16741)
+++ core/branches/Branch_3_3/documentation/manual/src/main/docbook/en-US/content/tutorial.xml 2009-06-10 17:18:13 UTC (rev 16742)
@@ -27,25 +27,17 @@
<!ENTITY mdash "-">
]>
- <!-- todo : need separate sections, one for each tutorial -->
-
<chapter id="tutorial">
- <title>Introduction to Hibernate</title>
-
- <sect1 id="tutorial-intro">
- <title>Preface</title>
+ <title>Tutorial</title>
- <para>
- Intended for new users, this chapter provides an introduction to Hibernate
- The chapter provides a step-by-step tutorial, starting with a simple
- application using an in-memory database. The tutorial is
- based on an earlier tutorial developed by Michael Gloegl. All
- code is contained in the <filename>tutorials/web</filename> directory
- of the project source.
- </para>
+ <para>
+ Intended for new users, this chapter provides an step-by-step introduction
+ to Hibernate, starting with a simple application using an in-memory database. The
+ tutorial is based on an earlier tutorial developed by Michael Gloegl. All
+ code is contained in the <filename>tutorials/web</filename> directory of the project
+ source.
+ </para>
- </sect1>
-
<important>
<para>
This tutorial expects the user have knowledge of both Java and
@@ -57,11 +49,9 @@
<note>
<para>
- There is another tutorial/example application in the
- <filename>/tutorials/eg</filename> directory of the project source.
- That example is console-based and, as such, would not have the
- dependency on a servlet container to execute. The basic setup is
- the same as the instructions below.
+ The distribution contains another example application under
+ the <filename>tutorial/eg</filename> project source
+ directory.
</para>
</note>
@@ -70,34 +60,56 @@
<para>
For this example, we will set up a small database application that can store
- events we want to attend, and information about the host(s) of
- these events. Although you can use whatever database you feel comfortable using, we will use an in-memory, Java database named HSQLDB
- to avoid describing installation/setup of any particular database
- servers.
+ events we want to attend and information about the host(s) of these events.
</para>
-
- <para>
- The first thing we need to do is set up the development environment,
- including all the required dependencies to Hibernate,
- as well as other libraries. Hibernate is built using Maven which,
- amongst other features, provides <literal>dependency management</literal>;
- moreover it provides <emphasis>transitive</emphasis>
- <literal>dependency management</literal>, which simply means that by using
- Hibernate we can define our dependency on Hibernate. Hibernate
- itself defines the dependencies it needs which then become transitive
- dependencies of our project.
- </para>
- <programlisting><![CDATA[.
-<project xmlns="http://maven.apache.org/POM/4.0.0"
+ <note>
+ <para>
+ Although you can use whatever database you feel comfortable using, we
+ will use <ulink url="http://hsqldb.org/">HSQLDB</ulink> (an in-memory,
+ Java database) to avoid describing installation/setup of any particular
+ database servers.
+ </para>
+ </note>
+
+ <sect2 id="tutorial-firstapp-setup">
+ <title>Setup</title>
+
+ <para>
+ The first thing we need to do is to set up the development environment. We
+ will be using the "standard layout" advocated by alot of build tools such
+ as <ulink url="http://maven.org">Maven</ulink>. Maven, in particular, has a
+ good resource describing this <ulink url="http://maven.apache.org/guides/introduction/introduction-to-the-standard-...">layout</ulink>.
+ As this tutorial is to be a web application, we will be creating and making
+ use of <filename>src/main/java</filename>, <filename>src/main/resources</filename>
+ and <filename>src/main/webapp</filename> directories.
+ </para>
+
+ <para>
+ We will be using Maven in this tutorial, taking advantage of its
+ transitive dependency management capabilities as well as the ability of
+ many IDEs to automatically set up a project for us based on the maven descriptor.
+ </para>
+
+ <programlisting><![CDATA[<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- ...
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.hibernate.tutorials</groupId>
+ <artifactId>hibernate-tutorial</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>First Hibernate Tutorial</name>
+
+ <build>
+ <!-- we dont want the version to be part of the generated war file name -->
+ <finalName>${artifactId}</finalName>
+ </build>
+
<dependencies>
<dependency>
- <groupId>${groupId}</groupId>
+ <groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
@@ -106,34 +118,52 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
+
+ <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+
+ <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->
+ <dependency>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
</dependencies>
</project>]]></programlisting>
- <note>
- <para>
- Essentially we are describing here the
- <filename>/tutorials/web/pom.xml</filename> file. See the
- <ulink url="http://maven.org">Maven</ulink> site for more information.
- </para>
- </note>
+ <tip>
+ <para>
+ It is not a requirement to use Maven. If you wish to use something else to
+ build this tutoial (such as Ant), the layout will remain the same. The only
+ change is that you will need to manually account for all the needed
+ dependencies. If you use something like <ulink url="http://ant.apache.org/ivy/">Ivy</ulink>
+ providing transitive dependency management you would still use the dependencies
+ mentioned below. Otherwise, you'd need to grab <emphasis>all</emphasis>
+ dependencies, both explicit and transitive, and add them to the project's
+ classpath. If working from the Hibernate distribution bundle, this would mean
+ <filename>hibernate3.jar</filename>, all artifacts in the
+ <filename>lib/required</filename> directory and all files from either the
+ <filename>lib/bytecode/cglib</filename> or <filename>lib/bytecode/javassist</filename>
+ directory; additionally you will need both the servlet-api jar and one of the slf4j
+ logging backends.
+ </para>
+ </tip>
- <tip>
<para>
- While not strictly necessary, most IDEs have integration with Maven
- to read these POM files and automatically set up a project for you.
+ Save this file as <filename>pom.xml</filename> in the project root directory.
</para>
- </tip>
+ </sect2>
- <para>
- Next, create a class that represents the event you want to store in the database.
- </para>
-
+
<sect2 id="tutorial-firstapp-firstclass">
<title>The first class</title>
<para>
- The first persistent class is a simple JavaBean class with some properties:
+ Next, we create a class that represents the event we want to store in the
+ database; it is a simple JavaBean class with some properties:
</para>
<programlisting><![CDATA[package org.hibernate.tutorial.domain;
@@ -175,57 +205,50 @@
<para>
This class uses standard JavaBean naming conventions for property
- getter and setter methods, as well as private visibility for the fields. Although this is
- a recommended design, it is not required. Hibernate can also access fields directly,
- the benefit of accessor methods is robustness for refactoring. The no-argument
- constructor is required to instantiate an object of this class through reflection.
+ getter and setter methods, as well as private visibility for the
+ fields. Although this is the recommended design, it is not required.
+ Hibernate can also access fields directly, the benefit of accessor
+ methods is robustness for refactoring.
</para>
<para>
- The <literal>id</literal> property holds a unique identifier value for a particular event.
- All persistent entity classes (there are less important dependent classes as well) will need
- such an identifier property if we want to use the full feature set of Hibernate. In fact,
- most applications, especially web applications, need to distinguish objects by identifier, so you
- should consider this a feature rather than a limitation. However, we usually do not manipulate
- the identity of an object, hence the setter method should be private. Only Hibernate will assign
- identifiers when an object is saved. Hibernate can access public, private,
- and protected accessor methods, as well as public, private and protected fields directly. The
- choice is up to you and you can match it to fit your application design.
+ The <literal>id</literal> property holds a unique identifier value
+ for a particular event. All persistent entity classes (there are
+ less important dependent classes as well) will need such an identifier
+ property if we want to use the full feature set of Hibernate. In fact,
+ most applications, especially web applications, need to distinguish
+ objects by identifier, so you should consider this a feature rather
+ than a limitation. However, we usually do not manipulate the identity
+ of an object, hence the setter method should be private. Only Hibernate
+ will assign identifiers when an object is saved. Hibernate can access
+ public, private, and protected accessor methods, as well as public,
+ private and protected fields directly. The choice is up to you and
+ you can match it to fit your application design.
</para>
<para>
- The no-argument constructor is a requirement for all persistent classes; Hibernate
- has to create objects for you, using Java Reflection. The constructor can be
- private, however, package visibility is required for runtime proxy generation and
- efficient data retrieval without bytecode instrumentation.
+ The no-argument constructor is a requirement for all persistent
+ classes; Hibernate has to create objects for you, using Java
+ Reflection. The constructor can be private, however package or public
+ visibility is required for runtime proxy generation and efficient data
+ retrieval without bytecode instrumentation.
</para>
<para>
- Place the Java source file in a directory called <literal>src</literal> in the
- development folder, and in its correct package. The directory should now look like this:
+ Save this file to the <filename>src/main/java/org/hibernate/tutorial/domain</filename>
+ directory.
</para>
-
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java]]></programlisting>
-
- <para>
- In the next step, we tell Hibernate about this persistent class.
- </para>
-
</sect2>
<sect2 id="tutorial-firstapp-mapping">
<title>The mapping file</title>
<para>
- Hibernate needs to know how to load and store objects of the persistent class.
- This is where the Hibernate mapping file comes into play. The mapping file
- tells Hibernate what table in the database it has to access, and what columns
- in that table it should use.
+ Hibernate needs to know how to load and store objects of the
+ persistent class. This is where the Hibernate mapping file
+ comes into play. The mapping file tells Hibernate what table in
+ the database it has to access, and what columns in that table
+ it should use.
</para>
<para>
@@ -237,25 +260,28 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
-<hibernate-mapping>
+<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping>]]></programlisting>
<para>
- Hibernate DTD is sophisticated. You can use it for
- auto-completion of XML mapping elements and attributes in your editor or
- IDE. Opening up the DTD file in your text editor is the
- easiest way to get an overview of all elements and attributes, and to view
- the defaults, as well as some comments. Hibernate will not
- load the DTD file from the web, but first look it up from the classpath
- of the application. The DTD file is included in <literal>hibernate3.jar</literal>
- as well as in the <literal>src/</literal> directory of the Hibernate distribution.
+ Hibernate DTD is sophisticated. You can use it for auto-completion
+ of XML mapping elements and attributes in your editor or IDE.
+ Opening up the DTD file in your text editor is the easiest way to
+ get an overview of all elements and attributes, and to view the
+ defaults, as well as some comments. Hibernate will not load the
+ DTD file from the web, but first look it up from the classpath of
+ the application. The DTD file is included in
+ <filename>hibernate-core.jar</filename> (it is also included in the
+ <filename>hibernate3.jar</filename>, if using the distribution bundle).
</para>
- <para>
- We will omit the DTD declaration in future examples to shorten the code. It is,
- of course, not optional.
- </para>
+ <important>
+ <para>
+ We will omit the DTD declaration in future examples to shorten the code. It is,
+ of course, not optional.
+ </para>
+ </important>
<para>
Between the two <literal>hibernate-mapping</literal> tags, include a
@@ -264,25 +290,27 @@
a mapping to a table in the SQL database:
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <programlisting><![CDATA[<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
</class>
</hibernate-mapping>]]></programlisting>
<para>
- So far we have told Hibernate how to persist and load object of class <literal>Event</literal>
- to the table <literal>EVENTS</literal>. Each instance is now represented by a row in that table.
- Now we can continue by mapping the unique identifier property to the tables primary key.
- As we do not want to care about handling this identifier, we configure Hibernate's
+ So far we have told Hibernate how to persist and load object of
+ class <literal>Event</literal> to the table
+ <literal>EVENTS</literal>. Each instance is now represented by a
+ row in that table. Now we can continue by mapping the unique
+ identifier property to the tables primary key. As we do not want
+ to care about handling this identifier, we configure Hibernate's
identifier generation strategy for a surrogate primary key column:
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <programlisting><![CDATA[<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
@@ -291,29 +319,43 @@
</hibernate-mapping>]]></programlisting>
<para>
- The <literal>id</literal> element is the declaration of the identifier property,
- <literal>name="id"</literal> declares the name of the Java property.
- Hibernate will use the getter and setter methods to access the property.
- The column attribute tells Hibernate which column of the
- <literal>EVENTS</literal> table to use for this primary key. The nested
- <literal>generator</literal> element specifies the identifier generation strategy.
-
- In this case you used <literal>native</literal>, which selects the best strategy depending
- on the configured database dialect. Hibernate supports database generated, globally
- unique, as well as application assigned, identifiers. Hibernate supports any strategy you have written
- an extension for.
+ The <literal>id</literal> element is the declaration of the
+ identifier property. The <literal>name="id"</literal> mapping
+ attribute declares the name of the JavaBean property and tells
+ Hibernate to use the <literal>getId()</literal> and
+ <literal>setId()</literal> methods to access the property. The
+ column attribute tells Hibernate which column of the
+ <literal>EVENTS</literal> table holds the primary key value.
</para>
<para>
- Finally, include declarations for the persistent properties of the class in
- the mapping file. By default, no properties of the class are considered
+ The nested <literal>generator</literal> element specifies the
+ identifier generation strategy (aka how are identifier values
+ generated?). In this case we choose <literal>native</literal>,
+ which offers a level of portability depending on the configured
+ database dialect. Hibernate supports database generated, globally
+ unique, as well as application assigned, identifiers. Identifier
+ value generation is also one of Hibernate's many extension points
+ and you can plugin in your own strategy.
+ </para>
+
+ <tip>
+ <para>
+ <literal>native</literal> is no longer consider the best strategy in terms of portability. for further
+ discussion, see <xref linkend="portability-idgen"/>
+ </para>
+ </tip>
+
+ <para>
+ Lastly, we need to tell Hibernate about the remaining entity class
+ properties. By default, no properties of the class are considered
persistent:
</para>
<programlisting><![CDATA[
-<hibernate-mapping>
+<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
@@ -324,27 +366,31 @@
</hibernate-mapping>]]></programlisting>
<para>
- Similar to the <literal>id</literal> element, the <literal>name</literal>
- attribute of the <literal>property</literal> element tells Hibernate which getter
- and setter methods to use. In this case, Hibernate will search for
- <literal>getDate()/setDate()</literal>, as well as <literal>getTitle()/setTitle()</literal>.
+ Similar to the <literal>id</literal> element, the
+ <literal>name</literal> attribute of the
+ <literal>property</literal> element tells Hibernate which getter
+ and setter methods to use. In this case, Hibernate will search
+ for <literal>getDate()</literal>, <literal>setDate()</literal>,
+ <literal>getTitle()</literal> and <literal>setTitle()</literal>
+ methods.
</para>
- <para>
- Why does the <literal>date</literal> property mapping include the
- <literal>column</literal> attribute, but the <literal>title</literal>
- does not? Without the <literal>column</literal> attribute, Hibernate
- by default uses the property name as the column name. This works for
- <literal>title</literal>, however, <literal>date</literal> is a reserved
- keyword in most databases so you will need to map it to a different name.
- </para>
+ <note>
+ <para>
+ Why does the <literal>date</literal> property mapping include the
+ <literal>column</literal> attribute, but the <literal>title</literal>
+ does not? Without the <literal>column</literal> attribute, Hibernate
+ by default uses the property name as the column name. This works for
+ <literal>title</literal>, however, <literal>date</literal> is a reserved
+ keyword in most databases so you will need to map it to a different name.
+ </para>
+ </note>
<para>
- The <literal>title</literal> mapping also lacks
- a <literal>type</literal> attribute. The types declared and used in the mapping
- files are not, as you might expect, Java data types. They are also not SQL
- database types. These types are called <emphasis>Hibernate mapping types</emphasis>,
- converters which can translate from Java to SQL data types and vice versa. Again,
+ The <literal>title</literal> mapping also lacks a <literal>type</literal> attribute. The
+ types declared and used in the mapping files are not Java data types; they are not SQL
+ database types either. These types are called <emphasis>Hibernate mapping types</emphasis>,
+ converters which can translate from Java to SQL data types and vice versa. Again,
Hibernate will try to determine the correct conversion and mapping type itself if
the <literal>type</literal> attribute is not present in the mapping. In some cases this
automatic detection using Reflection on the Java class might not have the default you
@@ -355,59 +401,64 @@
<literal>timestamp</literal> converter.
</para>
+ <tip>
+ <para>
+ Hibernate makes this mapping type determination using reflection when the mapping files
+ are processed. This can take time and resources, so if startup performance is important
+ you should consider explicitly defining the type to use.
+ </para>
+ </tip>
+
<para>
- Save the mapping file as <literal>Event.hbm.xml</literal> in
- the directory next to the <literal>Event</literal> Java class source file.
- The naming of mapping files can be arbitrary, however, the <literal>hbm.xml</literal>
- suffix is a convention in the Hibernate developer community. The directory structure
- should now look like this:
+ Save this mapping file as
+ <filename>src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml</filename>.
</para>
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java
- Event.hbm.xml]]></programlisting>
-
- <para>
- The next section continues with the main configuration of Hibernate.
- </para>
-
</sect2>
<sect2 id="tutorial-firstapp-configuration" revision="2">
<title>Hibernate configuration</title>
<para>
- You should have a persistent class and its mapping file in place. It is now time to configure
- Hibernate. Before you do this, you will need a database. HSQL DB, a java-based SQL DBMS,
- can be downloaded from the HSQL DB website(http://hsqldb.org/). You only need the <literal>hsqldb.jar</literal>
- from this download. Place this file in the <literal>lib/</literal> directory of the
- development folder.
+ At this point, you should have the persistent class and its mapping
+ file in place. It is now time to configure Hibernate. First let's set up
+ HSQLDB to run in "server mode"
</para>
+ <note>
+ <para>
+ We do this do that the data remains between runs.
+ </para>
+ </note>
+
<para>
- Create a directory called <literal>data</literal> in the root of the development directory.
- This is where HSQL DB will store its data files. Start the database by running
- <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal> in this data directory.
- You will see it start up and bind to a TCP/IP socket; this is where our application
- will connect later. If you want to start with a fresh database during this tutorial,
- shutdown HSQL DB (press <literal>CTRL + C</literal> in the window), delete all files in the
- <literal>data/</literal> directory, and start HSQL DB again.
+ We will utilize the Maven exec plugin to launch the HSQLDB server
+ by running:
+ <command> mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial"</command>
+ You will see it start up and bind to a TCP/IP socket; this is where
+ our application will connect later. If you want to start
+ with a fresh database during this tutorial, shutdown HSQLDB, delete
+ all files in the <filename>target/data</filename> directory,
+ and start HSQLDB again.
</para>
<para>
- Hibernate is the layer in your application that connects to this database, so it needs
- connection information. The connections are made through a JDBC connection pool, which you
- will also have to configure. The Hibernate distribution contains several open source JDBC connection
- pooling tools, but you will use the Hibernate built-in connection pool for this tutorial.
- You will have to copy the required library into your classpath and use different
- connection pooling settings if you want to use a production-quality third party
- JDBC pooling software.
+ Hibernate will be connecting to the database on behalf of your application, so it needs to know
+ how to obtain connections. For this tutorial we will be using a standalone connection
+ pool (as opposed to a <interfacename>javax.sql.DataSource</interfacename>). Hibernate comes with
+ support for two third-party open source JDBC connection pools:
+ <ulink url="https://sourceforge.net/projects/c3p0">c3p0</ulink> and
+ <ulink url="http://proxool.sourceforge.net/">proxool</ulink>. However, we will be using the
+ Hibernate built-in connection pool for this tutorial.
</para>
+ <caution>
+ <para>
+ The built-in Hibernate connection pool is in no way intended for production use. It
+ lacks several features found on any decent connection pool.
+ </para>
+ </caution>
+
<para>
For Hibernate's configuration, we can use a simple <literal>hibernate.properties</literal> file, a
more sophisticated <literal>hibernate.cfg.xml</literal> file, or even complete
@@ -445,19 +496,22 @@
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
- <property name="hbm2ddl.auto">create</property>
+ <property name="hbm2ddl.auto">update</property>
- <mapping resource="events/Event.hbm.xml"/>
+ <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
+ <note>
+ <para>Notice that this configuration file specifies a different DTD</para>
+ </note>
+
<para>
- This XML configuration uses a different DTD. You configure
- Hibernate's <literal>SessionFactory</literal>. SessionFactory is a global factory responsible
- for a particular database. If you have several databases, for easier startup you should use several
- <literal><session-factory></literal> configurations in
+ You configure Hibernate's <literal>SessionFactory</literal>. SessionFactory is a global
+ factory responsible for a particular database. If you have several databases, for easier
+ startup you should use several <literal><session-factory></literal> configurations in
several configuration files.
</para>
@@ -465,9 +519,18 @@
The first four <literal>property</literal> elements contain the necessary
configuration for the JDBC connection. The dialect <literal>property</literal>
element specifies the particular SQL variant Hibernate generates.
- Hibernate's automatic session management for persistence contexts is particularly useful in this context.
+ </para>
- The <literal>hbm2ddl.auto</literal> option turns on automatic generation of
+ <tip>
+ <para>
+ In most cases, Hibernate is able to properly determine which dialect to use. See
+ <xref linkend="portability-dialectresolver"/> for more information.
+ </para>
+ </tip>
+
+ <para>
+ Hibernate's automatic session management for persistence contexts is particularly useful
+ in this context. The <literal>hbm2ddl.auto</literal> option turns on automatic generation of
database schemas directly into the database. This can also be turned
off by removing the configuration option, or redirected to a file with the help of
the <literal>SchemaExport</literal> Ant task. Finally, add the mapping file(s)
@@ -475,116 +538,83 @@
</para>
<para>
- Copy the file into the source directory, so it will end up in the
- root of the classpath. On startup, Hibernate automatically searches for a file called
- <literal>hibernate.cfg.xml</literal> in the root of the classpath.
+ Save this file as <filename>hibernate.cfg.xml</filename> into the
+ <filename>src/main/resources</filename> directory.
</para>
</sect2>
- <sect2 id="tutorial-firstapp-ant" revision="1">
- <title>Building with Ant</title>
+ <sect2 id="tutorial-firstapp-mvn" revision="1">
+ <title>Building with Maven</title>
<para>
- We will now build the tutorial with Ant. You will need to have Ant installed.
- It is available from the <ulink url="http://ant.apache.org/bindownload.cgi">Ant download page</ulink>.
- Installation instructions for Ant, however, are not be covered here. Please refer to the
- <ulink url="http://ant.apache.org/manual/index.html">Ant manual</ulink> for further information. After you
- have installed Ant, you can start to create the buildfile. It will be called
- <literal>build.xml</literal> and placed directly in the development directory.
+ We will now build the tutorial with Maven. You will need to
+ have Maven installed; it is available from the
+ <ulink url="http://maven.apache.org/download.html">Maven download page</ulink>.
+ Maven will read the <filename>/pom.xml</filename> file we created
+ earlier and know how to perform some basic project tasks. First,
+ lets run the <literal>compile</literal> goal to make sure we can compile
+ everything so far:
</para>
- <para>
- A basic build file looks like this:
- </para>
+ <programlisting><![CDATA[[hibernateTutorial]$ mvn compile
+[INFO] Scanning for projects...
+[INFO] ------------------------------------------------------------------------
+[INFO] Building First Hibernate Tutorial
+[INFO] task-segment: [compile]
+[INFO] ------------------------------------------------------------------------
+[INFO] [resources:resources]
+[INFO] Using default encoding to copy filtered resources.
+[INFO] [compiler:compile]
+[INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESSFUL
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 2 seconds
+[INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
+[INFO] Final Memory: 5M/547M
+[INFO] ------------------------------------------------------------------------]]></programlisting>
- <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
-
- <property name="sourcedir" value="${basedir}/src"/>
- <property name="targetdir" value="${basedir}/bin"/>
- <property name="librarydir" value="${basedir}/lib"/>
-
- <path id="libraries">
- <fileset dir="${librarydir}">
- <include name="*.jar"/>
- </fileset>
- </path>
-
- <target name="clean">
- <delete dir="${targetdir}"/>
- <mkdir dir="${targetdir}"/>
- </target>
-
- <target name="compile" depends="clean, copy-resources">
- <javac srcdir="${sourcedir}"
- destdir="${targetdir}"
- classpathref="libraries"/>
- </target>
-
- <target name="copy-resources">
- <copy todir="${targetdir}">
- <fileset dir="${sourcedir}">
- <exclude name="**/*.java"/>
- </fileset>
- </copy>
- </target>
-
-</project>]]></programlisting>
-
- <para>
- This will tell Ant to add all files in the library directory ending with <literal>.jar</literal>
- to the classpath used for compilation. It will also copy all non-Java source files to the
- target directory, configuration and Hibernate mapping files, for example. If you now run Ant, you
- will get this output:
- </para>
-
- <programlisting><![CDATA[C:\hibernateTutorial\>ant
-Buildfile: build.xml
-
-copy-resources:
- [copy] Copying 2 files to C:\hibernateTutorial\bin
-
-compile:
- [javac] Compiling 1 source file to C:\hibernateTutorial\bin
-
-BUILD SUCCESSFUL
-Total time: 1 second ]]></programlisting>
-
</sect2>
<sect2 id="tutorial-firstapp-helpers" revision="3">
<title>Startup and helpers</title>
<para>
- It is time to load and store some <literal>Event</literal> objects, but first
- you have to complete the setup with some infrastructure code. You have to startup
- Hibernate by building a global <literal>SessionFactory</literal>
- object and storing it somewhere for easy access in application code.
- A <literal>SessionFactory</literal> can open up new <literal>Session</literal>s.
- A <literal>Session</literal> represents a single-threaded unit of work. The
- <literal>SessionFactory</literal> is a thread-safe global object, that is instantiated once.
+ It is time to load and store some <literal>Event</literal>
+ objects, but first you have to complete the setup with some
+ infrastructure code. You have to startup Hibernate by building
+ a global <interfacename>org.hibernate.SessionFactory</interfacename>
+ object and storing it somewhere for easy access in application code. A
+ <interfacename>org.hibernate.SessionFactory</interfacename> is used to
+ obtain <interfacename>org.hibernate.Session</interfacename> instances.
+ A <interfacename>org.hibernate.Session</interfacename> represents a
+ single-threaded unit of work. The
+ <interfacename>org.hibernate.SessionFactory</interfacename> is a
+ thread-safe global object that is instantiated once.
</para>
<para>
- Create a <literal>HibernateUtil</literal> helper class that takes care
- of startup and makes accessing a <literal>SessionFactory</literal> convenient.
- The following example illustrates the implementation:
+ We will create a <literal>HibernateUtil</literal> helper class that
+ takes care of startup and makes accessing the
+ <interfacename>org.hibernate.SessionFactory</interfacename> more convenient.
</para>
- <programlisting><![CDATA[package util;
+ <programlisting><![CDATA[package org.hibernate.tutorial.util;
-import org.hibernate.*;
-import org.hibernate.cfg.*;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
public class HibernateUtil {
- private static final SessionFactory sessionFactory;
+ private static final SessionFactory sessionFactory = buildSessionFactory();
- static {
+ private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
- sessionFactory = new Configuration().configure().buildSessionFactory();
- } catch (Throwable ex) {
+ return new Configuration().configure().buildSessionFactory();
+ }
+ catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
@@ -598,39 +628,28 @@
}]]></programlisting>
<para>
- This class not only produces the global <literal>SessionFactory</literal> in
- its static initializer that is called once by the JVM when the class is loaded. It also
- hides the fact that it uses a static singleton. It might as well lookup the
- <literal>SessionFactory</literal> from JNDI in an application server.
+ Save this code as
+ <filename>src/main/java/org/hibernate/tutorial/util/HibernateUtil.java</filename>
</para>
<para>
- If you give the <literal>SessionFactory</literal> a name in your configuration
- file, Hibernate will try to bind it to JNDI after it has been built.
- To avoid this code completely you could also use JMX deployment and let the
- JMX-capable container instantiate and bind a <literal>HibernateService</literal>
- to JNDI. These advanced options are discussed in the Hibernate reference
- documentation.
+ This class not only produces the global
+ <interfacename>org.hibernate.SessionFactory</interfacename> reference in
+ its static initializer; it also hides the fact that it uses a
+ static singleton. We might just as well have looked up the
+ <interfacename>org.hibernate.SessionFactory</interfacename> reference from
+ JNDI in an application server or any other location for that matter.
</para>
<para>
- Place <literal>HibernateUtil.java</literal> in the development source directory in
- a package next to <literal>events</literal>:
+ If you give the <interfacename>org.hibernate.SessionFactory</interfacename>
+ a name in your configuration, Hibernate will try to bind it to
+ JNDI under that name after it has been built. Another, better option is to
+ use a JMX deployment and let the JMX-capable container instantiate and bind
+ a <literal>HibernateService</literal> to JNDI. Such advanced options are
+ discussed later.
</para>
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java
- Event.hbm.xml
- +util
- HibernateUtil.java
- hibernate.cfg.xml
-+data
-build.xml]]></programlisting>
-
<para>
You now need to configure a logging
system. Hibernate uses commons logging and provides two choices: Log4j and
@@ -652,16 +671,19 @@
<title>Loading and storing objects</title>
<para>
- You can use Hibernate to load and store objects. Write an
- <literal>EventManager</literal> class with a <literal>main()</literal> method:
+ We are now ready to start doing some real worjk with Hibernate.
+ Let's start by writing an <literal>EventManager</literal> class
+ with a <literal>main()</literal> method:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial;
+
import org.hibernate.Session;
-import java.util.Date;
+import java.util.*;
-import util.HibernateUtil;
+import org.hibernate.tutorial.domain.Event;
+import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
@@ -676,15 +698,12 @@
}
private void createAndStoreEvent(String title, Date theDate) {
-
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
-
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
-
session.save(theEvent);
session.getTransaction().commit();
@@ -693,165 +712,149 @@
}]]></programlisting>
<para>
- You can create a new <literal>Event</literal> object and hand it over to Hibernate.
- Hibernate takes care of the SQL and executes <literal>INSERT</literal>
- on the database. We will explain the <literal>Session</literal> and
- <literal>Transaction</literal>-handling code before we run this.
+ In <literal>createAndStoreEvent()</literal> we created a new
+ <literal>Event</literal> object and handed it over to Hibernate.
+ At that point, Hibernate takes care of the SQL and executes an
+ <literal>INSERT</literal> on the database.
</para>
<para>
- A <literal>Session</literal> is a single unit of work. For now we will keep things
- simple and assume a one-to-one granularity between a Hibernate <literal>Session</literal>
- and a database transaction. To shield our code from the actual underlying transaction
- system we use the
- <literal>Transaction</literal> API that is available on the Hibernate <literal>Session</literal>.
- In this case plain JDBC, but it could also run with JTA.
+ A <interface>org.hibernate.Session</interface> is designed to
+ represent a single unit of work (a single atmoic piece of work
+ to be performed). For now we will keep things simple and assume
+ a one-to-one granularity between a Hibernate
+ <interface>org.hibernate.Session</interface> and a database
+ transaction. To shield our code from the actual underlying
+ transaction system we use the Hibernate
+ <interfacename>org.hibernate.Transaction</interfacename> API.
+ In this particular case we are using JDBC-based transactional
+ semantics, but it could also run with JTA.
</para>
<para>
- What does <literal>sessionFactory.getCurrentSession()</literal> do? First, you can call it
- as many times and anywhere you like, once you get hold of your <literal>SessionFactory</literal>.
- This is easy thanks to <literal>HibernateUtil</literal>). The <literal>getCurrentSession()</literal>
- method always returns the "current" unit of work. Remember that we switched the configuration
- option for this mechanism to "thread" in <literal>hibernate.cfg.xml</literal>? The current unit of work is bound to the current Java thread that executes the application.
- However, you also have to consider when a unit of work
- begins and when it ends.
+ What does <literal>sessionFactory.getCurrentSession()</literal> do?
+ First, you can call it as many times and anywhere you like
+ once you get hold of your
+ <interfacename>org.hibernate.SessionFactory</interfacename>.
+ The <literal>getCurrentSession()</literal> method always returns
+ the "current" unit of work. Remember that we switched
+ the configuration option for this mechanism to "thread" in our
+ <filename>src/main/resources/hibernate.cfg.xml</filename>?
+ Due to that setting, the context of a current unit of work is bound
+ to the current Java thread that executes the application.
</para>
+ <important>
+ <para>
+ Hibernate offers three methods of current session tracking.
+ The "thread" based method is not intended for production use;
+ it is merely useful for prototyping and tutorials such as this
+ one. Current session tracking is discussed in more detail
+ later on.
+ </para>
+ </important>
+
<para>
- A <literal>Session</literal> begins when the first call to
- <literal>getCurrentSession()</literal> is made. It is then bound by Hibernate to the current
- thread. When the transaction ends, either through commit or rollback, Hibernate automatically
- unbinds the <literal>Session</literal> from the thread and closes it for you. If you call
- <literal>getCurrentSession()</literal> again, you get a new <literal>Session</literal> and can
- start a new unit of work. This <emphasis>thread-bound</emphasis> programming model is the most
- popular way of using Hibernate, as it allows flexible layering of your code. Transaction
- demarcation code can be separated from data access code. This will be covered later in this tutorial.
+ A <interface>org.hibernate.Session</interface> begins when the
+ first call to <literal>getCurrentSession()</literal> is made for
+ the current thread. It is then bound by Hibernate to the current
+ thread. When the transaction ends, either through commit or
+ rollback, Hibernate automatically unbinds the
+ <interface>org.hibernate.Session</interface> from the thread
+ and closes it for you. If you call
+ <literal>getCurrentSession()</literal> again, you get a new
+ <interface>org.hibernate.Session</interface> and can start a
+ new unit of work.
</para>
<para>
- Related to the unit of work scope, should the Hibernate <literal>Session</literal> be used to
- execute one or several database operations? The above example uses one <literal>Session</literal>
- for one operation. This is pure coincidence, the example is just not complex enough to show any
- other approach. The scope of a Hibernate <literal>Session</literal> is flexible but you should
- never design your application to use a new Hibernate <literal>Session</literal> for
- <emphasis>every</emphasis> database operation. Even though it is used in
- the following examples, consider <emphasis>session-per-operation</emphasis>
- an anti-pattern. A real web application is shown later in the tutorial.
+ Related to the unit of work scope, should the Hibernate
+ <interface>org.hibernate.Session</interface> be used to execute
+ one or several database operations? The above example uses one
+ <interface>org.hibernate.Session</interface> for one operation.
+ However this is pure coincidence; the example is just not complex
+ enough to show any other approach. The scope of a Hibernate
+ <interface>org.hibernate.Session</interface> is flexible but you
+ should never design your application to use a new Hibernate
+ <interface>org.hibernate.Session</interface> for
+ <emphasis>every</emphasis> database operation. Even though it is
+ used in the following examples, consider
+ <emphasis>session-per-operation</emphasis> an anti-pattern.
+ A real web application is shown later in the tutorial which will
+ help illustrate this.
</para>
<para>
See <xref linkend="transactions"/> for more information
- about transaction handling and demarcation. The previous example also skipped any error handling and
- rollback.
+ about transaction handling and demarcation. The previous
+ example also skipped any error handling and rollback.
</para>
<para>
- To run this first routine we have to add a callable target to the Ant build file:
+ To run this, we will make use of the Maven exec plugin to call our class
+ with the necessary classpath setup:
+ <command>mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"</command>
</para>
- <programlisting><![CDATA[<target name="run" depends="compile">
- <java fork="true" classname="events.EventManager" classpathref="libraries">
- <classpath path="${targetdir}"/>
- <arg value="${action}"/>
- </java>
-</target>]]></programlisting>
+ <note>
+ <para>
+ You may need to perform <command>mvn compile</command> first.
+ </para>
+ </note>
<para>
- The value of the <literal>action</literal> argument is set on the command line when
- calling the target:
+ You should see Hibernate starting up and, depending on your configuration,
+ lots of log output. Towards the end, the following line will be displayed:
</para>
- <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
-
- <para>
- After compilation, you should see Hibernate starting up and, depending on your
- configuration, lots of log output. At the end, the following line will be displayed:
- </para>
-
<programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
<para>
- This is the <literal>INSERT</literal> executed by Hibernate. The question marks
- represent JDBC bind parameters. To view the values bound as arguments, or to reduce
- the verbosity of the log, check your <literal>log4j.properties</literal>.
+ This is the <literal>INSERT</literal> executed by Hibernate.
</para>
<para>
To list stored events an option is added to the main method:
</para>
- <programlisting><![CDATA[if (args[0].equals("store")) {
- mgr.createAndStoreEvent("My Event", new Date());
-}
-else if (args[0].equals("list")) {
- List events = mgr.listEvents();
- for (int i = 0; i < events.size(); i++) {
- Event theEvent = (Event) events.get(i);
- System.out.println("Event: " + theEvent.getTitle() +
- " Time: " + theEvent.getDate());
- }
-}]]></programlisting>
+ <programlisting><![CDATA[ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+ else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println(
+ "Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()
+ );
+ }
+ }]]></programlisting>
<para>
A new <literal>listEvents() method is also added</literal>:
</para>
- <programlisting><![CDATA[private List listEvents() {
+ <programlisting><![CDATA[ private List listEvents() {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+ List result = session.createQuery("from Event").list();
+ session.getTransaction().commit();
+ return result;
+ }]]></programlisting>
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
-
- session.beginTransaction();
-
- List result = session.createQuery("from Event").list();
-
- session.getTransaction().commit();
-
- return result;
-}]]></programlisting>
-
<para>
- Use an HQL (Hibernate Query Language) query to load all existing
+ Here, we are using a Hibernate Query Language (HQL) query to load all existing
<literal>Event</literal> objects from the database. Hibernate will generate the
appropriate SQL, send it to the database and populate <literal>Event</literal> objects
- with the data. You can create more complex queries with HQL.
+ with the data. You can create more complex queries with HQL. See <xref linkend="query-hql"/>
+ for more information.
</para>
<para>
- To execute and test all of this, follow these steps:
+ Now we can call our new functionality, again using the Maven exec plugin:
+ <command>mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"</command>
</para>
- <itemizedlist>
- <listitem>
- <para>
- Run <literal>ant run -Daction=store</literal> to store something in the database
- and, of course, to generate the database schema before through hbm2ddl.
- </para>
- </listitem>
- <listitem>
- <para>
- Disable hbm2ddl by commenting out the property in your <literal>hibernate.cfg.xml</literal>
- file. It is only turned on in continuous unit testing. Another
- run of hbm2ddl would <emphasis>drop</emphasis> everything you have stored. The
- <literal>create</literal> configuration setting translates into "drop all
- tables from the schema, then re-create all tables, when the SessionFactory is built".
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- If you can call Ant with <literal>-Daction=list</literal>, you will be able to view the events
- you have stored so far. You can also call the <literal>store</literal> action.
- </para>
-
- <para>
- Most new Hibernate users fail at this point and we regularly receive questions relating to
- <emphasis>Table not found</emphasis> error messages. However, if you follow the
- steps outlined above you will not have this problem, as hbm2ddl creates the database
- schema on the first run, and subsequent application restarts will use this schema. If
- you change the mapping and/or database schema, you will have to re-enable hbm2ddl.
- </para>
-
</sect2>
</sect1>
@@ -860,8 +863,10 @@
<title>Part 2 - Mapping associations</title>
<para>
- You have mapped a persistent entity class to a table. You can now build on this by adding some class associations.
- First you will add people to your application, and store a list of events they participate in.
+ So far we have mapped a single persistent entity class to a table in
+ isolation. Let's expand on that a bit and add some class associations.
+ We will add people to the application and store a list of events in
+ which they participate.
</para>
<sect2 id="tutorial-associations-mappinguser" revision="1">
@@ -871,7 +876,7 @@
The first cut of the <literal>Person</literal> class looks like this:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial.domain;
public class Person {
@@ -887,13 +892,18 @@
}]]></programlisting>
<para>
- Create a new mapping file called <literal>Person.hbm.xml</literal>. Do not forget the
- DTD reference at the top:
+ Save this to a file named
+ <filename>src/main/java/org/hibernate/tutorial/domain/Person.java</filename>
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <para>
+ Next, create the new mapping file as
+ <filename>src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml</filename>
+ </para>
- <class name="events.Person" table="PERSON">
+ <programlisting><![CDATA[<hibernate-mapping package="org.hibernate.tutorial.domain">
+
+ <class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
@@ -924,17 +934,16 @@
<title>A unidirectional Set-based association</title>
<para>
- By adding a collection of events to the <literal>Person</literal> class, you can
- easily navigate to the events for a particular person, without executing an explicit query -
- by calling <literal>aPerson.getEvents()</literal>. A Java collection, a <literal>Set</literal>, is used
- because the collection will not contain duplicate elements and the ordering is not relevant to our examples.
+ By adding a collection of events to the <literal>Person</literal>
+ class, you can easily navigate to the events for a particular person,
+ without executing an explicit query - by calling
+ <literal>Person#getEvents</literal>. Multi-valued associations
+ are represented in Hibernate by one of the Java Collection Framework
+ contracts; here we choose a <interfacename>java.util.Set</interfacename>
+ because the collection will not contain duplicate elements and the ordering
+ is not relevant to our examples:
</para>
- <para>
- We need unidirectional, many-valued associations, implemented with a <literal>Set</literal>.
- Write the code for this in the Java classes and then map it:
- </para>
-
<programlisting><![CDATA[public class Person {
private Set events = new HashSet();
@@ -949,17 +958,19 @@
}]]></programlisting>
<para>
- Before you map this association, consider the other side. You could just keep this
- unidirectional. Or, you could create another collection on the <literal>Event</literal>, if you
- want to be able to navigate it bi-directional, i.e., <literal>anEvent.getParticipants()</literal>.
- This is not necessary, from a functional perspective. You can always execute an explicit query
- to retrieve the participants for a particular event. This is a design choice left to you, but what
- is clear from this discussion is the multiplicity of the association: "many" valued on both sides
- is called a <emphasis>many-to-many</emphasis> association. Hence, we use Hibernate's
- many-to-many mapping:
+ Before mapping this association, let's consider the other side.
+ We could just keep this unidirectional or create another
+ collection on the <literal>Event</literal>, if we wanted to be
+ able to navigate it from both directions. This is not necessary,
+ from a functional perspective. You can always execute an explicit
+ query to retrieve the participants for a particular event. This
+ is a design choice left to you, but what is clear from this
+ discussion is the multiplicity of the association: "many" valued
+ on both sides is called a <emphasis>many-to-many</emphasis>
+ association. Hence, we use Hibernate's many-to-many mapping:
</para>
- <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <programlisting><![CDATA[<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
@@ -969,20 +980,24 @@
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
- <many-to-many column="EVENT_ID" class="events.Event"/>
+ <many-to-many column="EVENT_ID" class="Event"/>
</set>
</class>]]></programlisting>
<para>
- Hibernate supports a broad range of collection mappings, a <literal><set></literal> being most
- common. For a many-to-many association, or <emphasis>n:m</emphasis> entity relationship, an
- association table is required. Each row in this table represents a link between a person and an event.
- The table name is configured with the <literal>table</literal> attribute of the <literal>set</literal>
- element. The identifier column name in the association, for the person side, is defined with the
- <literal><key></literal> element, the column name for the event's side with the
- <literal>column</literal> attribute of the <literal><many-to-many></literal>. You also
- have to tell Hibernate the class of the objects in your collection (the class on the
+ Hibernate supports a broad range of collection mappings, a
+ <literal>set</literal> being most common. For a many-to-many
+ association, or <emphasis>n:m</emphasis> entity relationship, an
+ association table is required. Each row in this table represents
+ a link between a person and an event. The table name is
+ decalred using the <literal>table</literal> attribute of the
+ <literal>set</literal> element. The identifier column name in
+ the association, for the person side, is defined with the
+ <literal>key</literal> element, the column name for the event's
+ side with the <literal>column</literal> attribute of the
+ <literal>many-to-many</literal>. You also have to tell Hibernate
+ the class of the objects in your collection (the class on the
other side of the collection of references).
</para>
@@ -1013,73 +1028,77 @@
Now we will bring some people and events together in a new method in <literal>EventManager</literal>:
</para>
- <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+ <programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+ aPerson.getEvents().add(anEvent);
- Person aPerson = (Person) session.load(Person.class, personId);
- Event anEvent = (Event) session.load(Event.class, eventId);
+ session.getTransaction().commit();
+ }]]></programlisting>
- aPerson.getEvents().add(anEvent);
-
- session.getTransaction().commit();
-}]]></programlisting>
-
<para>
- After loading a <literal>Person</literal> and an <literal>Event</literal>, simply
- modify the collection using the normal collection methods. There is no explicit call
- to <literal>update()</literal> or <literal>save()</literal>; Hibernate automatically
- detects that the collection has been modified and needs to be updated. This is called <emphasis>automatic
- dirty checking</emphasis>. You can also try it by modifying the name or the date property of
- any of your objects. As long as they are in <emphasis>persistent</emphasis> state, that is, bound
- to a particular Hibernate <literal>Session</literal> (i.e. they have been just loaded or saved in
- a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The
- process of synchronizing the memory state with the database, usually only at the end of a unit of
- work, is called <emphasis>flushing</emphasis>. In our code, the unit of work ends with a commit,
- or rollback, of the database transaction. This is defined by the <literal>thread</literal> configuration
- option for the <literal>CurrentSessionContext</literal> class.
+ After loading a <literal>Person</literal> and an
+ <literal>Event</literal>, simply modify the collection using the
+ normal collection methods. There is no explicit call to
+ <literal>update()</literal> or <literal>save()</literal>;
+ Hibernate automatically detects that the collection has been modified
+ and needs to be updated. This is called
+ <emphasis>automatic dirty checking</emphasis>. You can also try
+ it by modifying the name or the date property of any of your
+ objects. As long as they are in <emphasis>persistent</emphasis>
+ state, that is, bound to a particular Hibernate
+ <interfacename>org.hibernate.Session</interfacename>, Hibernate
+ monitors any changes and executes SQL in a write-behind fashion.
+ The process of synchronizing the memory state with the database,
+ usually only at the end of a unit of work, is called
+ <emphasis>flushing</emphasis>. In our code, the unit of work
+ ends with a commit, or rollback, of the database transaction.
</para>
<para>
- You can load person and event in different units of work. Or you can modify an object
- outside of a <literal>Session</literal>, when it is not in persistent state (if it was persistent
- before, this state is called <emphasis>detached</emphasis>). You can even modify a collection when
- it is detached:
+ You can load person and event in different units of work. Or
+ you can modify an object outside of a
+ <interfacename>org.hibernate.Session</interfacename>, when it
+ is not in persistent state (if it was persistent before, this
+ state is called <emphasis>detached</emphasis>). You can even
+ modify a collection when it is detached:
</para>
- <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+ <programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+ Event anEvent = (Event) session.load(Event.class, eventId);
- Person aPerson = (Person) session
- .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
- .setParameter("pid", personId)
- .uniqueResult(); // Eager fetch the collection so we can use it detached
+ session.getTransaction().commit();
- Event anEvent = (Event) session.load(Event.class, eventId);
+ // End of first unit of work
- session.getTransaction().commit();
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
- // End of first unit of work
+ // Begin second unit of work
- aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+ session2.update(aPerson); // Reattachment of aPerson
- // Begin second unit of work
+ session2.getTransaction().commit();
+ }]]></programlisting>
- Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
- session2.beginTransaction();
-
- session2.update(aPerson); // Reattachment of aPerson
-
- session2.getTransaction().commit();
-}]]></programlisting>
-
<para>
- The call to <literal>update</literal> makes a detached object persistent again by binding it to a new unit of work, so any modifications you made to it while detached
- can be saved to the database. This includes any modifications (additions/deletions) you
- made to a collection of that entity object.
+ The call to <literal>update</literal> makes a detached object
+ persistent again by binding it to a new unit of work, so any
+ modifications you made to it while detached can be saved to
+ the database. This includes any modifications
+ (additions/deletions) you made to a collection of that entity
+ object.
</para>
<para>
@@ -1090,29 +1109,35 @@
returns it (you might have to modify some of the previous methods to return that identifier):
</para>
- <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
- Long eventId = mgr.createAndStoreEvent("My Event", new Date());
- Long personId = mgr.createAndStorePerson("Foo", "Bar");
- mgr.addPersonToEvent(personId, eventId);
- System.out.println("Added person " + personId + " to event " + eventId);
-}]]></programlisting>
+ <programlisting><![CDATA[ else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+ }]]></programlisting>
<para>
- This is an example of an association between two equally important classes, two entities.
- As mentioned earlier, there are other classes and types in a typical model, usually "less
- important". Some you have already seen, like an <literal>int</literal> or a <literal>String</literal>.
- We call these classes <emphasis>value types</emphasis>, and their instances <emphasis>depend</emphasis>
- on a particular entity. Instances of these types do not have their own identity, nor are they
- shared between entities. Two persons do not reference the same <literal>firstname</literal>
- object, even if they have the same first name. Value types cannot only be found in
- the JDK , but
- you can also write dependent classes yourself (<literal>Address</literal> or <literal>MonetaryAmount</literal>,
- for example). In fact, in a Hibernate application all JDK classes are considered value types.
+ This is an example of an association between two equally important
+ classes : two entities. As mentioned earlier, there are other
+ classes and types in a typical model, usually "less important".
+ Some you have already seen, like an <literal>int</literal> or a
+ <classname>java.lang.String</classname>. We call these classes
+ <emphasis>value types</emphasis>, and their instances
+ <emphasis>depend</emphasis> on a particular entity. Instances of
+ these types do not have their own identity, nor are they shared
+ between entities. Two persons do not reference the same
+ <literal>firstname</literal> object, even if they have the same
+ first name. Value types cannot only be found in the JDK , but
+ you can also write dependent classes yourself
+ such as an <literal>Address</literal> or
+ <literal>MonetaryAmount</literal> class. In fact, in a Hibernate
+ application all JDK classes are considered value types.
</para>
<para>
- You can also design a collection of value types. This is conceptually different from a
- collection of references to other entities, but looks almost the same in Java.
+ You can also design a collection of value types. This is
+ conceptually different from a collection of references to other
+ entities, but looks almost the same in Java.
</para>
</sect2>
@@ -1121,37 +1146,44 @@
<title>Collection of values</title>
<para>
- You can add a collection of value typed objects to the <literal>Person</literal> entity. If you want to
- store email addresses, the type you use is <literal>String</literal>, and the collection is
- again a <literal>Set</literal>:
+ Let's add a collection of email addresses to the
+ <literal>Person</literal> entity. This will be represented as a
+ <interfacename>java.util.Set</interfacename> of
+ <classname>java.lang.String</classname> instances:
</para>
- <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+ <programlisting><![CDATA[ private Set emailAddresses = new HashSet();
-public Set getEmailAddresses() {
- return emailAddresses;
-}
+ public Set getEmailAddresses() {
+ return emailAddresses;
+ }
-public void setEmailAddresses(Set emailAddresses) {
- this.emailAddresses = emailAddresses;
-}]]></programlisting>
+ public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+ }]]></programlisting>
<para>
The mapping of this <literal>Set</literal> is as follows:
</para>
- <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
- <key column="PERSON_ID"/>
- <element type="string" column="EMAIL_ADDR"/>
-</set>]]></programlisting>
+ <programlisting><![CDATA[ <set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+ </set>]]></programlisting>
<para>
- The difference compared with the earlier mapping is the <literal>element</literal> part which tells Hibernate that the collection
- does not contain references to another entity, but a collection of elements of type
- <literal>String</literal>. The lowercase name tells you it is a Hibernate mapping type/converter.
- Again the <literal>table</literal> attribute of the <literal>set</literal> element determines
- the table name for the collection. The <literal>key</literal> element defines the foreign-key column
- name in the collection table. The <literal>column</literal> attribute in the <literal>element</literal>
- element defines the column name where the <literal>String</literal> values will actually be stored.
+ The difference compared with the earlier mapping is the use of
+ the <literal>element</literal> part which tells Hibernate that the
+ collection does not contain references to another entity, but is
+ rather a collection whose elements are values types, here specifically
+ of type <literal>string</literal>. The lowercase name tells you
+ it is a Hibernate mapping type/converter. Again the
+ <literal>table</literal> attribute of the <literal>set</literal>
+ element determines the table name for the collection. The
+ <literal>key</literal> element defines the foreign-key column
+ name in the collection table. The <literal>column</literal>
+ attribute in the <literal>element</literal> element defines the
+ column name where the email address values will actually
+ be stored.
</para>
<para>
@@ -1183,24 +1215,21 @@
linking persons and events. It is the same code in Java:
</para>
- <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+ <programlisting><![CDATA[ private void addEmailToPerson(Long personId, String emailAddress) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session.load(Person.class, personId);
+ // adding to the emailAddress collection might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
- Person aPerson = (Person) session.load(Person.class, personId);
+ session.getTransaction().commit();
+ }]]></programlisting>
- // The getEmailAddresses() might trigger a lazy load of the collection
- aPerson.getEmailAddresses().add(emailAddress);
-
- session.getTransaction().commit();
-}]]></programlisting>
-
<para>
- This time we did not use a <emphasis>fetch</emphasis> query to initialize the collection.
- Hence, the call to its getter method will trigger an additional select to initialize
- it, so we can add an element to it. Monitor the SQL log and try to optimize this with
- an eager fetch.
+ This time we did not use a <emphasis>fetch</emphasis> query to
+ initialize the collection. Monitor the SQL log and try to
+ optimize this with an eager fetch.
</para>
</sect2>
@@ -1209,35 +1238,44 @@
<title>Bi-directional associations</title>
<para>
- Next you will map a bi-directional association. You will make the association between
- person and event work from both sides in Java. The database schema does not
- change, so you will still have many-to-many multiplicity. A relational database is more flexible
- than a network programming language, so it does not need a navigation
- direction; data can be viewed and retrieved in any possible way.
+ Next you will map a bi-directional association. You will make
+ the association between person and event work from both sides
+ in Java. The database schema does not change, so you will still
+ have many-to-many multiplicity.
</para>
+ <note>
+ <para>
+ A relational database is more flexible than a network
+ programming language, in that it does not need a navigation
+ direction; data can be viewed and retrieved in any possible
+ way.
+ </para>
+ </note>
+
<para>
- First, add a collection of participants to the <literal>Event</literal> Event class:
+ First, add a collection of participants to the
+ <literal>Event</literal> class:
</para>
- <programlisting><![CDATA[private Set participants = new HashSet();
+ <programlisting><![CDATA[ private Set participants = new HashSet();
-public Set getParticipants() {
- return participants;
-}
+ public Set getParticipants() {
+ return participants;
+ }
-public void setParticipants(Set participants) {
- this.participants = participants;
-}]]></programlisting>
+ public void setParticipants(Set participants) {
+ this.participants = participants;
+ }]]></programlisting>
<para>
Now map this side of the association in <literal>Event.hbm.xml</literal>.
</para>
- <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
- <key column="EVENT_ID"/>
- <many-to-many column="PERSON_ID" class="events.Person"/>
-</set>]]></programlisting>
+ <programlisting><![CDATA[ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>]]></programlisting>
<para>
These are normal <literal>set</literal> mappings in both mapping documents.
@@ -1273,23 +1311,23 @@
correctly set both sides (for example, in <literal>Person</literal>):
</para>
- <programlisting><![CDATA[protected Set getEvents() {
- return events;
-}
+ <programlisting><![CDATA[ protected Set getEvents() {
+ return events;
+ }
-protected void setEvents(Set events) {
- this.events = events;
-}
+ protected void setEvents(Set events) {
+ this.events = events;
+ }
-public void addToEvent(Event event) {
- this.getEvents().add(event);
- event.getParticipants().add(this);
-}
+ public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+ }
-public void removeFromEvent(Event event) {
- this.getEvents().remove(event);
- event.getParticipants().remove(this);
-}]]></programlisting>
+ public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+ }]]></programlisting>
<para>
The get and set methods for the collection are now protected. This allows classes in the
@@ -1309,7 +1347,6 @@
and in many-to-many association you can select either side.
</para>
- <para>In the next section we will turn this into a small web application.</para>
</sect2>
</sect1>
@@ -1328,50 +1365,51 @@
<title>Writing the basic servlet</title>
<para>
- Create a new class in your source directory in the <literal>events</literal>
- package:
+ First we need create our basic processing servlet. Since our
+ servlet only handles HTTP <literal>GET</literal> requests, we
+ will only implement the <literal>doGet()</literal> method:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial.web;
// Imports
public class EventManagerServlet extends HttpServlet {
- // Servlet code
-}]]></programlisting>
+ protected void doGet(
+ HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
- <para>
- The servlet handles HTTP <literal>GET</literal> requests only. It is for this reason that the method
- we implement is <literal>doGet()</literal>:
- </para>
+ SimpleDateFormat dateFormatter = new SimpleDateFormat( "dd.MM.yyyy" );
- <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
- SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+ // Process request and render page...
- try {
- // Begin unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().beginTransaction();
-
- // Process request and render page...
-
- // End unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().commit();
-
- } catch (Exception ex) {
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().rollback();
- throw new ServletException(ex);
+ // End unit of work
+ HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
+ }
+ catch (Exception ex) {
+ HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();
+ if ( ServletException.class.isInstance( ex ) ) {
+ throw ( ServletException ) ex;
+ }
+ else {
+ throw new ServletException( ex );
+ }
+ }
}
}]]></programlisting>
<para>
+ Save this servlet as
+ <filename>src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java</filename>
+ </para>
+
+ <para>
The pattern applied here is called <emphasis>session-per-request</emphasis>.
When a request hits the servlet, a new Hibernate <literal>Session</literal> is
opened through the first call to <literal>getCurrentSession()</literal> on the
@@ -1412,32 +1450,33 @@
Now you can implement the processing of the request and the rendering of the page.
</para>
-<programlisting><![CDATA[// Write HTML header
-PrintWriter out = response.getWriter();
-out.println("<html><head><title>Event Manager</title></head><body>");
+<programlisting><![CDATA[ // Write HTML header
+ PrintWriter out = response.getWriter();
+ out.println("<html><head><title>Event Manager</title></head><body>");
-// Handle actions
-if ( "store".equals(request.getParameter("action")) ) {
+ // Handle actions
+ if ( "store".equals(request.getParameter("action")) ) {
- String eventTitle = request.getParameter("eventTitle");
- String eventDate = request.getParameter("eventDate");
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
- if ( "".equals(eventTitle) || "".equals(eventDate) ) {
- out.println("<b><i>Please enter event title and date.</i></b>");
- } else {
- createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
- out.println("<b><i>Added event.</i></b>");
- }
-}
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ }
+ else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+ }
-// Print page
-printEventForm(out);
-listEvents(out, dateFormatter);
+ // Print page
+ printEventForm(out);
+ listEvents(out, dateFormatter);
-// Write HTML footer
-out.println("</body></html>");
-out.flush();
-out.close();]]></programlisting>
+ // Write HTML footer
+ out.println("</body></html>");
+ out.flush();
+ out.close();]]></programlisting>
<para>
This coding style, with a mix of Java and HTML, would not scale
@@ -1448,14 +1487,14 @@
trivial and only outputs HTML:
</para>
- <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
- out.println("<h2>Add new event:</h2>");
- out.println("<form>");
- out.println("Title: <input name='eventTitle' length='50'/><br/>");
- out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
- out.println("<input type='submit' name='action' value='store'/>");
- out.println("</form>");
-}]]></programlisting>
+ <programlisting><![CDATA[ private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+ }]]></programlisting>
<para>
The <literal>listEvents()</literal> method uses the Hibernate
@@ -1463,27 +1502,28 @@
a query:
</para>
- <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+ <programlisting><![CDATA[ private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
- List result = HibernateUtil.getSessionFactory()
- .getCurrentSession().createCriteria(Event.class).list();
- if (result.size() > 0) {
- out.println("<h2>Events in database:</h2>");
- out.println("<table border='1'>");
- out.println("<tr>");
- out.println("<th>Event title</th>");
- out.println("<th>Event date</th>");
- out.println("</tr>");
- for (Iterator it = result.iterator(); it.hasNext();) {
- Event event = (Event) it.next();
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
out.println("<tr>");
- out.println("<td>" + event.getTitle() + "</td>");
- out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
out.println("</tr>");
+ Iterator it = result.iterator();
+ while (it.hasNext()) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
}
- out.println("</table>");
- }
-}]]></programlisting>
+ }]]></programlisting>
<para>
Finally, the <literal>store</literal> action is dispatched to the
@@ -1491,14 +1531,14 @@
the <literal>Session</literal> of the current thread:
</para>
- <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
- Event theEvent = new Event();
- theEvent.setTitle(title);
- theEvent.setDate(theDate);
+ <programlisting><![CDATA[ protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
- HibernateUtil.getSessionFactory()
- .getCurrentSession().save(theEvent);
-}]]></programlisting>
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+ }]]></programlisting>
<para>
The servlet is now complete. A request to the servlet will be processed
@@ -1517,26 +1557,11 @@
<title>Deploying and testing</title>
<para>
- To deploy this application you have to create a web archive, or what is known as a WAR. Add the
- following Ant target to your <literal>build.xml</literal>:
+ To deploy this application for testing we must create a
+ Web ARchive (WAR). First we must define the WAR descriptor
+ as <filename>src/main/webapp/WEB-INF/web.xml</filename>
</para>
-<programlisting><![CDATA[<target name="war" depends="compile">
- <war destfile="hibernate-tutorial.war" webxml="web.xml">
- <lib dir="${librarydir}">
- <exclude name="jsdk*.jar"/>
- </lib>
-
- <classes dir="${targetdir}"/>
- </war>
-</target>]]></programlisting>
-
- <para>
- This target creates a file called <literal>hibernate-tutorial.war</literal>
- in your project directory. It packages all libraries and the <literal>web.xml</literal>
- descriptor, which is expected in the base directory of your project:
- </para>
-
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
@@ -1545,7 +1570,7 @@
<servlet>
<servlet-name>Event Manager</servlet-name>
- <servlet-class>events.EventManagerServlet</servlet-class>
+ <servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
@@ -1555,20 +1580,19 @@
</web-app>]]></programlisting>
<para>
- Before you compile and deploy the web application, an additional library
- is required: <literal>jsdk.jar</literal>. This is the Java servlet development kit.
- If you do not have this library already, get it from the Sun website and copy it to
- your library directory. However, it will be only used for compilation and excluded
- from the WAR package.
+ To build and deploy call <literal>mvn package</literal> in your
+ project directory and copy the <filename>hibernate-tutorial.war</filename>
+ file into your Tomcat <filename>webapps</filename> directory.
</para>
- <para>
- To build and deploy call <literal>ant war</literal> in your project directory
- and copy the <literal>hibernate-tutorial.war</literal> file into your Tomcat
- <literal>webapp</literal> directory. If you do not have Tomcat installed, download
- it and follow the installation instructions. You do not have to change any Tomcat
- configuration to deploy this application though.
- </para>
+ <note>
+ <para>
+ If you do not have Tomcat installed, download it from
+ <ulink url="http://tomcat.apache.org/"/> and follow the
+ installation instructions. Our application requires
+ no changes to the standard Tomcat configuration.
+ </para>
+ </note>
<para>
Once deployed and Tomcat is running, access the application at
@@ -1587,21 +1611,10 @@
<para>
This tutorial covered the basics of writing a simple standalone Hibernate application
- and a small web application.
+ and a small web application. More tutorials are available from the Hibernate
+ <ulink url="http://hibernate.org">website</ulink>.
</para>
- <para>
- If you already feel confident with Hibernate, continue browsing through the reference
- documentation table of contents for topics you find interesting. The most popular are
- transactional processing (<xref linkend="transactions"/>), fetch
- performance (<xref linkend="performance"/>), or the usage of the API (<xref linkend="objectstate"/>)
- and the query features (<xref linkend="objectstate-querying"/>).
- </para>
-
- <para>
- More tutorials are available from the Hibernate website.
- </para>
-
</sect1>
</chapter>
15 years, 7 months
Hibernate SVN: r16741 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2009-06-10 13:17:48 -0400 (Wed, 10 Jun 2009)
New Revision: 16741
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml
Log:
HHH-3953 - Update tutorial chapter
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml 2009-06-10 14:27:12 UTC (rev 16740)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/tutorial.xml 2009-06-10 17:17:48 UTC (rev 16741)
@@ -27,25 +27,17 @@
<!ENTITY mdash "-">
]>
- <!-- todo : need separate sections, one for each tutorial -->
-
<chapter id="tutorial">
- <title>Introduction to Hibernate</title>
-
- <sect1 id="tutorial-intro">
- <title>Preface</title>
+ <title>Tutorial</title>
- <para>
- Intended for new users, this chapter provides an introduction to Hibernate
- The chapter provides a step-by-step tutorial, starting with a simple
- application using an in-memory database. The tutorial is
- based on an earlier tutorial developed by Michael Gloegl. All
- code is contained in the <filename>tutorials/web</filename> directory
- of the project source.
- </para>
+ <para>
+ Intended for new users, this chapter provides an step-by-step introduction
+ to Hibernate, starting with a simple application using an in-memory database. The
+ tutorial is based on an earlier tutorial developed by Michael Gloegl. All
+ code is contained in the <filename>tutorials/web</filename> directory of the project
+ source.
+ </para>
- </sect1>
-
<important>
<para>
This tutorial expects the user have knowledge of both Java and
@@ -57,11 +49,9 @@
<note>
<para>
- There is another tutorial/example application in the
- <filename>/tutorials/eg</filename> directory of the project source.
- That example is console-based and, as such, would not have the
- dependency on a servlet container to execute. The basic setup is
- the same as the instructions below.
+ The distribution contains another example application under
+ the <filename>tutorial/eg</filename> project source
+ directory.
</para>
</note>
@@ -70,34 +60,56 @@
<para>
For this example, we will set up a small database application that can store
- events we want to attend, and information about the host(s) of
- these events. Although you can use whatever database you feel comfortable using, we will use an in-memory, Java database named HSQLDB
- to avoid describing installation/setup of any particular database
- servers.
+ events we want to attend and information about the host(s) of these events.
</para>
-
- <para>
- The first thing we need to do is set up the development environment,
- including all the required dependencies to Hibernate,
- as well as other libraries. Hibernate is built using Maven which,
- amongst other features, provides <literal>dependency management</literal>;
- moreover it provides <emphasis>transitive</emphasis>
- <literal>dependency management</literal>, which simply means that by using
- Hibernate we can define our dependency on Hibernate. Hibernate
- itself defines the dependencies it needs which then become transitive
- dependencies of our project.
- </para>
- <programlisting><![CDATA[.
-<project xmlns="http://maven.apache.org/POM/4.0.0"
+ <note>
+ <para>
+ Although you can use whatever database you feel comfortable using, we
+ will use <ulink url="http://hsqldb.org/">HSQLDB</ulink> (an in-memory,
+ Java database) to avoid describing installation/setup of any particular
+ database servers.
+ </para>
+ </note>
+
+ <sect2 id="tutorial-firstapp-setup">
+ <title>Setup</title>
+
+ <para>
+ The first thing we need to do is to set up the development environment. We
+ will be using the "standard layout" advocated by alot of build tools such
+ as <ulink url="http://maven.org">Maven</ulink>. Maven, in particular, has a
+ good resource describing this <ulink url="http://maven.apache.org/guides/introduction/introduction-to-the-standard-...">layout</ulink>.
+ As this tutorial is to be a web application, we will be creating and making
+ use of <filename>src/main/java</filename>, <filename>src/main/resources</filename>
+ and <filename>src/main/webapp</filename> directories.
+ </para>
+
+ <para>
+ We will be using Maven in this tutorial, taking advantage of its
+ transitive dependency management capabilities as well as the ability of
+ many IDEs to automatically set up a project for us based on the maven descriptor.
+ </para>
+
+ <programlisting><![CDATA[<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- ...
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.hibernate.tutorials</groupId>
+ <artifactId>hibernate-tutorial</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <name>First Hibernate Tutorial</name>
+
+ <build>
+ <!-- we dont want the version to be part of the generated war file name -->
+ <finalName>${artifactId}</finalName>
+ </build>
+
<dependencies>
<dependency>
- <groupId>${groupId}</groupId>
+ <groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
@@ -106,34 +118,52 @@
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
+
+ <!-- Hibernate uses slf4j for logging, for our purposes here use the simple backend -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+
+ <!-- Hibernate gives you a choice of bytecode providers between cglib and javassist -->
+ <dependency>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </dependency>
</dependencies>
</project>]]></programlisting>
- <note>
- <para>
- Essentially we are describing here the
- <filename>/tutorials/web/pom.xml</filename> file. See the
- <ulink url="http://maven.org">Maven</ulink> site for more information.
- </para>
- </note>
+ <tip>
+ <para>
+ It is not a requirement to use Maven. If you wish to use something else to
+ build this tutoial (such as Ant), the layout will remain the same. The only
+ change is that you will need to manually account for all the needed
+ dependencies. If you use something like <ulink url="http://ant.apache.org/ivy/">Ivy</ulink>
+ providing transitive dependency management you would still use the dependencies
+ mentioned below. Otherwise, you'd need to grab <emphasis>all</emphasis>
+ dependencies, both explicit and transitive, and add them to the project's
+ classpath. If working from the Hibernate distribution bundle, this would mean
+ <filename>hibernate3.jar</filename>, all artifacts in the
+ <filename>lib/required</filename> directory and all files from either the
+ <filename>lib/bytecode/cglib</filename> or <filename>lib/bytecode/javassist</filename>
+ directory; additionally you will need both the servlet-api jar and one of the slf4j
+ logging backends.
+ </para>
+ </tip>
- <tip>
<para>
- While not strictly necessary, most IDEs have integration with Maven
- to read these POM files and automatically set up a project for you.
+ Save this file as <filename>pom.xml</filename> in the project root directory.
</para>
- </tip>
+ </sect2>
- <para>
- Next, create a class that represents the event you want to store in the database.
- </para>
-
+
<sect2 id="tutorial-firstapp-firstclass">
<title>The first class</title>
<para>
- The first persistent class is a simple JavaBean class with some properties:
+ Next, we create a class that represents the event we want to store in the
+ database; it is a simple JavaBean class with some properties:
</para>
<programlisting><![CDATA[package org.hibernate.tutorial.domain;
@@ -175,57 +205,50 @@
<para>
This class uses standard JavaBean naming conventions for property
- getter and setter methods, as well as private visibility for the fields. Although this is
- a recommended design, it is not required. Hibernate can also access fields directly,
- the benefit of accessor methods is robustness for refactoring. The no-argument
- constructor is required to instantiate an object of this class through reflection.
+ getter and setter methods, as well as private visibility for the
+ fields. Although this is the recommended design, it is not required.
+ Hibernate can also access fields directly, the benefit of accessor
+ methods is robustness for refactoring.
</para>
<para>
- The <literal>id</literal> property holds a unique identifier value for a particular event.
- All persistent entity classes (there are less important dependent classes as well) will need
- such an identifier property if we want to use the full feature set of Hibernate. In fact,
- most applications, especially web applications, need to distinguish objects by identifier, so you
- should consider this a feature rather than a limitation. However, we usually do not manipulate
- the identity of an object, hence the setter method should be private. Only Hibernate will assign
- identifiers when an object is saved. Hibernate can access public, private,
- and protected accessor methods, as well as public, private and protected fields directly. The
- choice is up to you and you can match it to fit your application design.
+ The <literal>id</literal> property holds a unique identifier value
+ for a particular event. All persistent entity classes (there are
+ less important dependent classes as well) will need such an identifier
+ property if we want to use the full feature set of Hibernate. In fact,
+ most applications, especially web applications, need to distinguish
+ objects by identifier, so you should consider this a feature rather
+ than a limitation. However, we usually do not manipulate the identity
+ of an object, hence the setter method should be private. Only Hibernate
+ will assign identifiers when an object is saved. Hibernate can access
+ public, private, and protected accessor methods, as well as public,
+ private and protected fields directly. The choice is up to you and
+ you can match it to fit your application design.
</para>
<para>
- The no-argument constructor is a requirement for all persistent classes; Hibernate
- has to create objects for you, using Java Reflection. The constructor can be
- private, however, package visibility is required for runtime proxy generation and
- efficient data retrieval without bytecode instrumentation.
+ The no-argument constructor is a requirement for all persistent
+ classes; Hibernate has to create objects for you, using Java
+ Reflection. The constructor can be private, however package or public
+ visibility is required for runtime proxy generation and efficient data
+ retrieval without bytecode instrumentation.
</para>
<para>
- Place the Java source file in a directory called <literal>src</literal> in the
- development folder, and in its correct package. The directory should now look like this:
+ Save this file to the <filename>src/main/java/org/hibernate/tutorial/domain</filename>
+ directory.
</para>
-
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java]]></programlisting>
-
- <para>
- In the next step, we tell Hibernate about this persistent class.
- </para>
-
</sect2>
<sect2 id="tutorial-firstapp-mapping">
<title>The mapping file</title>
<para>
- Hibernate needs to know how to load and store objects of the persistent class.
- This is where the Hibernate mapping file comes into play. The mapping file
- tells Hibernate what table in the database it has to access, and what columns
- in that table it should use.
+ Hibernate needs to know how to load and store objects of the
+ persistent class. This is where the Hibernate mapping file
+ comes into play. The mapping file tells Hibernate what table in
+ the database it has to access, and what columns in that table
+ it should use.
</para>
<para>
@@ -237,25 +260,28 @@
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
-<hibernate-mapping>
+<hibernate-mapping package="org.hibernate.tutorial.domain">
[...]
</hibernate-mapping>]]></programlisting>
<para>
- Hibernate DTD is sophisticated. You can use it for
- auto-completion of XML mapping elements and attributes in your editor or
- IDE. Opening up the DTD file in your text editor is the
- easiest way to get an overview of all elements and attributes, and to view
- the defaults, as well as some comments. Hibernate will not
- load the DTD file from the web, but first look it up from the classpath
- of the application. The DTD file is included in <literal>hibernate3.jar</literal>
- as well as in the <literal>src/</literal> directory of the Hibernate distribution.
+ Hibernate DTD is sophisticated. You can use it for auto-completion
+ of XML mapping elements and attributes in your editor or IDE.
+ Opening up the DTD file in your text editor is the easiest way to
+ get an overview of all elements and attributes, and to view the
+ defaults, as well as some comments. Hibernate will not load the
+ DTD file from the web, but first look it up from the classpath of
+ the application. The DTD file is included in
+ <filename>hibernate-core.jar</filename> (it is also included in the
+ <filename>hibernate3.jar</filename>, if using the distribution bundle).
</para>
- <para>
- We will omit the DTD declaration in future examples to shorten the code. It is,
- of course, not optional.
- </para>
+ <important>
+ <para>
+ We will omit the DTD declaration in future examples to shorten the code. It is,
+ of course, not optional.
+ </para>
+ </important>
<para>
Between the two <literal>hibernate-mapping</literal> tags, include a
@@ -264,25 +290,27 @@
a mapping to a table in the SQL database:
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <programlisting><![CDATA[<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
</class>
</hibernate-mapping>]]></programlisting>
<para>
- So far we have told Hibernate how to persist and load object of class <literal>Event</literal>
- to the table <literal>EVENTS</literal>. Each instance is now represented by a row in that table.
- Now we can continue by mapping the unique identifier property to the tables primary key.
- As we do not want to care about handling this identifier, we configure Hibernate's
+ So far we have told Hibernate how to persist and load object of
+ class <literal>Event</literal> to the table
+ <literal>EVENTS</literal>. Each instance is now represented by a
+ row in that table. Now we can continue by mapping the unique
+ identifier property to the tables primary key. As we do not want
+ to care about handling this identifier, we configure Hibernate's
identifier generation strategy for a surrogate primary key column:
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <programlisting><![CDATA[<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
@@ -291,29 +319,43 @@
</hibernate-mapping>]]></programlisting>
<para>
- The <literal>id</literal> element is the declaration of the identifier property,
- <literal>name="id"</literal> declares the name of the Java property.
- Hibernate will use the getter and setter methods to access the property.
- The column attribute tells Hibernate which column of the
- <literal>EVENTS</literal> table to use for this primary key. The nested
- <literal>generator</literal> element specifies the identifier generation strategy.
-
- In this case you used <literal>native</literal>, which selects the best strategy depending
- on the configured database dialect. Hibernate supports database generated, globally
- unique, as well as application assigned, identifiers. Hibernate supports any strategy you have written
- an extension for.
+ The <literal>id</literal> element is the declaration of the
+ identifier property. The <literal>name="id"</literal> mapping
+ attribute declares the name of the JavaBean property and tells
+ Hibernate to use the <literal>getId()</literal> and
+ <literal>setId()</literal> methods to access the property. The
+ column attribute tells Hibernate which column of the
+ <literal>EVENTS</literal> table holds the primary key value.
</para>
<para>
- Finally, include declarations for the persistent properties of the class in
- the mapping file. By default, no properties of the class are considered
+ The nested <literal>generator</literal> element specifies the
+ identifier generation strategy (aka how are identifier values
+ generated?). In this case we choose <literal>native</literal>,
+ which offers a level of portability depending on the configured
+ database dialect. Hibernate supports database generated, globally
+ unique, as well as application assigned, identifiers. Identifier
+ value generation is also one of Hibernate's many extension points
+ and you can plugin in your own strategy.
+ </para>
+
+ <tip>
+ <para>
+ <literal>native</literal> is no longer consider the best strategy in terms of portability. for further
+ discussion, see <xref linkend="portability-idgen"/>
+ </para>
+ </tip>
+
+ <para>
+ Lastly, we need to tell Hibernate about the remaining entity class
+ properties. By default, no properties of the class are considered
persistent:
</para>
<programlisting><![CDATA[
-<hibernate-mapping>
+<hibernate-mapping package="org.hibernate.tutorial.domain">
- <class name="events.Event" table="EVENTS">
+ <class name="Event" table="EVENTS">
<id name="id" column="EVENT_ID">
<generator class="native"/>
</id>
@@ -324,27 +366,31 @@
</hibernate-mapping>]]></programlisting>
<para>
- Similar to the <literal>id</literal> element, the <literal>name</literal>
- attribute of the <literal>property</literal> element tells Hibernate which getter
- and setter methods to use. In this case, Hibernate will search for
- <literal>getDate()/setDate()</literal>, as well as <literal>getTitle()/setTitle()</literal>.
+ Similar to the <literal>id</literal> element, the
+ <literal>name</literal> attribute of the
+ <literal>property</literal> element tells Hibernate which getter
+ and setter methods to use. In this case, Hibernate will search
+ for <literal>getDate()</literal>, <literal>setDate()</literal>,
+ <literal>getTitle()</literal> and <literal>setTitle()</literal>
+ methods.
</para>
- <para>
- Why does the <literal>date</literal> property mapping include the
- <literal>column</literal> attribute, but the <literal>title</literal>
- does not? Without the <literal>column</literal> attribute, Hibernate
- by default uses the property name as the column name. This works for
- <literal>title</literal>, however, <literal>date</literal> is a reserved
- keyword in most databases so you will need to map it to a different name.
- </para>
+ <note>
+ <para>
+ Why does the <literal>date</literal> property mapping include the
+ <literal>column</literal> attribute, but the <literal>title</literal>
+ does not? Without the <literal>column</literal> attribute, Hibernate
+ by default uses the property name as the column name. This works for
+ <literal>title</literal>, however, <literal>date</literal> is a reserved
+ keyword in most databases so you will need to map it to a different name.
+ </para>
+ </note>
<para>
- The <literal>title</literal> mapping also lacks
- a <literal>type</literal> attribute. The types declared and used in the mapping
- files are not, as you might expect, Java data types. They are also not SQL
- database types. These types are called <emphasis>Hibernate mapping types</emphasis>,
- converters which can translate from Java to SQL data types and vice versa. Again,
+ The <literal>title</literal> mapping also lacks a <literal>type</literal> attribute. The
+ types declared and used in the mapping files are not Java data types; they are not SQL
+ database types either. These types are called <emphasis>Hibernate mapping types</emphasis>,
+ converters which can translate from Java to SQL data types and vice versa. Again,
Hibernate will try to determine the correct conversion and mapping type itself if
the <literal>type</literal> attribute is not present in the mapping. In some cases this
automatic detection using Reflection on the Java class might not have the default you
@@ -355,59 +401,64 @@
<literal>timestamp</literal> converter.
</para>
+ <tip>
+ <para>
+ Hibernate makes this mapping type determination using reflection when the mapping files
+ are processed. This can take time and resources, so if startup performance is important
+ you should consider explicitly defining the type to use.
+ </para>
+ </tip>
+
<para>
- Save the mapping file as <literal>Event.hbm.xml</literal> in
- the directory next to the <literal>Event</literal> Java class source file.
- The naming of mapping files can be arbitrary, however, the <literal>hbm.xml</literal>
- suffix is a convention in the Hibernate developer community. The directory structure
- should now look like this:
+ Save this mapping file as
+ <filename>src/main/resources/org/hibernate/tutorial/domain/Event.hbm.xml</filename>.
</para>
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java
- Event.hbm.xml]]></programlisting>
-
- <para>
- The next section continues with the main configuration of Hibernate.
- </para>
-
</sect2>
<sect2 id="tutorial-firstapp-configuration" revision="2">
<title>Hibernate configuration</title>
<para>
- You should have a persistent class and its mapping file in place. It is now time to configure
- Hibernate. Before you do this, you will need a database. HSQL DB, a java-based SQL DBMS,
- can be downloaded from the HSQL DB website(http://hsqldb.org/). You only need the <literal>hsqldb.jar</literal>
- from this download. Place this file in the <literal>lib/</literal> directory of the
- development folder.
+ At this point, you should have the persistent class and its mapping
+ file in place. It is now time to configure Hibernate. First let's set up
+ HSQLDB to run in "server mode"
</para>
+ <note>
+ <para>
+ We do this do that the data remains between runs.
+ </para>
+ </note>
+
<para>
- Create a directory called <literal>data</literal> in the root of the development directory.
- This is where HSQL DB will store its data files. Start the database by running
- <literal>java -classpath ../lib/hsqldb.jar org.hsqldb.Server</literal> in this data directory.
- You will see it start up and bind to a TCP/IP socket; this is where our application
- will connect later. If you want to start with a fresh database during this tutorial,
- shutdown HSQL DB (press <literal>CTRL + C</literal> in the window), delete all files in the
- <literal>data/</literal> directory, and start HSQL DB again.
+ We will utilize the Maven exec plugin to launch the HSQLDB server
+ by running:
+ <command> mvn exec:java -Dexec.mainClass="org.hsqldb.Server" -Dexec.args="-database.0 file:target/data/tutorial"</command>
+ You will see it start up and bind to a TCP/IP socket; this is where
+ our application will connect later. If you want to start
+ with a fresh database during this tutorial, shutdown HSQLDB, delete
+ all files in the <filename>target/data</filename> directory,
+ and start HSQLDB again.
</para>
<para>
- Hibernate is the layer in your application that connects to this database, so it needs
- connection information. The connections are made through a JDBC connection pool, which you
- will also have to configure. The Hibernate distribution contains several open source JDBC connection
- pooling tools, but you will use the Hibernate built-in connection pool for this tutorial.
- You will have to copy the required library into your classpath and use different
- connection pooling settings if you want to use a production-quality third party
- JDBC pooling software.
+ Hibernate will be connecting to the database on behalf of your application, so it needs to know
+ how to obtain connections. For this tutorial we will be using a standalone connection
+ pool (as opposed to a <interfacename>javax.sql.DataSource</interfacename>). Hibernate comes with
+ support for two third-party open source JDBC connection pools:
+ <ulink url="https://sourceforge.net/projects/c3p0">c3p0</ulink> and
+ <ulink url="http://proxool.sourceforge.net/">proxool</ulink>. However, we will be using the
+ Hibernate built-in connection pool for this tutorial.
</para>
+ <caution>
+ <para>
+ The built-in Hibernate connection pool is in no way intended for production use. It
+ lacks several features found on any decent connection pool.
+ </para>
+ </caution>
+
<para>
For Hibernate's configuration, we can use a simple <literal>hibernate.properties</literal> file, a
more sophisticated <literal>hibernate.cfg.xml</literal> file, or even complete
@@ -445,19 +496,22 @@
<property name="show_sql">true</property>
<!-- Drop and re-create the database schema on startup -->
- <property name="hbm2ddl.auto">create</property>
+ <property name="hbm2ddl.auto">update</property>
- <mapping resource="events/Event.hbm.xml"/>
+ <mapping resource="org/hibernate/tutorial/domain/Event.hbm.xml"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
+ <note>
+ <para>Notice that this configuration file specifies a different DTD</para>
+ </note>
+
<para>
- This XML configuration uses a different DTD. You configure
- Hibernate's <literal>SessionFactory</literal>. SessionFactory is a global factory responsible
- for a particular database. If you have several databases, for easier startup you should use several
- <literal><session-factory></literal> configurations in
+ You configure Hibernate's <literal>SessionFactory</literal>. SessionFactory is a global
+ factory responsible for a particular database. If you have several databases, for easier
+ startup you should use several <literal><session-factory></literal> configurations in
several configuration files.
</para>
@@ -465,9 +519,18 @@
The first four <literal>property</literal> elements contain the necessary
configuration for the JDBC connection. The dialect <literal>property</literal>
element specifies the particular SQL variant Hibernate generates.
- Hibernate's automatic session management for persistence contexts is particularly useful in this context.
+ </para>
- The <literal>hbm2ddl.auto</literal> option turns on automatic generation of
+ <tip>
+ <para>
+ In most cases, Hibernate is able to properly determine which dialect to use. See
+ <xref linkend="portability-dialectresolver"/> for more information.
+ </para>
+ </tip>
+
+ <para>
+ Hibernate's automatic session management for persistence contexts is particularly useful
+ in this context. The <literal>hbm2ddl.auto</literal> option turns on automatic generation of
database schemas directly into the database. This can also be turned
off by removing the configuration option, or redirected to a file with the help of
the <literal>SchemaExport</literal> Ant task. Finally, add the mapping file(s)
@@ -475,116 +538,83 @@
</para>
<para>
- Copy the file into the source directory, so it will end up in the
- root of the classpath. On startup, Hibernate automatically searches for a file called
- <literal>hibernate.cfg.xml</literal> in the root of the classpath.
+ Save this file as <filename>hibernate.cfg.xml</filename> into the
+ <filename>src/main/resources</filename> directory.
</para>
</sect2>
- <sect2 id="tutorial-firstapp-ant" revision="1">
- <title>Building with Ant</title>
+ <sect2 id="tutorial-firstapp-mvn" revision="1">
+ <title>Building with Maven</title>
<para>
- We will now build the tutorial with Ant. You will need to have Ant installed.
- It is available from the <ulink url="http://ant.apache.org/bindownload.cgi">Ant download page</ulink>.
- Installation instructions for Ant, however, are not be covered here. Please refer to the
- <ulink url="http://ant.apache.org/manual/index.html">Ant manual</ulink> for further information. After you
- have installed Ant, you can start to create the buildfile. It will be called
- <literal>build.xml</literal> and placed directly in the development directory.
+ We will now build the tutorial with Maven. You will need to
+ have Maven installed; it is available from the
+ <ulink url="http://maven.apache.org/download.html">Maven download page</ulink>.
+ Maven will read the <filename>/pom.xml</filename> file we created
+ earlier and know how to perform some basic project tasks. First,
+ lets run the <literal>compile</literal> goal to make sure we can compile
+ everything so far:
</para>
- <para>
- A basic build file looks like this:
- </para>
+ <programlisting><![CDATA[[hibernateTutorial]$ mvn compile
+[INFO] Scanning for projects...
+[INFO] ------------------------------------------------------------------------
+[INFO] Building First Hibernate Tutorial
+[INFO] task-segment: [compile]
+[INFO] ------------------------------------------------------------------------
+[INFO] [resources:resources]
+[INFO] Using default encoding to copy filtered resources.
+[INFO] [compiler:compile]
+[INFO] Compiling 1 source file to /home/steve/projects/sandbox/hibernateTutorial/target/classes
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESSFUL
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 2 seconds
+[INFO] Finished at: Tue Jun 09 12:25:25 CDT 2009
+[INFO] Final Memory: 5M/547M
+[INFO] ------------------------------------------------------------------------]]></programlisting>
- <programlisting><![CDATA[<project name="hibernate-tutorial" default="compile">
-
- <property name="sourcedir" value="${basedir}/src"/>
- <property name="targetdir" value="${basedir}/bin"/>
- <property name="librarydir" value="${basedir}/lib"/>
-
- <path id="libraries">
- <fileset dir="${librarydir}">
- <include name="*.jar"/>
- </fileset>
- </path>
-
- <target name="clean">
- <delete dir="${targetdir}"/>
- <mkdir dir="${targetdir}"/>
- </target>
-
- <target name="compile" depends="clean, copy-resources">
- <javac srcdir="${sourcedir}"
- destdir="${targetdir}"
- classpathref="libraries"/>
- </target>
-
- <target name="copy-resources">
- <copy todir="${targetdir}">
- <fileset dir="${sourcedir}">
- <exclude name="**/*.java"/>
- </fileset>
- </copy>
- </target>
-
-</project>]]></programlisting>
-
- <para>
- This will tell Ant to add all files in the library directory ending with <literal>.jar</literal>
- to the classpath used for compilation. It will also copy all non-Java source files to the
- target directory, configuration and Hibernate mapping files, for example. If you now run Ant, you
- will get this output:
- </para>
-
- <programlisting><![CDATA[C:\hibernateTutorial\>ant
-Buildfile: build.xml
-
-copy-resources:
- [copy] Copying 2 files to C:\hibernateTutorial\bin
-
-compile:
- [javac] Compiling 1 source file to C:\hibernateTutorial\bin
-
-BUILD SUCCESSFUL
-Total time: 1 second ]]></programlisting>
-
</sect2>
<sect2 id="tutorial-firstapp-helpers" revision="3">
<title>Startup and helpers</title>
<para>
- It is time to load and store some <literal>Event</literal> objects, but first
- you have to complete the setup with some infrastructure code. You have to startup
- Hibernate by building a global <literal>SessionFactory</literal>
- object and storing it somewhere for easy access in application code.
- A <literal>SessionFactory</literal> can open up new <literal>Session</literal>s.
- A <literal>Session</literal> represents a single-threaded unit of work. The
- <literal>SessionFactory</literal> is a thread-safe global object, that is instantiated once.
+ It is time to load and store some <literal>Event</literal>
+ objects, but first you have to complete the setup with some
+ infrastructure code. You have to startup Hibernate by building
+ a global <interfacename>org.hibernate.SessionFactory</interfacename>
+ object and storing it somewhere for easy access in application code. A
+ <interfacename>org.hibernate.SessionFactory</interfacename> is used to
+ obtain <interfacename>org.hibernate.Session</interfacename> instances.
+ A <interfacename>org.hibernate.Session</interfacename> represents a
+ single-threaded unit of work. The
+ <interfacename>org.hibernate.SessionFactory</interfacename> is a
+ thread-safe global object that is instantiated once.
</para>
<para>
- Create a <literal>HibernateUtil</literal> helper class that takes care
- of startup and makes accessing a <literal>SessionFactory</literal> convenient.
- The following example illustrates the implementation:
+ We will create a <literal>HibernateUtil</literal> helper class that
+ takes care of startup and makes accessing the
+ <interfacename>org.hibernate.SessionFactory</interfacename> more convenient.
</para>
- <programlisting><![CDATA[package util;
+ <programlisting><![CDATA[package org.hibernate.tutorial.util;
-import org.hibernate.*;
-import org.hibernate.cfg.*;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.Configuration;
public class HibernateUtil {
- private static final SessionFactory sessionFactory;
+ private static final SessionFactory sessionFactory = buildSessionFactory();
- static {
+ private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
- sessionFactory = new Configuration().configure().buildSessionFactory();
- } catch (Throwable ex) {
+ return new Configuration().configure().buildSessionFactory();
+ }
+ catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
@@ -598,39 +628,28 @@
}]]></programlisting>
<para>
- This class not only produces the global <literal>SessionFactory</literal> in
- its static initializer that is called once by the JVM when the class is loaded. It also
- hides the fact that it uses a static singleton. It might as well lookup the
- <literal>SessionFactory</literal> from JNDI in an application server.
+ Save this code as
+ <filename>src/main/java/org/hibernate/tutorial/util/HibernateUtil.java</filename>
</para>
<para>
- If you give the <literal>SessionFactory</literal> a name in your configuration
- file, Hibernate will try to bind it to JNDI after it has been built.
- To avoid this code completely you could also use JMX deployment and let the
- JMX-capable container instantiate and bind a <literal>HibernateService</literal>
- to JNDI. These advanced options are discussed in the Hibernate reference
- documentation.
+ This class not only produces the global
+ <interfacename>org.hibernate.SessionFactory</interfacename> reference in
+ its static initializer; it also hides the fact that it uses a
+ static singleton. We might just as well have looked up the
+ <interfacename>org.hibernate.SessionFactory</interfacename> reference from
+ JNDI in an application server or any other location for that matter.
</para>
<para>
- Place <literal>HibernateUtil.java</literal> in the development source directory in
- a package next to <literal>events</literal>:
+ If you give the <interfacename>org.hibernate.SessionFactory</interfacename>
+ a name in your configuration, Hibernate will try to bind it to
+ JNDI under that name after it has been built. Another, better option is to
+ use a JMX deployment and let the JMX-capable container instantiate and bind
+ a <literal>HibernateService</literal> to JNDI. Such advanced options are
+ discussed later.
</para>
- <programlisting><![CDATA[.
-+lib
- <Hibernate and third-party libraries>
-+src
- +events
- Event.java
- Event.hbm.xml
- +util
- HibernateUtil.java
- hibernate.cfg.xml
-+data
-build.xml]]></programlisting>
-
<para>
You now need to configure a logging
system. Hibernate uses commons logging and provides two choices: Log4j and
@@ -652,16 +671,19 @@
<title>Loading and storing objects</title>
<para>
- You can use Hibernate to load and store objects. Write an
- <literal>EventManager</literal> class with a <literal>main()</literal> method:
+ We are now ready to start doing some real worjk with Hibernate.
+ Let's start by writing an <literal>EventManager</literal> class
+ with a <literal>main()</literal> method:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial;
+
import org.hibernate.Session;
-import java.util.Date;
+import java.util.*;
-import util.HibernateUtil;
+import org.hibernate.tutorial.domain.Event;
+import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
@@ -676,15 +698,12 @@
}
private void createAndStoreEvent(String title, Date theDate) {
-
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
-
session.beginTransaction();
Event theEvent = new Event();
theEvent.setTitle(title);
theEvent.setDate(theDate);
-
session.save(theEvent);
session.getTransaction().commit();
@@ -693,165 +712,149 @@
}]]></programlisting>
<para>
- You can create a new <literal>Event</literal> object and hand it over to Hibernate.
- Hibernate takes care of the SQL and executes <literal>INSERT</literal>
- on the database. We will explain the <literal>Session</literal> and
- <literal>Transaction</literal>-handling code before we run this.
+ In <literal>createAndStoreEvent()</literal> we created a new
+ <literal>Event</literal> object and handed it over to Hibernate.
+ At that point, Hibernate takes care of the SQL and executes an
+ <literal>INSERT</literal> on the database.
</para>
<para>
- A <literal>Session</literal> is a single unit of work. For now we will keep things
- simple and assume a one-to-one granularity between a Hibernate <literal>Session</literal>
- and a database transaction. To shield our code from the actual underlying transaction
- system we use the
- <literal>Transaction</literal> API that is available on the Hibernate <literal>Session</literal>.
- In this case plain JDBC, but it could also run with JTA.
+ A <interface>org.hibernate.Session</interface> is designed to
+ represent a single unit of work (a single atmoic piece of work
+ to be performed). For now we will keep things simple and assume
+ a one-to-one granularity between a Hibernate
+ <interface>org.hibernate.Session</interface> and a database
+ transaction. To shield our code from the actual underlying
+ transaction system we use the Hibernate
+ <interfacename>org.hibernate.Transaction</interfacename> API.
+ In this particular case we are using JDBC-based transactional
+ semantics, but it could also run with JTA.
</para>
<para>
- What does <literal>sessionFactory.getCurrentSession()</literal> do? First, you can call it
- as many times and anywhere you like, once you get hold of your <literal>SessionFactory</literal>.
- This is easy thanks to <literal>HibernateUtil</literal>). The <literal>getCurrentSession()</literal>
- method always returns the "current" unit of work. Remember that we switched the configuration
- option for this mechanism to "thread" in <literal>hibernate.cfg.xml</literal>? The current unit of work is bound to the current Java thread that executes the application.
- However, you also have to consider when a unit of work
- begins and when it ends.
+ What does <literal>sessionFactory.getCurrentSession()</literal> do?
+ First, you can call it as many times and anywhere you like
+ once you get hold of your
+ <interfacename>org.hibernate.SessionFactory</interfacename>.
+ The <literal>getCurrentSession()</literal> method always returns
+ the "current" unit of work. Remember that we switched
+ the configuration option for this mechanism to "thread" in our
+ <filename>src/main/resources/hibernate.cfg.xml</filename>?
+ Due to that setting, the context of a current unit of work is bound
+ to the current Java thread that executes the application.
</para>
+ <important>
+ <para>
+ Hibernate offers three methods of current session tracking.
+ The "thread" based method is not intended for production use;
+ it is merely useful for prototyping and tutorials such as this
+ one. Current session tracking is discussed in more detail
+ later on.
+ </para>
+ </important>
+
<para>
- A <literal>Session</literal> begins when the first call to
- <literal>getCurrentSession()</literal> is made. It is then bound by Hibernate to the current
- thread. When the transaction ends, either through commit or rollback, Hibernate automatically
- unbinds the <literal>Session</literal> from the thread and closes it for you. If you call
- <literal>getCurrentSession()</literal> again, you get a new <literal>Session</literal> and can
- start a new unit of work. This <emphasis>thread-bound</emphasis> programming model is the most
- popular way of using Hibernate, as it allows flexible layering of your code. Transaction
- demarcation code can be separated from data access code. This will be covered later in this tutorial.
+ A <interface>org.hibernate.Session</interface> begins when the
+ first call to <literal>getCurrentSession()</literal> is made for
+ the current thread. It is then bound by Hibernate to the current
+ thread. When the transaction ends, either through commit or
+ rollback, Hibernate automatically unbinds the
+ <interface>org.hibernate.Session</interface> from the thread
+ and closes it for you. If you call
+ <literal>getCurrentSession()</literal> again, you get a new
+ <interface>org.hibernate.Session</interface> and can start a
+ new unit of work.
</para>
<para>
- Related to the unit of work scope, should the Hibernate <literal>Session</literal> be used to
- execute one or several database operations? The above example uses one <literal>Session</literal>
- for one operation. This is pure coincidence, the example is just not complex enough to show any
- other approach. The scope of a Hibernate <literal>Session</literal> is flexible but you should
- never design your application to use a new Hibernate <literal>Session</literal> for
- <emphasis>every</emphasis> database operation. Even though it is used in
- the following examples, consider <emphasis>session-per-operation</emphasis>
- an anti-pattern. A real web application is shown later in the tutorial.
+ Related to the unit of work scope, should the Hibernate
+ <interface>org.hibernate.Session</interface> be used to execute
+ one or several database operations? The above example uses one
+ <interface>org.hibernate.Session</interface> for one operation.
+ However this is pure coincidence; the example is just not complex
+ enough to show any other approach. The scope of a Hibernate
+ <interface>org.hibernate.Session</interface> is flexible but you
+ should never design your application to use a new Hibernate
+ <interface>org.hibernate.Session</interface> for
+ <emphasis>every</emphasis> database operation. Even though it is
+ used in the following examples, consider
+ <emphasis>session-per-operation</emphasis> an anti-pattern.
+ A real web application is shown later in the tutorial which will
+ help illustrate this.
</para>
<para>
See <xref linkend="transactions"/> for more information
- about transaction handling and demarcation. The previous example also skipped any error handling and
- rollback.
+ about transaction handling and demarcation. The previous
+ example also skipped any error handling and rollback.
</para>
<para>
- To run this first routine we have to add a callable target to the Ant build file:
+ To run this, we will make use of the Maven exec plugin to call our class
+ with the necessary classpath setup:
+ <command>mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="store"</command>
</para>
- <programlisting><![CDATA[<target name="run" depends="compile">
- <java fork="true" classname="events.EventManager" classpathref="libraries">
- <classpath path="${targetdir}"/>
- <arg value="${action}"/>
- </java>
-</target>]]></programlisting>
+ <note>
+ <para>
+ You may need to perform <command>mvn compile</command> first.
+ </para>
+ </note>
<para>
- The value of the <literal>action</literal> argument is set on the command line when
- calling the target:
+ You should see Hibernate starting up and, depending on your configuration,
+ lots of log output. Towards the end, the following line will be displayed:
</para>
- <programlisting><![CDATA[C:\hibernateTutorial\>ant run -Daction=store]]></programlisting>
-
- <para>
- After compilation, you should see Hibernate starting up and, depending on your
- configuration, lots of log output. At the end, the following line will be displayed:
- </para>
-
<programlisting><![CDATA[[java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?)]]></programlisting>
<para>
- This is the <literal>INSERT</literal> executed by Hibernate. The question marks
- represent JDBC bind parameters. To view the values bound as arguments, or to reduce
- the verbosity of the log, check your <literal>log4j.properties</literal>.
+ This is the <literal>INSERT</literal> executed by Hibernate.
</para>
<para>
To list stored events an option is added to the main method:
</para>
- <programlisting><![CDATA[if (args[0].equals("store")) {
- mgr.createAndStoreEvent("My Event", new Date());
-}
-else if (args[0].equals("list")) {
- List events = mgr.listEvents();
- for (int i = 0; i < events.size(); i++) {
- Event theEvent = (Event) events.get(i);
- System.out.println("Event: " + theEvent.getTitle() +
- " Time: " + theEvent.getDate());
- }
-}]]></programlisting>
+ <programlisting><![CDATA[ if (args[0].equals("store")) {
+ mgr.createAndStoreEvent("My Event", new Date());
+ }
+ else if (args[0].equals("list")) {
+ List events = mgr.listEvents();
+ for (int i = 0; i < events.size(); i++) {
+ Event theEvent = (Event) events.get(i);
+ System.out.println(
+ "Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()
+ );
+ }
+ }]]></programlisting>
<para>
A new <literal>listEvents() method is also added</literal>:
</para>
- <programlisting><![CDATA[private List listEvents() {
+ <programlisting><![CDATA[ private List listEvents() {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
+ List result = session.createQuery("from Event").list();
+ session.getTransaction().commit();
+ return result;
+ }]]></programlisting>
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
-
- session.beginTransaction();
-
- List result = session.createQuery("from Event").list();
-
- session.getTransaction().commit();
-
- return result;
-}]]></programlisting>
-
<para>
- Use an HQL (Hibernate Query Language) query to load all existing
+ Here, we are using a Hibernate Query Language (HQL) query to load all existing
<literal>Event</literal> objects from the database. Hibernate will generate the
appropriate SQL, send it to the database and populate <literal>Event</literal> objects
- with the data. You can create more complex queries with HQL.
+ with the data. You can create more complex queries with HQL. See <xref linkend="query-hql"/>
+ for more information.
</para>
<para>
- To execute and test all of this, follow these steps:
+ Now we can call our new functionality, again using the Maven exec plugin:
+ <command>mvn exec:java -Dexec.mainClass="org.hibernate.tutorial.EventManager" -Dexec.args="list"</command>
</para>
- <itemizedlist>
- <listitem>
- <para>
- Run <literal>ant run -Daction=store</literal> to store something in the database
- and, of course, to generate the database schema before through hbm2ddl.
- </para>
- </listitem>
- <listitem>
- <para>
- Disable hbm2ddl by commenting out the property in your <literal>hibernate.cfg.xml</literal>
- file. It is only turned on in continuous unit testing. Another
- run of hbm2ddl would <emphasis>drop</emphasis> everything you have stored. The
- <literal>create</literal> configuration setting translates into "drop all
- tables from the schema, then re-create all tables, when the SessionFactory is built".
- </para>
- </listitem>
- </itemizedlist>
-
- <para>
- If you can call Ant with <literal>-Daction=list</literal>, you will be able to view the events
- you have stored so far. You can also call the <literal>store</literal> action.
- </para>
-
- <para>
- Most new Hibernate users fail at this point and we regularly receive questions relating to
- <emphasis>Table not found</emphasis> error messages. However, if you follow the
- steps outlined above you will not have this problem, as hbm2ddl creates the database
- schema on the first run, and subsequent application restarts will use this schema. If
- you change the mapping and/or database schema, you will have to re-enable hbm2ddl.
- </para>
-
</sect2>
</sect1>
@@ -860,8 +863,10 @@
<title>Part 2 - Mapping associations</title>
<para>
- You have mapped a persistent entity class to a table. You can now build on this by adding some class associations.
- First you will add people to your application, and store a list of events they participate in.
+ So far we have mapped a single persistent entity class to a table in
+ isolation. Let's expand on that a bit and add some class associations.
+ We will add people to the application and store a list of events in
+ which they participate.
</para>
<sect2 id="tutorial-associations-mappinguser" revision="1">
@@ -871,7 +876,7 @@
The first cut of the <literal>Person</literal> class looks like this:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial.domain;
public class Person {
@@ -887,13 +892,18 @@
}]]></programlisting>
<para>
- Create a new mapping file called <literal>Person.hbm.xml</literal>. Do not forget the
- DTD reference at the top:
+ Save this to a file named
+ <filename>src/main/java/org/hibernate/tutorial/domain/Person.java</filename>
</para>
- <programlisting><![CDATA[<hibernate-mapping>
+ <para>
+ Next, create the new mapping file as
+ <filename>src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml</filename>
+ </para>
- <class name="events.Person" table="PERSON">
+ <programlisting><![CDATA[<hibernate-mapping package="org.hibernate.tutorial.domain">
+
+ <class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
@@ -924,17 +934,16 @@
<title>A unidirectional Set-based association</title>
<para>
- By adding a collection of events to the <literal>Person</literal> class, you can
- easily navigate to the events for a particular person, without executing an explicit query -
- by calling <literal>aPerson.getEvents()</literal>. A Java collection, a <literal>Set</literal>, is used
- because the collection will not contain duplicate elements and the ordering is not relevant to our examples.
+ By adding a collection of events to the <literal>Person</literal>
+ class, you can easily navigate to the events for a particular person,
+ without executing an explicit query - by calling
+ <literal>Person#getEvents</literal>. Multi-valued associations
+ are represented in Hibernate by one of the Java Collection Framework
+ contracts; here we choose a <interfacename>java.util.Set</interfacename>
+ because the collection will not contain duplicate elements and the ordering
+ is not relevant to our examples:
</para>
- <para>
- We need unidirectional, many-valued associations, implemented with a <literal>Set</literal>.
- Write the code for this in the Java classes and then map it:
- </para>
-
<programlisting><![CDATA[public class Person {
private Set events = new HashSet();
@@ -949,17 +958,19 @@
}]]></programlisting>
<para>
- Before you map this association, consider the other side. You could just keep this
- unidirectional. Or, you could create another collection on the <literal>Event</literal>, if you
- want to be able to navigate it bi-directional, i.e., <literal>anEvent.getParticipants()</literal>.
- This is not necessary, from a functional perspective. You can always execute an explicit query
- to retrieve the participants for a particular event. This is a design choice left to you, but what
- is clear from this discussion is the multiplicity of the association: "many" valued on both sides
- is called a <emphasis>many-to-many</emphasis> association. Hence, we use Hibernate's
- many-to-many mapping:
+ Before mapping this association, let's consider the other side.
+ We could just keep this unidirectional or create another
+ collection on the <literal>Event</literal>, if we wanted to be
+ able to navigate it from both directions. This is not necessary,
+ from a functional perspective. You can always execute an explicit
+ query to retrieve the participants for a particular event. This
+ is a design choice left to you, but what is clear from this
+ discussion is the multiplicity of the association: "many" valued
+ on both sides is called a <emphasis>many-to-many</emphasis>
+ association. Hence, we use Hibernate's many-to-many mapping:
</para>
- <programlisting><![CDATA[<class name="events.Person" table="PERSON">
+ <programlisting><![CDATA[<class name="Person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="native"/>
</id>
@@ -969,20 +980,24 @@
<set name="events" table="PERSON_EVENT">
<key column="PERSON_ID"/>
- <many-to-many column="EVENT_ID" class="events.Event"/>
+ <many-to-many column="EVENT_ID" class="Event"/>
</set>
</class>]]></programlisting>
<para>
- Hibernate supports a broad range of collection mappings, a <literal><set></literal> being most
- common. For a many-to-many association, or <emphasis>n:m</emphasis> entity relationship, an
- association table is required. Each row in this table represents a link between a person and an event.
- The table name is configured with the <literal>table</literal> attribute of the <literal>set</literal>
- element. The identifier column name in the association, for the person side, is defined with the
- <literal><key></literal> element, the column name for the event's side with the
- <literal>column</literal> attribute of the <literal><many-to-many></literal>. You also
- have to tell Hibernate the class of the objects in your collection (the class on the
+ Hibernate supports a broad range of collection mappings, a
+ <literal>set</literal> being most common. For a many-to-many
+ association, or <emphasis>n:m</emphasis> entity relationship, an
+ association table is required. Each row in this table represents
+ a link between a person and an event. The table name is
+ decalred using the <literal>table</literal> attribute of the
+ <literal>set</literal> element. The identifier column name in
+ the association, for the person side, is defined with the
+ <literal>key</literal> element, the column name for the event's
+ side with the <literal>column</literal> attribute of the
+ <literal>many-to-many</literal>. You also have to tell Hibernate
+ the class of the objects in your collection (the class on the
other side of the collection of references).
</para>
@@ -1013,73 +1028,77 @@
Now we will bring some people and events together in a new method in <literal>EventManager</literal>:
</para>
- <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+ <programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session.load(Person.class, personId);
+ Event anEvent = (Event) session.load(Event.class, eventId);
+ aPerson.getEvents().add(anEvent);
- Person aPerson = (Person) session.load(Person.class, personId);
- Event anEvent = (Event) session.load(Event.class, eventId);
+ session.getTransaction().commit();
+ }]]></programlisting>
- aPerson.getEvents().add(anEvent);
-
- session.getTransaction().commit();
-}]]></programlisting>
-
<para>
- After loading a <literal>Person</literal> and an <literal>Event</literal>, simply
- modify the collection using the normal collection methods. There is no explicit call
- to <literal>update()</literal> or <literal>save()</literal>; Hibernate automatically
- detects that the collection has been modified and needs to be updated. This is called <emphasis>automatic
- dirty checking</emphasis>. You can also try it by modifying the name or the date property of
- any of your objects. As long as they are in <emphasis>persistent</emphasis> state, that is, bound
- to a particular Hibernate <literal>Session</literal> (i.e. they have been just loaded or saved in
- a unit of work), Hibernate monitors any changes and executes SQL in a write-behind fashion. The
- process of synchronizing the memory state with the database, usually only at the end of a unit of
- work, is called <emphasis>flushing</emphasis>. In our code, the unit of work ends with a commit,
- or rollback, of the database transaction. This is defined by the <literal>thread</literal> configuration
- option for the <literal>CurrentSessionContext</literal> class.
+ After loading a <literal>Person</literal> and an
+ <literal>Event</literal>, simply modify the collection using the
+ normal collection methods. There is no explicit call to
+ <literal>update()</literal> or <literal>save()</literal>;
+ Hibernate automatically detects that the collection has been modified
+ and needs to be updated. This is called
+ <emphasis>automatic dirty checking</emphasis>. You can also try
+ it by modifying the name or the date property of any of your
+ objects. As long as they are in <emphasis>persistent</emphasis>
+ state, that is, bound to a particular Hibernate
+ <interfacename>org.hibernate.Session</interfacename>, Hibernate
+ monitors any changes and executes SQL in a write-behind fashion.
+ The process of synchronizing the memory state with the database,
+ usually only at the end of a unit of work, is called
+ <emphasis>flushing</emphasis>. In our code, the unit of work
+ ends with a commit, or rollback, of the database transaction.
</para>
<para>
- You can load person and event in different units of work. Or you can modify an object
- outside of a <literal>Session</literal>, when it is not in persistent state (if it was persistent
- before, this state is called <emphasis>detached</emphasis>). You can even modify a collection when
- it is detached:
+ You can load person and event in different units of work. Or
+ you can modify an object outside of a
+ <interfacename>org.hibernate.Session</interfacename>, when it
+ is not in persistent state (if it was persistent before, this
+ state is called <emphasis>detached</emphasis>). You can even
+ modify a collection when it is detached:
</para>
- <programlisting><![CDATA[private void addPersonToEvent(Long personId, Long eventId) {
+ <programlisting><![CDATA[ private void addPersonToEvent(Long personId, Long eventId) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session
+ .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
+ .setParameter("pid", personId)
+ .uniqueResult(); // Eager fetch the collection so we can use it detached
+ Event anEvent = (Event) session.load(Event.class, eventId);
- Person aPerson = (Person) session
- .createQuery("select p from Person p left join fetch p.events where p.id = :pid")
- .setParameter("pid", personId)
- .uniqueResult(); // Eager fetch the collection so we can use it detached
+ session.getTransaction().commit();
- Event anEvent = (Event) session.load(Event.class, eventId);
+ // End of first unit of work
- session.getTransaction().commit();
+ aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
- // End of first unit of work
+ // Begin second unit of work
- aPerson.getEvents().add(anEvent); // aPerson (and its collection) is detached
+ Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
+ session2.beginTransaction();
+ session2.update(aPerson); // Reattachment of aPerson
- // Begin second unit of work
+ session2.getTransaction().commit();
+ }]]></programlisting>
- Session session2 = HibernateUtil.getSessionFactory().getCurrentSession();
- session2.beginTransaction();
-
- session2.update(aPerson); // Reattachment of aPerson
-
- session2.getTransaction().commit();
-}]]></programlisting>
-
<para>
- The call to <literal>update</literal> makes a detached object persistent again by binding it to a new unit of work, so any modifications you made to it while detached
- can be saved to the database. This includes any modifications (additions/deletions) you
- made to a collection of that entity object.
+ The call to <literal>update</literal> makes a detached object
+ persistent again by binding it to a new unit of work, so any
+ modifications you made to it while detached can be saved to
+ the database. This includes any modifications
+ (additions/deletions) you made to a collection of that entity
+ object.
</para>
<para>
@@ -1090,29 +1109,35 @@
returns it (you might have to modify some of the previous methods to return that identifier):
</para>
- <programlisting><![CDATA[else if (args[0].equals("addpersontoevent")) {
- Long eventId = mgr.createAndStoreEvent("My Event", new Date());
- Long personId = mgr.createAndStorePerson("Foo", "Bar");
- mgr.addPersonToEvent(personId, eventId);
- System.out.println("Added person " + personId + " to event " + eventId);
-}]]></programlisting>
+ <programlisting><![CDATA[ else if (args[0].equals("addpersontoevent")) {
+ Long eventId = mgr.createAndStoreEvent("My Event", new Date());
+ Long personId = mgr.createAndStorePerson("Foo", "Bar");
+ mgr.addPersonToEvent(personId, eventId);
+ System.out.println("Added person " + personId + " to event " + eventId);
+ }]]></programlisting>
<para>
- This is an example of an association between two equally important classes, two entities.
- As mentioned earlier, there are other classes and types in a typical model, usually "less
- important". Some you have already seen, like an <literal>int</literal> or a <literal>String</literal>.
- We call these classes <emphasis>value types</emphasis>, and their instances <emphasis>depend</emphasis>
- on a particular entity. Instances of these types do not have their own identity, nor are they
- shared between entities. Two persons do not reference the same <literal>firstname</literal>
- object, even if they have the same first name. Value types cannot only be found in
- the JDK , but
- you can also write dependent classes yourself (<literal>Address</literal> or <literal>MonetaryAmount</literal>,
- for example). In fact, in a Hibernate application all JDK classes are considered value types.
+ This is an example of an association between two equally important
+ classes : two entities. As mentioned earlier, there are other
+ classes and types in a typical model, usually "less important".
+ Some you have already seen, like an <literal>int</literal> or a
+ <classname>java.lang.String</classname>. We call these classes
+ <emphasis>value types</emphasis>, and their instances
+ <emphasis>depend</emphasis> on a particular entity. Instances of
+ these types do not have their own identity, nor are they shared
+ between entities. Two persons do not reference the same
+ <literal>firstname</literal> object, even if they have the same
+ first name. Value types cannot only be found in the JDK , but
+ you can also write dependent classes yourself
+ such as an <literal>Address</literal> or
+ <literal>MonetaryAmount</literal> class. In fact, in a Hibernate
+ application all JDK classes are considered value types.
</para>
<para>
- You can also design a collection of value types. This is conceptually different from a
- collection of references to other entities, but looks almost the same in Java.
+ You can also design a collection of value types. This is
+ conceptually different from a collection of references to other
+ entities, but looks almost the same in Java.
</para>
</sect2>
@@ -1121,37 +1146,44 @@
<title>Collection of values</title>
<para>
- You can add a collection of value typed objects to the <literal>Person</literal> entity. If you want to
- store email addresses, the type you use is <literal>String</literal>, and the collection is
- again a <literal>Set</literal>:
+ Let's add a collection of email addresses to the
+ <literal>Person</literal> entity. This will be represented as a
+ <interfacename>java.util.Set</interfacename> of
+ <classname>java.lang.String</classname> instances:
</para>
- <programlisting><![CDATA[private Set emailAddresses = new HashSet();
+ <programlisting><![CDATA[ private Set emailAddresses = new HashSet();
-public Set getEmailAddresses() {
- return emailAddresses;
-}
+ public Set getEmailAddresses() {
+ return emailAddresses;
+ }
-public void setEmailAddresses(Set emailAddresses) {
- this.emailAddresses = emailAddresses;
-}]]></programlisting>
+ public void setEmailAddresses(Set emailAddresses) {
+ this.emailAddresses = emailAddresses;
+ }]]></programlisting>
<para>
The mapping of this <literal>Set</literal> is as follows:
</para>
- <programlisting><![CDATA[<set name="emailAddresses" table="PERSON_EMAIL_ADDR">
- <key column="PERSON_ID"/>
- <element type="string" column="EMAIL_ADDR"/>
-</set>]]></programlisting>
+ <programlisting><![CDATA[ <set name="emailAddresses" table="PERSON_EMAIL_ADDR">
+ <key column="PERSON_ID"/>
+ <element type="string" column="EMAIL_ADDR"/>
+ </set>]]></programlisting>
<para>
- The difference compared with the earlier mapping is the <literal>element</literal> part which tells Hibernate that the collection
- does not contain references to another entity, but a collection of elements of type
- <literal>String</literal>. The lowercase name tells you it is a Hibernate mapping type/converter.
- Again the <literal>table</literal> attribute of the <literal>set</literal> element determines
- the table name for the collection. The <literal>key</literal> element defines the foreign-key column
- name in the collection table. The <literal>column</literal> attribute in the <literal>element</literal>
- element defines the column name where the <literal>String</literal> values will actually be stored.
+ The difference compared with the earlier mapping is the use of
+ the <literal>element</literal> part which tells Hibernate that the
+ collection does not contain references to another entity, but is
+ rather a collection whose elements are values types, here specifically
+ of type <literal>string</literal>. The lowercase name tells you
+ it is a Hibernate mapping type/converter. Again the
+ <literal>table</literal> attribute of the <literal>set</literal>
+ element determines the table name for the collection. The
+ <literal>key</literal> element defines the foreign-key column
+ name in the collection table. The <literal>column</literal>
+ attribute in the <literal>element</literal> element defines the
+ column name where the email address values will actually
+ be stored.
</para>
<para>
@@ -1183,24 +1215,21 @@
linking persons and events. It is the same code in Java:
</para>
- <programlisting><![CDATA[private void addEmailToPerson(Long personId, String emailAddress) {
+ <programlisting><![CDATA[ private void addEmailToPerson(Long personId, String emailAddress) {
+ Session session = HibernateUtil.getSessionFactory().getCurrentSession();
+ session.beginTransaction();
- Session session = HibernateUtil.getSessionFactory().getCurrentSession();
- session.beginTransaction();
+ Person aPerson = (Person) session.load(Person.class, personId);
+ // adding to the emailAddress collection might trigger a lazy load of the collection
+ aPerson.getEmailAddresses().add(emailAddress);
- Person aPerson = (Person) session.load(Person.class, personId);
+ session.getTransaction().commit();
+ }]]></programlisting>
- // The getEmailAddresses() might trigger a lazy load of the collection
- aPerson.getEmailAddresses().add(emailAddress);
-
- session.getTransaction().commit();
-}]]></programlisting>
-
<para>
- This time we did not use a <emphasis>fetch</emphasis> query to initialize the collection.
- Hence, the call to its getter method will trigger an additional select to initialize
- it, so we can add an element to it. Monitor the SQL log and try to optimize this with
- an eager fetch.
+ This time we did not use a <emphasis>fetch</emphasis> query to
+ initialize the collection. Monitor the SQL log and try to
+ optimize this with an eager fetch.
</para>
</sect2>
@@ -1209,35 +1238,44 @@
<title>Bi-directional associations</title>
<para>
- Next you will map a bi-directional association. You will make the association between
- person and event work from both sides in Java. The database schema does not
- change, so you will still have many-to-many multiplicity. A relational database is more flexible
- than a network programming language, so it does not need a navigation
- direction; data can be viewed and retrieved in any possible way.
+ Next you will map a bi-directional association. You will make
+ the association between person and event work from both sides
+ in Java. The database schema does not change, so you will still
+ have many-to-many multiplicity.
</para>
+ <note>
+ <para>
+ A relational database is more flexible than a network
+ programming language, in that it does not need a navigation
+ direction; data can be viewed and retrieved in any possible
+ way.
+ </para>
+ </note>
+
<para>
- First, add a collection of participants to the <literal>Event</literal> Event class:
+ First, add a collection of participants to the
+ <literal>Event</literal> class:
</para>
- <programlisting><![CDATA[private Set participants = new HashSet();
+ <programlisting><![CDATA[ private Set participants = new HashSet();
-public Set getParticipants() {
- return participants;
-}
+ public Set getParticipants() {
+ return participants;
+ }
-public void setParticipants(Set participants) {
- this.participants = participants;
-}]]></programlisting>
+ public void setParticipants(Set participants) {
+ this.participants = participants;
+ }]]></programlisting>
<para>
Now map this side of the association in <literal>Event.hbm.xml</literal>.
</para>
- <programlisting><![CDATA[<set name="participants" table="PERSON_EVENT" inverse="true">
- <key column="EVENT_ID"/>
- <many-to-many column="PERSON_ID" class="events.Person"/>
-</set>]]></programlisting>
+ <programlisting><![CDATA[ <set name="participants" table="PERSON_EVENT" inverse="true">
+ <key column="EVENT_ID"/>
+ <many-to-many column="PERSON_ID" class="events.Person"/>
+ </set>]]></programlisting>
<para>
These are normal <literal>set</literal> mappings in both mapping documents.
@@ -1273,23 +1311,23 @@
correctly set both sides (for example, in <literal>Person</literal>):
</para>
- <programlisting><![CDATA[protected Set getEvents() {
- return events;
-}
+ <programlisting><![CDATA[ protected Set getEvents() {
+ return events;
+ }
-protected void setEvents(Set events) {
- this.events = events;
-}
+ protected void setEvents(Set events) {
+ this.events = events;
+ }
-public void addToEvent(Event event) {
- this.getEvents().add(event);
- event.getParticipants().add(this);
-}
+ public void addToEvent(Event event) {
+ this.getEvents().add(event);
+ event.getParticipants().add(this);
+ }
-public void removeFromEvent(Event event) {
- this.getEvents().remove(event);
- event.getParticipants().remove(this);
-}]]></programlisting>
+ public void removeFromEvent(Event event) {
+ this.getEvents().remove(event);
+ event.getParticipants().remove(this);
+ }]]></programlisting>
<para>
The get and set methods for the collection are now protected. This allows classes in the
@@ -1309,7 +1347,6 @@
and in many-to-many association you can select either side.
</para>
- <para>In the next section we will turn this into a small web application.</para>
</sect2>
</sect1>
@@ -1328,50 +1365,51 @@
<title>Writing the basic servlet</title>
<para>
- Create a new class in your source directory in the <literal>events</literal>
- package:
+ First we need create our basic processing servlet. Since our
+ servlet only handles HTTP <literal>GET</literal> requests, we
+ will only implement the <literal>doGet()</literal> method:
</para>
- <programlisting><![CDATA[package events;
+ <programlisting><![CDATA[package org.hibernate.tutorial.web;
// Imports
public class EventManagerServlet extends HttpServlet {
- // Servlet code
-}]]></programlisting>
+ protected void doGet(
+ HttpServletRequest request,
+ HttpServletResponse response) throws ServletException, IOException {
- <para>
- The servlet handles HTTP <literal>GET</literal> requests only. It is for this reason that the method
- we implement is <literal>doGet()</literal>:
- </para>
+ SimpleDateFormat dateFormatter = new SimpleDateFormat( "dd.MM.yyyy" );
- <programlisting><![CDATA[protected void doGet(HttpServletRequest request,
- HttpServletResponse response)
- throws ServletException, IOException {
+ try {
+ // Begin unit of work
+ HibernateUtil.getSessionFactory().getCurrentSession().beginTransaction();
- SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
+ // Process request and render page...
- try {
- // Begin unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().beginTransaction();
-
- // Process request and render page...
-
- // End unit of work
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().commit();
-
- } catch (Exception ex) {
- HibernateUtil.getSessionFactory()
- .getCurrentSession().getTransaction().rollback();
- throw new ServletException(ex);
+ // End unit of work
+ HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
+ }
+ catch (Exception ex) {
+ HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().rollback();
+ if ( ServletException.class.isInstance( ex ) ) {
+ throw ( ServletException ) ex;
+ }
+ else {
+ throw new ServletException( ex );
+ }
+ }
}
}]]></programlisting>
<para>
+ Save this servlet as
+ <filename>src/main/java/org/hibernate/tutorial/web/EventManagerServlet.java</filename>
+ </para>
+
+ <para>
The pattern applied here is called <emphasis>session-per-request</emphasis>.
When a request hits the servlet, a new Hibernate <literal>Session</literal> is
opened through the first call to <literal>getCurrentSession()</literal> on the
@@ -1412,32 +1450,33 @@
Now you can implement the processing of the request and the rendering of the page.
</para>
-<programlisting><![CDATA[// Write HTML header
-PrintWriter out = response.getWriter();
-out.println("<html><head><title>Event Manager</title></head><body>");
+<programlisting><![CDATA[ // Write HTML header
+ PrintWriter out = response.getWriter();
+ out.println("<html><head><title>Event Manager</title></head><body>");
-// Handle actions
-if ( "store".equals(request.getParameter("action")) ) {
+ // Handle actions
+ if ( "store".equals(request.getParameter("action")) ) {
- String eventTitle = request.getParameter("eventTitle");
- String eventDate = request.getParameter("eventDate");
+ String eventTitle = request.getParameter("eventTitle");
+ String eventDate = request.getParameter("eventDate");
- if ( "".equals(eventTitle) || "".equals(eventDate) ) {
- out.println("<b><i>Please enter event title and date.</i></b>");
- } else {
- createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
- out.println("<b><i>Added event.</i></b>");
- }
-}
+ if ( "".equals(eventTitle) || "".equals(eventDate) ) {
+ out.println("<b><i>Please enter event title and date.</i></b>");
+ }
+ else {
+ createAndStoreEvent(eventTitle, dateFormatter.parse(eventDate));
+ out.println("<b><i>Added event.</i></b>");
+ }
+ }
-// Print page
-printEventForm(out);
-listEvents(out, dateFormatter);
+ // Print page
+ printEventForm(out);
+ listEvents(out, dateFormatter);
-// Write HTML footer
-out.println("</body></html>");
-out.flush();
-out.close();]]></programlisting>
+ // Write HTML footer
+ out.println("</body></html>");
+ out.flush();
+ out.close();]]></programlisting>
<para>
This coding style, with a mix of Java and HTML, would not scale
@@ -1448,14 +1487,14 @@
trivial and only outputs HTML:
</para>
- <programlisting><![CDATA[private void printEventForm(PrintWriter out) {
- out.println("<h2>Add new event:</h2>");
- out.println("<form>");
- out.println("Title: <input name='eventTitle' length='50'/><br/>");
- out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
- out.println("<input type='submit' name='action' value='store'/>");
- out.println("</form>");
-}]]></programlisting>
+ <programlisting><![CDATA[ private void printEventForm(PrintWriter out) {
+ out.println("<h2>Add new event:</h2>");
+ out.println("<form>");
+ out.println("Title: <input name='eventTitle' length='50'/><br/>");
+ out.println("Date (e.g. 24.12.2009): <input name='eventDate' length='10'/><br/>");
+ out.println("<input type='submit' name='action' value='store'/>");
+ out.println("</form>");
+ }]]></programlisting>
<para>
The <literal>listEvents()</literal> method uses the Hibernate
@@ -1463,27 +1502,28 @@
a query:
</para>
- <programlisting><![CDATA[private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
+ <programlisting><![CDATA[ private void listEvents(PrintWriter out, SimpleDateFormat dateFormatter) {
- List result = HibernateUtil.getSessionFactory()
- .getCurrentSession().createCriteria(Event.class).list();
- if (result.size() > 0) {
- out.println("<h2>Events in database:</h2>");
- out.println("<table border='1'>");
- out.println("<tr>");
- out.println("<th>Event title</th>");
- out.println("<th>Event date</th>");
- out.println("</tr>");
- for (Iterator it = result.iterator(); it.hasNext();) {
- Event event = (Event) it.next();
+ List result = HibernateUtil.getSessionFactory()
+ .getCurrentSession().createCriteria(Event.class).list();
+ if (result.size() > 0) {
+ out.println("<h2>Events in database:</h2>");
+ out.println("<table border='1'>");
out.println("<tr>");
- out.println("<td>" + event.getTitle() + "</td>");
- out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("<th>Event title</th>");
+ out.println("<th>Event date</th>");
out.println("</tr>");
+ Iterator it = result.iterator();
+ while (it.hasNext()) {
+ Event event = (Event) it.next();
+ out.println("<tr>");
+ out.println("<td>" + event.getTitle() + "</td>");
+ out.println("<td>" + dateFormatter.format(event.getDate()) + "</td>");
+ out.println("</tr>");
+ }
+ out.println("</table>");
}
- out.println("</table>");
- }
-}]]></programlisting>
+ }]]></programlisting>
<para>
Finally, the <literal>store</literal> action is dispatched to the
@@ -1491,14 +1531,14 @@
the <literal>Session</literal> of the current thread:
</para>
- <programlisting><![CDATA[protected void createAndStoreEvent(String title, Date theDate) {
- Event theEvent = new Event();
- theEvent.setTitle(title);
- theEvent.setDate(theDate);
+ <programlisting><![CDATA[ protected void createAndStoreEvent(String title, Date theDate) {
+ Event theEvent = new Event();
+ theEvent.setTitle(title);
+ theEvent.setDate(theDate);
- HibernateUtil.getSessionFactory()
- .getCurrentSession().save(theEvent);
-}]]></programlisting>
+ HibernateUtil.getSessionFactory()
+ .getCurrentSession().save(theEvent);
+ }]]></programlisting>
<para>
The servlet is now complete. A request to the servlet will be processed
@@ -1517,26 +1557,11 @@
<title>Deploying and testing</title>
<para>
- To deploy this application you have to create a web archive, or what is known as a WAR. Add the
- following Ant target to your <literal>build.xml</literal>:
+ To deploy this application for testing we must create a
+ Web ARchive (WAR). First we must define the WAR descriptor
+ as <filename>src/main/webapp/WEB-INF/web.xml</filename>
</para>
-<programlisting><![CDATA[<target name="war" depends="compile">
- <war destfile="hibernate-tutorial.war" webxml="web.xml">
- <lib dir="${librarydir}">
- <exclude name="jsdk*.jar"/>
- </lib>
-
- <classes dir="${targetdir}"/>
- </war>
-</target>]]></programlisting>
-
- <para>
- This target creates a file called <literal>hibernate-tutorial.war</literal>
- in your project directory. It packages all libraries and the <literal>web.xml</literal>
- descriptor, which is expected in the base directory of your project:
- </para>
-
<programlisting><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
@@ -1545,7 +1570,7 @@
<servlet>
<servlet-name>Event Manager</servlet-name>
- <servlet-class>events.EventManagerServlet</servlet-class>
+ <servlet-class>org.hibernate.tutorial.web.EventManagerServlet</servlet-class>
</servlet>
<servlet-mapping>
@@ -1555,20 +1580,19 @@
</web-app>]]></programlisting>
<para>
- Before you compile and deploy the web application, an additional library
- is required: <literal>jsdk.jar</literal>. This is the Java servlet development kit.
- If you do not have this library already, get it from the Sun website and copy it to
- your library directory. However, it will be only used for compilation and excluded
- from the WAR package.
+ To build and deploy call <literal>mvn package</literal> in your
+ project directory and copy the <filename>hibernate-tutorial.war</filename>
+ file into your Tomcat <filename>webapps</filename> directory.
</para>
- <para>
- To build and deploy call <literal>ant war</literal> in your project directory
- and copy the <literal>hibernate-tutorial.war</literal> file into your Tomcat
- <literal>webapp</literal> directory. If you do not have Tomcat installed, download
- it and follow the installation instructions. You do not have to change any Tomcat
- configuration to deploy this application though.
- </para>
+ <note>
+ <para>
+ If you do not have Tomcat installed, download it from
+ <ulink url="http://tomcat.apache.org/"/> and follow the
+ installation instructions. Our application requires
+ no changes to the standard Tomcat configuration.
+ </para>
+ </note>
<para>
Once deployed and Tomcat is running, access the application at
@@ -1587,21 +1611,10 @@
<para>
This tutorial covered the basics of writing a simple standalone Hibernate application
- and a small web application.
+ and a small web application. More tutorials are available from the Hibernate
+ <ulink url="http://hibernate.org">website</ulink>.
</para>
- <para>
- If you already feel confident with Hibernate, continue browsing through the reference
- documentation table of contents for topics you find interesting. The most popular are
- transactional processing (<xref linkend="transactions"/>), fetch
- performance (<xref linkend="performance"/>), or the usage of the API (<xref linkend="objectstate"/>)
- and the query features (<xref linkend="objectstate-querying"/>).
- </para>
-
- <para>
- More tutorials are available from the Hibernate website.
- </para>
-
</sect1>
</chapter>
15 years, 7 months
Hibernate SVN: r16739 - in beanvalidation/trunk/validation-tck/src/main: java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/xmldefined and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-06-10 10:05:26 -0400 (Wed, 10 Jun 2009)
New Revision: 16739
Added:
beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/CustomProviderResolverTest.java
beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/BootstrapTest.java
Removed:
beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/DefaultBootstrapTest.java
Modified:
beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/ExplicitCustomProviderBootstrapTest.java
beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/TCKValidatorConfiguration.java
beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/xmldefined/CustomProviderInXmlBootstrapTest.java
beanvalidation/trunk/validation-tck/src/main/resources/tck-audit.xml
Log:
More bootstrap tests.
Added: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/CustomProviderResolverTest.java
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/CustomProviderResolverTest.java (rev 0)
+++ beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/CustomProviderResolverTest.java 2009-06-10 14:05:26 UTC (rev 16739)
@@ -0,0 +1,115 @@
+// $Id$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.hibernate.jsr303.tck.tests.bootstrap.customprovider;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.Configuration;
+import javax.validation.Validation;
+import javax.validation.ValidationProviderResolver;
+import javax.validation.ValidatorFactory;
+import javax.validation.ValidationException;
+import javax.validation.bootstrap.ProviderSpecificBootstrap;
+import javax.validation.spi.ValidationProvider;
+
+import org.jboss.testharness.AbstractTest;
+import org.jboss.testharness.impl.packaging.Artifact;
+import org.jboss.testharness.impl.packaging.ArtifactType;
+import org.jboss.testharness.impl.packaging.Classes;
+import org.jboss.testharness.impl.packaging.IntegrationTest;
+import org.jboss.testharness.impl.packaging.Resource;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
+import org.testng.annotations.Test;
+import static org.testng.FileAssert.fail;
+
+import org.hibernate.jsr303.tck.util.TestUtil;
+
+/**
+ * @author Hardy Ferentschik
+ * @todo Review these tests. These tests are actually testing functionality within Validation. They should maybe be moved to
+ * the Bean Validation API itself!?
+ *
+ */
+@Artifact(artifactType = ArtifactType.JSR303)
+(a)Classes(TestUtil.class)
+public class CustomProviderResolverTest extends AbstractTest {
+
+ @Test
+ public void testCustomResolverAndType() {
+ ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+ public List<ValidationProvider> getValidationProviders() {
+ List<ValidationProvider> list = new ArrayList<ValidationProvider>();
+ list.add( new TCKValidationProvider() );
+ return list;
+ }
+ };
+
+
+ TCKValidatorConfiguration configuration = Validation
+ .byProvider( TCKValidatorConfiguration.class )
+ .providerResolver( resolver )
+ .configure();
+
+ ValidatorFactory factory = configuration.buildValidatorFactory();
+ assertTrue(factory instanceof TCKValidationProvider.DummyValidatorFactory);
+ }
+
+ @Test
+ public void testCustomResolver() {
+ ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+ public List<ValidationProvider> getValidationProviders() {
+ List<ValidationProvider> list = new ArrayList<ValidationProvider>();
+ list.add( new TCKValidationProvider() );
+ return list;
+ }
+ };
+
+ Configuration<?> configuration = Validation
+ .byDefaultProvider()
+ .providerResolver( resolver )
+ .configure();
+ ValidatorFactory factory = configuration.buildValidatorFactory();
+ assertTrue(factory instanceof TCKValidationProvider.DummyValidatorFactory);
+ }
+
+ @Test
+ public void testFailingCustomResolver() {
+ ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+ public List<ValidationProvider> getValidationProviders() {
+ return new ArrayList<ValidationProvider>();
+ }
+ };
+
+ final ProviderSpecificBootstrap<TCKValidatorConfiguration> providerSpecificBootstrap =
+ Validation
+ .byProvider( TCKValidatorConfiguration.class )
+ .providerResolver( resolver );
+
+ try {
+ providerSpecificBootstrap.configure();
+ fail();
+ }
+ catch ( ValidationException e ) {
+ // success
+ }
+ }
+}
\ No newline at end of file
Property changes on: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/CustomProviderResolverTest.java
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/ExplicitCustomProviderBootstrapTest.java
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/ExplicitCustomProviderBootstrapTest.java 2009-06-10 14:04:41 UTC (rev 16738)
+++ beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/ExplicitCustomProviderBootstrapTest.java 2009-06-10 14:05:26 UTC (rev 16739)
@@ -1,4 +1,4 @@
-// $Id:$
+// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
@@ -27,6 +27,7 @@
import org.jboss.testharness.impl.packaging.Resource;
import org.jboss.testharness.impl.packaging.IntegrationTest;
import org.jboss.test.audit.annotations.SpecAssertion;
+import org.jboss.test.audit.annotations.SpecAssertions;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
@@ -43,7 +44,10 @@
public class ExplicitCustomProviderBootstrapTest extends AbstractTest {
@Test
- @SpecAssertion(section = "4.4.4.2", id = "a")
+ @SpecAssertions({
+ @SpecAssertion(section = "4.4", id = "a"),
+ @SpecAssertion(section = "4.4.4.2", id = "a")
+ })
public void testGetFactoryByProviderSpecifiedProgrammatically() {
TCKValidatorConfiguration configuration = Validation.byProvider( TCKValidatorConfiguration.class ).configure();
ValidatorFactory factory = configuration.buildValidatorFactory();
Modified: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/TCKValidatorConfiguration.java
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/TCKValidatorConfiguration.java 2009-06-10 14:04:41 UTC (rev 16738)
+++ beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/TCKValidatorConfiguration.java 2009-06-10 14:05:26 UTC (rev 16739)
@@ -1,4 +1,4 @@
-// $Id:$
+// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
@@ -40,31 +40,31 @@
}
public TCKValidatorConfiguration ignoreXmlConfiguration() {
- return this;
+ throw new UnsupportedOperationException();
}
public TCKValidatorConfiguration messageInterpolator(MessageInterpolator interpolator) {
- return this;
+ throw new UnsupportedOperationException();
}
public TCKValidatorConfiguration traversableResolver(TraversableResolver resolver) {
- return this;
+ throw new UnsupportedOperationException();
}
public TCKValidatorConfiguration constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
- return this;
+ throw new UnsupportedOperationException();
}
public TCKValidatorConfiguration addMapping(InputStream stream) {
- return this;
+ throw new UnsupportedOperationException();
}
public TCKValidatorConfiguration addProperty(String name, String value) {
- return this;
+ throw new UnsupportedOperationException();
}
public MessageInterpolator getDefaultMessageInterpolator() {
- return null;
+ throw new UnsupportedOperationException();
}
public ValidatorFactory buildValidatorFactory() {
Modified: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/xmldefined/CustomProviderInXmlBootstrapTest.java
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/xmldefined/CustomProviderInXmlBootstrapTest.java 2009-06-10 14:04:41 UTC (rev 16738)
+++ beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/customprovider/xmldefined/CustomProviderInXmlBootstrapTest.java 2009-06-10 14:05:26 UTC (rev 16739)
@@ -1,4 +1,4 @@
-// $Id:$
+// $Id$
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
@@ -21,6 +21,7 @@
import javax.validation.ValidatorFactory;
import org.jboss.test.audit.annotations.SpecAssertion;
+import org.jboss.test.audit.annotations.SpecAssertions;
import org.jboss.testharness.AbstractTest;
import org.jboss.testharness.impl.packaging.Artifact;
import org.jboss.testharness.impl.packaging.ArtifactType;
@@ -53,7 +54,10 @@
public class CustomProviderInXmlBootstrapTest extends AbstractTest {
@Test
- @SpecAssertion(section = "4.4.4.2", id = "a")
+ @SpecAssertions({
+ @SpecAssertion(section = "4.4", id = "a"),
+ @SpecAssertion(section = "4.4.4.2", id = "a")
+ })
public void testGetFactoryByProviderSpecifiedInXml() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
assertNotNull( factory );
Added: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/BootstrapTest.java
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/BootstrapTest.java (rev 0)
+++ beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/BootstrapTest.java 2009-06-10 14:05:26 UTC (rev 16739)
@@ -0,0 +1,260 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.hibernate.jsr303.tck.tests.bootstrap.defaultprovider;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.ElementType;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import javax.validation.Configuration;
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ValidationProvider;
+
+import org.jboss.test.audit.annotations.SpecAssertion;
+import org.jboss.test.audit.annotations.SpecAssertions;
+import org.jboss.testharness.AbstractTest;
+import org.jboss.testharness.impl.packaging.Artifact;
+import org.jboss.testharness.impl.packaging.ArtifactType;
+import org.jboss.testharness.impl.packaging.Classes;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+
+import org.hibernate.jsr303.tck.util.TestUtil;
+import static org.hibernate.jsr303.tck.util.TestUtil.assertConstraintViolation;
+import static org.hibernate.jsr303.tck.util.TestUtil.assertInvalidPropertyPaths;
+
+/**
+ * @author Hardy Ferentschik
+ */
+@Artifact(artifactType = ArtifactType.JSR303)
+(a)Classes(TestUtil.class)
+public class BootstrapTest extends AbstractTest {
+ private static final String SERVICES_FILE = "META-INF/services/" + ValidationProvider.class.getName();
+
+ @Test
+ public void testGetDefaultValidator() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+ assertNotNull( validator, "We should be able to get a validator." );
+
+ Person person = new Person();
+ person.setPersonalNumber( 12345678900l );
+
+ Set<ConstraintViolation<Person>> constraintViolations = validator.validate( person );
+ assertEquals( constraintViolations.size(), 3, "Wrong number of constraints" );
+ assertInvalidPropertyPaths(
+ constraintViolations,
+ new String[] { "firstName", "lastName", "personalNumber" }
+ );
+
+ person.setFirstName( "John" );
+ person.setLastName( "Doe" );
+
+ constraintViolations = validator.validate( person );
+ assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
+ assertConstraintViolation(
+ constraintViolations.iterator().next(), Person.class, 12345678900l, "personalNumber"
+ );
+
+ person.setPersonalNumber( 1234567890l );
+ constraintViolations = validator.validate( person );
+ assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
+ }
+
+ @Test
+ @SpecAssertion(section = "4.4.4.1", id = "c")
+ public void testServiceFileExists() {
+ List<ValidationProvider> providers = readBeanValidationServiceFile();
+ assertTrue( !providers.isEmpty(), "There should be at least one provider" );
+ }
+
+ private List<ValidationProvider> readBeanValidationServiceFile() {
+ ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+ if ( classloader == null ) {
+ classloader = BootstrapTest.class.getClassLoader();
+ }
+ List<ValidationProvider> providers = new ArrayList<ValidationProvider>();
+ try {
+
+ Enumeration<URL> providerDefinitions = classloader.getResources( SERVICES_FILE );
+ while ( providerDefinitions.hasMoreElements() ) {
+ URL url = providerDefinitions.nextElement();
+ addProviderToList( providers, url );
+ }
+ }
+ catch ( Exception e ) {
+ throw new RuntimeException( "Unable to load service file", e );
+ }
+ return providers;
+ }
+
+
+ @Test
+ public void testCustomMessageInterpolatorViaConfiguration() {
+ // create a configurtation with a custom message interpolator
+ Configuration<?> configuration = Validation.byDefaultProvider().configure();
+ configuration.messageInterpolator( new DummyMessageInterpolator() );
+
+ Validator validator = configuration.buildValidatorFactory().getValidator();
+ assertCustomMessageInterpolatorUsed( validator );
+ }
+
+ @Test
+ @SpecAssertions({
+ @SpecAssertion(section = "4.4.2", id = "a"),
+ @SpecAssertion(section = "4.4.2", id = "b"),
+ @SpecAssertion(section = "4.3.2", id = "b")
+ })
+ public void testCustomMessageInterpolatorViaValidatorContext() {
+ // create a configurtation with a custom message interpolator
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ DummyMessageInterpolator dummyMessageInterpolator = new DummyMessageInterpolator();
+ Validator validator = factory.usingContext().messageInterpolator( dummyMessageInterpolator ).getValidator();
+ assertCustomMessageInterpolatorUsed( validator );
+ assertFalse(
+ factory.getMessageInterpolator().equals( dummyMessageInterpolator ),
+ "getMessageInterpolator() should return the default message interpolator."
+ );
+ }
+
+ @Test
+ @SpecAssertion(section = "3.5.2", id = "b")
+ public void testCustomTraversableResolverViaConfiguration() {
+
+ // get a new factory using a custom configuration
+ Configuration<?> configuration = Validation.byDefaultProvider().configure();
+ configuration.traversableResolver( new DummyTraversableResolver() );
+ ValidatorFactory factory = configuration.buildValidatorFactory();
+ Validator validator = factory.getValidator();
+
+ assertCustomTrversableResolverUsed( validator );
+ }
+
+ @Test
+ @SpecAssertion(section = "3.5.2", id = "b")
+ public void testCustomTraversableResolverViaValidatorContext() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ DummyTraversableResolver DummyTraversableResolver = new DummyTraversableResolver();
+ Validator validator = factory.usingContext().traversableResolver( DummyTraversableResolver ).getValidator();
+
+ assertCustomTrversableResolverUsed( validator );
+ }
+
+ private void assertCustomTrversableResolverUsed(Validator validator) {
+ Person person = new Person();
+ Set<ConstraintViolation<Person>> constraintViolations = validator.validate( person );
+ assertEquals(
+ constraintViolations.size(),
+ 0,
+ "There should be no failures since the custom traversable resolver makes all properties non traversable."
+ );
+ }
+
+ private void assertCustomMessageInterpolatorUsed(Validator validator) {
+ Person person = new Person();
+ person.setFirstName( "John" );
+ person.setPersonalNumber( 1234567890l );
+
+ Set<ConstraintViolation<Person>> constraintViolations = validator.validate( person );
+ assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
+ ConstraintViolation<Person> constraintViolation = constraintViolations.iterator().next();
+ assertEquals( "my custom message", constraintViolation.getMessage(), "Wrong message" );
+ }
+
+ private void addProviderToList(List<ValidationProvider> providers, URL url)
+ throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
+ InputStream stream = url.openStream();
+ try {
+ BufferedReader reader = new BufferedReader( new InputStreamReader( stream ), 100 );
+ String name = reader.readLine();
+ while ( name != null ) {
+ name = name.trim();
+ if ( !name.startsWith( "#" ) ) {
+ final Class<?> providerClass = loadClass(
+ name,
+ BootstrapTest.class
+ );
+
+ providers.add(
+ ( ValidationProvider ) providerClass.newInstance()
+ );
+ }
+ name = reader.readLine();
+ }
+ }
+ finally {
+ stream.close();
+ }
+ }
+
+ private static Class<?> loadClass(String name, Class caller) throws ClassNotFoundException {
+ try {
+ //try context classloader, if fails try caller classloader
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if ( loader != null ) {
+ return loader.loadClass( name );
+ }
+ }
+ catch ( ClassNotFoundException e ) {
+ //trying caller classloader
+ if ( caller == null ) {
+ throw e;
+ }
+ }
+ return Class.forName( name, true, caller.getClassLoader() );
+ }
+
+ private static class DummyMessageInterpolator implements MessageInterpolator {
+ public String interpolate(String message, Context context) {
+ return "my custom message";
+ }
+
+ public String interpolate(String message, Context context, Locale locale) {
+ throw new UnsupportedOperationException( "No specific locale is possible" );
+ }
+ }
+
+ private static class DummyTraversableResolver implements TraversableResolver {
+ public boolean isReachable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
+ return false;
+ }
+
+ public boolean isCascadable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
+ return false;
+ }
+
+ public boolean isTraversable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
+ return false;
+ }
+ }
+}
Property changes on: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/BootstrapTest.java
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Deleted: beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/DefaultBootstrapTest.java
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/DefaultBootstrapTest.java 2009-06-10 14:04:41 UTC (rev 16738)
+++ beanvalidation/trunk/validation-tck/src/main/java/org/hibernate/jsr303/tck/tests/bootstrap/defaultprovider/DefaultBootstrapTest.java 2009-06-10 14:05:26 UTC (rev 16739)
@@ -1,357 +0,0 @@
-// $Id:$
-/*
-* JBoss, Home of Professional Open Source
-* Copyright 2008, Red Hat Middleware LLC, and individual contributors
-* by the @authors tag. See the copyright.txt in the distribution for a
-* full listing of individual contributors.
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-* http://www.apache.org/licenses/LICENSE-2.0
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-package org.hibernate.jsr303.tck.tests.bootstrap.defaultprovider;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Set;
-import javax.validation.ConstraintViolation;
-import javax.validation.Validation;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
-import javax.validation.spi.ValidationProvider;
-
-import org.jboss.test.audit.annotations.SpecAssertion;
-import org.jboss.testharness.AbstractTest;
-import org.jboss.testharness.impl.packaging.Artifact;
-import org.jboss.testharness.impl.packaging.ArtifactType;
-import org.jboss.testharness.impl.packaging.Classes;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-import org.testng.annotations.Test;
-
-import org.hibernate.jsr303.tck.util.TestUtil;
-import static org.hibernate.jsr303.tck.util.TestUtil.assertConstraintViolation;
-import static org.hibernate.jsr303.tck.util.TestUtil.assertInvalidPropertyPaths;
-
-/**
- * @author Hardy Ferentschik
- */
-@Artifact(artifactType = ArtifactType.JSR303)
-(a)Classes(TestUtil.class)
-public class DefaultBootstrapTest extends AbstractTest {
- private static final String SERVICES_FILE = "META-INF/services/" + ValidationProvider.class.getName();
-
- @Test
- public void testGetDefaultValidator() {
- ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
- Validator validator = factory.getValidator();
- assertNotNull( validator, "We should be able to get a validator." );
-
- Person person = new Person();
- person.setPersonalNumber( 12345678900l );
-
- Set<ConstraintViolation<Person>> constraintViolations = validator.validate( person );
- assertEquals( constraintViolations.size(), 3, "Wrong number of constraints" );
- assertInvalidPropertyPaths(
- constraintViolations,
- new String[] { "firstName", "lastName", "personalNumber" }
- );
-
- person.setFirstName( "John" );
- person.setLastName( "Doe" );
-
- constraintViolations = validator.validate( person );
- assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
- assertConstraintViolation(
- constraintViolations.iterator().next(), Person.class, 12345678900l, "personalNumber"
- );
-
- person.setPersonalNumber( 1234567890l );
- constraintViolations = validator.validate( person );
- assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
- }
-
- @Test
- @SpecAssertion(section = "4.4.4.1", id = "c")
- public void testServiceFileExists() {
- List<ValidationProvider> providers = readBeanValidationServiceFile();
- assertTrue( !providers.isEmpty(), "There should be at least one provider" );
- }
-
- private List<ValidationProvider> readBeanValidationServiceFile() {
- ClassLoader classloader = Thread.currentThread().getContextClassLoader();
- if ( classloader == null ) {
- classloader = DefaultBootstrapTest.class.getClassLoader();
- }
- List<ValidationProvider> providers = new ArrayList<ValidationProvider>();
- try {
-
- Enumeration<URL> providerDefinitions = classloader.getResources( SERVICES_FILE );
- while ( providerDefinitions.hasMoreElements() ) {
- URL url = providerDefinitions.nextElement();
- addProviderToList( providers, url );
- }
- }
- catch ( Exception e ) {
- throw new RuntimeException( "Unable to load service file", e );
- }
- return providers;
- }
-
- private void addProviderToList(List<ValidationProvider> providers, URL url)
- throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
- InputStream stream = url.openStream();
- try {
- BufferedReader reader = new BufferedReader( new InputStreamReader( stream ), 100 );
- String name = reader.readLine();
- while ( name != null ) {
- name = name.trim();
- if ( !name.startsWith( "#" ) ) {
- final Class<?> providerClass = loadClass(
- name,
- DefaultBootstrapTest.class
- );
-
- providers.add(
- ( ValidationProvider ) providerClass.newInstance()
- );
- }
- name = reader.readLine();
- }
- }
- finally {
- stream.close();
- }
- }
-
- private static Class<?> loadClass(String name, Class caller) throws ClassNotFoundException {
- try {
- //try context classloader, if fails try caller classloader
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- if ( loader != null ) {
- return loader.loadClass( name );
- }
- }
- catch ( ClassNotFoundException e ) {
- //trying caller classloader
- if ( caller == null ) {
- throw e;
- }
- }
- return Class.forName( name, true, caller.getClassLoader() );
- }
-
-
-//
-// @Test
-// public void testCustomMessageInterpolator() {
-//
-// // first try with the default message resolver
-// Configuration<?> configuration = Validation.byDefaultProvider().configure();
-// assertDefaultBuilderAndFactory( configuration );
-//
-// ValidatorFactory factory = configuration.buildValidatorFactory();
-// Validator validator = factory.getValidator();
-//
-// Customer customer = new Customer();
-// customer.setFirstName( "John" );
-//
-// Set<ConstraintViolation<Customer>> constraintViolations = validator.validate( customer );
-// assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
-// ConstraintViolation<Customer> constraintViolation = constraintViolations.iterator().next();
-// assertEquals( "may not be null", constraintViolation.getMessage(), "Wrong message" );
-//
-// // now we modify the configuration, get a new factory and valiator and try again
-// configuration = Validation.byDefaultProvider().configure();
-// configuration.messageInterpolator(
-// new MessageInterpolator() {
-// public String interpolate(String message, Context context) {
-// return "my custom message";
-// }
-//
-// public String interpolate(String message, Context context, Locale locale) {
-// throw new UnsupportedOperationException( "No specific locale is possible" );
-// }
-// }
-// );
-// factory = configuration.buildValidatorFactory();
-// validator = factory.getValidator();
-// constraintViolations = validator.validate( customer );
-// assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
-// constraintViolation = constraintViolations.iterator().next();
-// assertEquals( "my custom message", constraintViolation.getMessage(), "Wrong message" );
-// }
-//
-// @Test
-// public void testCustomConstraintValidatorFactory() {
-//
-// Configuration<?> configuration = Validation.byDefaultProvider().configure();
-// assertDefaultBuilderAndFactory( configuration );
-//
-// ValidatorFactory factory = configuration.buildValidatorFactory();
-// Validator validator = factory.getValidator();
-//
-// Customer customer = new Customer();
-// customer.setFirstName( "John" );
-//
-// Set<ConstraintViolation<Customer>> constraintViolations = validator.validate( customer );
-// assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
-// ConstraintViolation<Customer> constraintViolation = constraintViolations.iterator().next();
-// assertEquals( "may not be null", constraintViolation.getMessage(), "Wrong message" );
-//
-// // get a new factory using a custom configuration
-// configuration = Validation.byDefaultProvider().configure();
-// configuration.constraintValidatorFactory(
-// new ConstraintValidatorFactory() {
-//
-// public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
-// if ( key == NotNullValidator.class ) {
-// return ( T ) new BadlyBehavedNotNullConstraintValidator();
-// }
-// return new ConstraintValidatorFactoryImpl().getInstance( key );
-// }
-// }
-// );
-// factory = configuration.buildValidatorFactory();
-// validator = factory.getValidator();
-// constraintViolations = validator.validate( customer );
-// assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
-// }
-//
-// @Test
-// public void testCustomResolverAndType() {
-// ValidationProviderResolver resolver = new ValidationProviderResolver() {
-//
-// public List<ValidationProvider> getValidationProviders() {
-// List<ValidationProvider> list = new ArrayList<ValidationProvider>();
-// list.add( new HibernateValidationProvider() );
-// return list;
-// }
-// };
-//
-//
-// HibernateValidatorConfiguration configuration = Validation
-// .byProvider( HibernateValidatorConfiguration.class )
-// .providerResolver( resolver )
-// .configure();
-// assertDefaultBuilderAndFactory( configuration );
-// }
-//
-// @Test
-// public void testCustomResolver() {
-// ValidationProviderResolver resolver = new ValidationProviderResolver() {
-//
-// public List<ValidationProvider> getValidationProviders() {
-// List<ValidationProvider> list = new ArrayList<ValidationProvider>();
-// list.add( new HibernateValidationProvider() );
-// return list;
-// }
-// };
-//
-//
-// Configuration<?> configuration = Validation
-// .byDefaultProvider()
-// .providerResolver( resolver )
-// .configure();
-// assertDefaultBuilderAndFactory( configuration );
-// }
-//
-// @Test
-// public void testFailingCustomResolver() {
-// ValidationProviderResolver resolver = new ValidationProviderResolver() {
-//
-// public List<ValidationProvider> getValidationProviders() {
-// return new ArrayList<ValidationProvider>();
-// }
-// };
-//
-// final ProviderSpecificBootstrap<HibernateValidatorConfiguration> providerSpecificBootstrap =
-// Validation
-// .byProvider( HibernateValidatorConfiguration.class )
-// .providerResolver( resolver );
-//
-// try {
-// providerSpecificBootstrap.configure();
-// fail();
-// }
-// catch ( ValidationException e ) {
-// assertEquals(
-// "Unable to find provider: interface org.hibernate.validation.engine.HibernateValidatorConfiguration",
-// e.getMessage(),
-// "Wrong error message"
-// );
-// }
-// }
-//
-// private void assertDefaultBuilderAndFactory(Configuration configuration) {
-// assertNotNull( configuration );
-// assertTrue( configuration instanceof ConfigurationImpl );
-//
-// ValidatorFactory factory = configuration.buildValidatorFactory();
-// assertDefaultFactory( factory );
-// }
-//
-// private void assertDefaultFactory(ValidatorFactory factory) {
-// assertNotNull( factory );
-// assertTrue( factory instanceof ValidatorFactoryImpl );
-// }
-//
-// class BadlyBehavedNotNullConstraintValidator extends NotNullValidator {
-// @Override
-// public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
-// return true;
-// }
-// }
-//
-// @Test
-// public void testCustomTraversableResolver() {
-//
-// Configuration<?> configuration = Validation.byDefaultProvider().configure();
-// assertDefaultBuilderAndFactory( configuration );
-//
-// ValidatorFactory factory = configuration.buildValidatorFactory();
-// Validator validator = factory.getValidator();
-//
-// Customer customer = new Customer();
-// customer.setFirstName( "John" );
-//
-// Set<ConstraintViolation<Customer>> constraintViolations = validator.validate( customer );
-// assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
-// ConstraintViolation<Customer> constraintViolation = constraintViolations.iterator().next();
-// assertEquals( "may not be null", constraintViolation.getMessage(), "Wrong message" );
-//
-// // get a new factory using a custom configuration
-// configuration = Validation.byDefaultProvider().configure();
-// configuration.traversableResolver(
-// new TraversableResolver() {
-// public boolean isReachable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
-// return false;
-// }
-//
-// public boolean isCascadable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
-// return false;
-// }
-// }
-// );
-// factory = configuration.buildValidatorFactory();
-// validator = factory.getValidator();
-// constraintViolations = validator.validate( customer );
-// assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
-// }
-//}
-
-
-}
Modified: beanvalidation/trunk/validation-tck/src/main/resources/tck-audit.xml
===================================================================
--- beanvalidation/trunk/validation-tck/src/main/resources/tck-audit.xml 2009-06-10 14:04:41 UTC (rev 16738)
+++ beanvalidation/trunk/validation-tck/src/main/resources/tck-audit.xml 2009-06-10 14:05:26 UTC (rev 16739)
@@ -677,7 +677,7 @@
</section>
<section id="4.4" title="Bootstrapping">
<assertion id="a">
- <text>a bootstrap implementation must be able to bootstrap any Bean Validation provider
+ <text>A bootstrap implementation must be able to bootstrap any Bean Validation provider
implementation</text>
</assertion>
</section>
15 years, 7 months
Hibernate SVN: r16738 - validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-06-10 10:04:41 -0400 (Wed, 10 Jun 2009)
New Revision: 16738
Modified:
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java
Log:
Moved some bootstrap tests into the TCK
Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java 2009-06-10 10:47:12 UTC (rev 16737)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/bootstrap/ValidationTest.java 2009-06-10 14:04:41 UTC (rev 16738)
@@ -17,33 +17,21 @@
*/
package org.hibernate.validation.bootstrap;
-import java.lang.annotation.ElementType;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
import java.util.Set;
import javax.validation.Configuration;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.ConstraintViolation;
-import javax.validation.MessageInterpolator;
-import javax.validation.TraversableResolver;
import javax.validation.Validation;
-import javax.validation.ValidationException;
-import javax.validation.ValidationProviderResolver;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
-import javax.validation.bootstrap.ProviderSpecificBootstrap;
-import javax.validation.spi.ValidationProvider;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
import org.testng.annotations.Test;
-import org.hibernate.validation.HibernateValidationProvider;
import org.hibernate.validation.constraints.NotNullValidator;
import org.hibernate.validation.engine.ConfigurationImpl;
import org.hibernate.validation.engine.ConstraintValidatorFactoryImpl;
@@ -73,65 +61,6 @@
}
@Test
- public void testGetCustomerValidator() {
- Configuration<?> configuration = Validation.byDefaultProvider().configure();
- assertDefaultBuilderAndFactory( configuration );
-
- ValidatorFactory factory = configuration.buildValidatorFactory();
- Validator validator = factory.getValidator();
-
- Customer customer = new Customer();
- customer.setFirstName( "John" );
-
- Set<ConstraintViolation<Customer>> constraintViolations = validator.validate( customer );
- assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
-
- customer.setLastName( "Doe" );
-
- constraintViolations = validator.validate( customer );
- assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
- }
-
- @Test
- public void testCustomMessageInterpolator() {
-
- // first try with the default message resolver
- Configuration<?> configuration = Validation.byDefaultProvider().configure();
- assertDefaultBuilderAndFactory( configuration );
-
- ValidatorFactory factory = configuration.buildValidatorFactory();
- Validator validator = factory.getValidator();
-
- Customer customer = new Customer();
- customer.setFirstName( "John" );
-
- Set<ConstraintViolation<Customer>> constraintViolations = validator.validate( customer );
- assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
- ConstraintViolation<Customer> constraintViolation = constraintViolations.iterator().next();
- assertEquals( "may not be null", constraintViolation.getMessage(), "Wrong message" );
-
- // now we modify the configuration, get a new factory and valiator and try again
- configuration = Validation.byDefaultProvider().configure();
- configuration.messageInterpolator(
- new MessageInterpolator() {
- public String interpolate(String message, Context context) {
- return "my custom message";
- }
-
- public String interpolate(String message, Context context, Locale locale) {
- throw new UnsupportedOperationException( "No specific locale is possible" );
- }
- }
- );
- factory = configuration.buildValidatorFactory();
- validator = factory.getValidator();
- constraintViolations = validator.validate( customer );
- assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
- constraintViolation = constraintViolations.iterator().next();
- assertEquals( "my custom message", constraintViolation.getMessage(), "Wrong message" );
- }
-
- @Test
public void testCustomConstraintValidatorFactory() {
Configuration<?> configuration = Validation.byDefaultProvider().configure();
@@ -167,71 +96,6 @@
assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
}
- @Test
- public void testCustomResolverAndType() {
- ValidationProviderResolver resolver = new ValidationProviderResolver() {
-
- public List<ValidationProvider> getValidationProviders() {
- List<ValidationProvider> list = new ArrayList<ValidationProvider>();
- list.add( new HibernateValidationProvider() );
- return list;
- }
- };
-
-
- HibernateValidatorConfiguration configuration = Validation
- .byProvider( HibernateValidatorConfiguration.class )
- .providerResolver( resolver )
- .configure();
- assertDefaultBuilderAndFactory( configuration );
- }
-
- @Test
- public void testCustomResolver() {
- ValidationProviderResolver resolver = new ValidationProviderResolver() {
-
- public List<ValidationProvider> getValidationProviders() {
- List<ValidationProvider> list = new ArrayList<ValidationProvider>();
- list.add( new HibernateValidationProvider() );
- return list;
- }
- };
-
-
- Configuration<?> configuration = Validation
- .byDefaultProvider()
- .providerResolver( resolver )
- .configure();
- assertDefaultBuilderAndFactory( configuration );
- }
-
- @Test
- public void testFailingCustomResolver() {
- ValidationProviderResolver resolver = new ValidationProviderResolver() {
-
- public List<ValidationProvider> getValidationProviders() {
- return new ArrayList<ValidationProvider>();
- }
- };
-
- final ProviderSpecificBootstrap<HibernateValidatorConfiguration> providerSpecificBootstrap =
- Validation
- .byProvider( HibernateValidatorConfiguration.class )
- .providerResolver( resolver );
-
- try {
- providerSpecificBootstrap.configure();
- fail();
- }
- catch ( ValidationException e ) {
- assertEquals(
- "Unable to find provider: interface org.hibernate.validation.engine.HibernateValidatorConfiguration",
- e.getMessage(),
- "Wrong error message"
- );
- }
- }
-
private void assertDefaultBuilderAndFactory(Configuration configuration) {
assertNotNull( configuration );
assertTrue( configuration instanceof ConfigurationImpl );
@@ -251,40 +115,4 @@
return true;
}
}
-
- @Test
- public void testCustomTraversableResolver() {
-
- Configuration<?> configuration = Validation.byDefaultProvider().configure();
- assertDefaultBuilderAndFactory( configuration );
-
- ValidatorFactory factory = configuration.buildValidatorFactory();
- Validator validator = factory.getValidator();
-
- Customer customer = new Customer();
- customer.setFirstName( "John" );
-
- Set<ConstraintViolation<Customer>> constraintViolations = validator.validate( customer );
- assertEquals( constraintViolations.size(), 1, "Wrong number of constraints" );
- ConstraintViolation<Customer> constraintViolation = constraintViolations.iterator().next();
- assertEquals( "may not be null", constraintViolation.getMessage(), "Wrong message" );
-
- // get a new factory using a custom configuration
- configuration = Validation.byDefaultProvider().configure();
- configuration.traversableResolver(
- new TraversableResolver() {
- public boolean isReachable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
- return false;
- }
-
- public boolean isCascadable(Object traversableObject, String traversableProperty, Class<?> rootBeanType, String pathToTraversableObject, ElementType elementType) {
- return false;
- }
- }
- );
- factory = configuration.buildValidatorFactory();
- validator = factory.getValidator();
- constraintViolations = validator.validate( customer );
- assertEquals( constraintViolations.size(), 0, "Wrong number of constraints" );
- }
}
15 years, 7 months
Hibernate SVN: r16737 - validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-06-10 06:47:12 -0400 (Wed, 10 Jun 2009)
New Revision: 16737
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java
Log:
formatting
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java 2009-06-10 10:46:49 UTC (rev 16736)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java 2009-06-10 10:47:12 UTC (rev 16737)
@@ -50,12 +50,10 @@
return configurationClass.cast( new ConfigurationImpl( this ) );
}
-
public Configuration<?> createGenericConfiguration(BootstrapState state) {
return new ConfigurationImpl( state );
}
-
public ValidatorFactory buildValidatorFactory(ConfigurationState configurationState) {
return new ValidatorFactoryImpl( configurationState );
}
15 years, 7 months
Hibernate SVN: r16736 - validator/trunk/hibernate-validator-tck-runner.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-06-10 06:46:49 -0400 (Wed, 10 Jun 2009)
New Revision: 16736
Modified:
validator/trunk/hibernate-validator-tck-runner/pom.xml
Log:
Attached dumpArtifacts to different life cycle. It should not matter if tests fails to dump the artifacts.
Modified: validator/trunk/hibernate-validator-tck-runner/pom.xml
===================================================================
--- validator/trunk/hibernate-validator-tck-runner/pom.xml 2009-06-10 10:45:54 UTC (rev 16735)
+++ validator/trunk/hibernate-validator-tck-runner/pom.xml 2009-06-10 10:46:49 UTC (rev 16736)
@@ -222,7 +222,7 @@
<executions>
<execution>
<id>generate-jsr-303-artifacts</id>
- <phase>test</phase>
+ <phase>test-compile</phase>
<goals>
<goal>java</goal>
</goals>
15 years, 7 months
Hibernate SVN: r16735 - validator/trunk/hibernate-validator-tck-runner/src/main/resources/META-INF.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-06-10 06:45:54 -0400 (Wed, 10 Jun 2009)
New Revision: 16735
Modified:
validator/trunk/hibernate-validator-tck-runner/src/main/resources/META-INF/jboss-test-harness.properties
Log:
added org.jboss.testharness.testPackage to make dumpArtifacts work out of the box
Modified: validator/trunk/hibernate-validator-tck-runner/src/main/resources/META-INF/jboss-test-harness.properties
===================================================================
--- validator/trunk/hibernate-validator-tck-runner/src/main/resources/META-INF/jboss-test-harness.properties 2009-06-10 10:44:41 UTC (rev 16734)
+++ validator/trunk/hibernate-validator-tck-runner/src/main/resources/META-INF/jboss-test-harness.properties 2009-06-10 10:45:54 UTC (rev 16735)
@@ -1,2 +1,3 @@
org.jboss.testharness.spi.StandaloneContainers=org.hibernate.jsr303.tck.util.StandaloneContainersImpl
org.jboss.testharness.api.TestLauncher=org.jboss.testharness.impl.runner.servlet.ServletTestLauncher
+org.jboss.testharness.testPackage=org.hibernate.jsr303.tck.tests
\ No newline at end of file
15 years, 7 months