Second batch of doco updates

git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@4366 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Christian Bauer 2004-08-17 16:03:26 +00:00
parent 7225b36735
commit 8c6da481a6
9 changed files with 752 additions and 301 deletions

View File

@ -62,22 +62,22 @@
<sect2 id="mapping-declaration-doctype">
<sect2 id="mapping-declaration-doctype" revision="1">
<title>Doctype</title>
<para>
All XML mappings should declare the doctype shown. The actual DTD may be found
at the URL above, in the directory <literal>hibernate-x.x.x/src/net/sf/hibernate
</literal> or in <literal>hibernate.jar</literal>. Hibernate will always look for
at the URL above, in the directory <literal>hibernate-x.x.x/src/org/hibernate
</literal> or in <literal>hibernate3.jar</literal>. Hibernate will always look for
the DTD in its classpath first.
</para>
</sect2>
<sect2 id="mapping-declaration-mapping">
<sect2 id="mapping-declaration-mapping" revision="1">
<title>hibernate-mapping</title>
<para>
This element has three optional attributes. The <literal>schema</literal> attribute
This element has several optional attributes. The <literal>schema</literal> attribute
specifies that tables referred to by this mapping belong to the named schema. If specified,
tablenames will be qualified by the given schema name. If missing, tablenames will be
unqualified. The <literal>default-cascade</literal> attribute specifies what cascade style
@ -92,10 +92,12 @@
<area id="hm2" coords="3 55"/>
<area id="hm3" coords="4 55"/>
<area id="hm4" coords="5 55"/>
</areaspec>
<area id="hm5" coords="6 55"/>
</areaspec>
<programlisting><![CDATA[<hibernate-mapping
schema="schemaName"
default-cascade="none|save-update"
default-access="field|property|ClassName"
auto-import="true|false"
package="package.name"
/>]]></programlisting>
@ -112,13 +114,20 @@
</para>
</callout>
<callout arearefs="hm3">
<para>
<literal>default-access</literal> (optional - defaults to <literal>property</literal>):
The strategy Hibernate should use for accessing all properties. Can be a custom
implementation of <literal>PropertyAccessor</literal>.
</para>
</callout>
<callout arearefs="hm4">
<para>
<literal>auto-import</literal> (optional - defaults to <literal>true</literal>):
Specifies whether we can use unqualified class names (of classes in this mapping)
in the query language.
</para>
</callout>
<callout arearefs="hm4">
<callout arearefs="hm5">
<para>
<literal>package</literal> (optional): Specifies a package prefix to assume for
unqualified class names in the mapping document.
@ -133,10 +142,10 @@
to assign two classes to the same "imported" name.
</para>
</sect2>
</sect2>
<sect2 id="mapping-declaration-class">
<title>class</title>
<sect2 id="mapping-declaration-class" revision="1">
<title>class, dynamic-class</title>
<para>
You may declare a persistent class using the <literal>class</literal> element:
@ -159,7 +168,12 @@
<area id="class13" coords="14 55"/>
<area id="class14" coords="15 55"/>
<area id="class15" coords="16 55"/>
</areaspec>
<area id="class16" coords="17 55"/>
<area id="class17" coords="18 55"/>
<area id="class18" coords="19 55"/>
<area id="class19" coords="20 55"/>
<area id="class20" coords="21 55"/>
</areaspec>
<programlisting><![CDATA[<class
name="ClassName"
table="tableName"
@ -176,6 +190,11 @@
batch-size="N"
optimistic-lock="none|version|dirty|all"
lazy="true|false"
entity-name="EntityName"
catalog="catalog"
check="arbitrary sql check condition"
rowid="TODO"
subselect="TODO"
/>]]></programlisting>
<calloutlist>
<callout arearefs="class1">
@ -274,6 +293,33 @@
interface.
</para>
</callout>
<callout arearefs="class16">
<para>
<literal>entity-name</literal> (optional): TODO
</para>
</callout>
<callout arearefs="class17">
<para>
<literal>catalog</literal> (optional): The name of a database catalog used for this
class and its table.
</para>
</callout>
<callout arearefs="class18">
<para>
<literal>check</literal> (optional): A SQL expression used to generate a multi-row
<emphasis>check</emphasis> constraint for automatic schema generation.
</para>
</callout>
<callout arearefs="class19">
<para>
<literal>rowid</literal> (optional): TODO
</para>
</callout>
<callout arearefs="class19">
<para>
<literal>subselect</literal> (optional): TODO
</para>
</callout>
</calloutlist>
</programlistingco>
@ -367,6 +413,69 @@
what <literal>unsaved-value</literal> strategy, or an instance will be detected as
transient.
</para>
<para>
TODO: Document entity name and dynamic class
</para>
<para>
TODO: Document subselect and and synchronize for view simulation
</para>
</sect2>
<sect2 id="mapping-declaration-join">
<title>join</title>
<para>
Using the <literal>&lt;join&gt;</literal> element, it is possible to map
properties of one class to several tables.
</para>
<programlistingco>
<areaspec>
<area id="join1" coords="2 50"/>
<area id="join2" coords="3 50" />
<area id="join3" coords="4 50"/>
<area id="join4" coords="5 50" />
</areaspec>
<programlisting><![CDATA[<join
table="tablename"
schema="owner"
catalog="catalog"
sequential-select="true|false"
/>]]></programlisting>
<calloutlist>
<callout arearefs="join1">
<para>
<literal>tabe</literal>: The name of the joined table.
</para>
</callout>
<callout arearefs="join2">
<para>
<literal>schema</literal> (optional): Override the schema name specified by
the root <literal>&lt;hibernate-mapping&gt;</literal> element.
</para>
</callout>
<callout arearefs="join3">
<para>
<literal>catalog</literal> (optional): Name of the database catalog.
</para>
</callout>
<callout arearefs="join4">
<para>
<literal>sequential-select</literal> (optional - defaults to <literal>true</literal>):
TODO: Document join behavior
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
TODO: Document join with an example
</para>
</sect2>
<sect2 id="mapping-declaration-id">
@ -445,7 +554,7 @@
legacy data with composite keys. We strongly discourage its use for anything else.
</para>
<sect3 id="mapping-declaration-id-generator">
<sect3 id="mapping-declaration-id-generator" revision="1">
<title>generator</title>
<para>
@ -544,6 +653,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><literal>guid</literal></term>
<listitem>
<para>
uses the string-based GUID algorithm available in MS SQL Server and MySQL.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>native</literal></term>
<listitem>
<para>
@ -738,7 +855,7 @@
</sect2>
<sect2 id="mapping-declaration-discriminator" revision="1">
<sect2 id="mapping-declaration-discriminator" revision="2">
<title>discriminator</title>
<para>
@ -757,12 +874,14 @@
<area id="discriminator2" coords="3 40" />
<area id="discriminator3" coords="4 40" />
<area id="discriminator4" coords="5 40" />
<area id="discriminator5" coords="6 40" />
</areaspec>
<programlisting><![CDATA[<discriminator
column="discriminator_column"
type="discriminator_type"
force="true|false"
insert="true|false"
formula="arbitrary sql expression"
/>]]></programlisting>
<calloutlist>
<callout arearefs="discriminator1">
@ -791,6 +910,12 @@
of a mapped composite identifier.
</para>
</callout>
<callout arearefs="discriminator5">
<para>
<literal>formula</literal> (optional)
an arbitrary SQL expression that is executed when a type has to be evaluated
</para>
</callout>
</calloutlist>
</programlistingco>
@ -805,6 +930,14 @@
"extra" discriminator values that are not mapped to a persistent class. This will not
usually be the case.
</para>
<para>
Using the <literal>formula</literal> attribute you can declare an arbitrary SQL expression
that will be used to evaluate the type of a row:
</para>
<programlisting><![CDATA[<discriminator formula="case when CLASS_TYPE in ('a', 'b', 'c') then 0 else 1 end" type="integer"/>]]></programlisting>
</sect2>
<sect2 id="mapping-declaration-version">
@ -936,7 +1069,7 @@
</sect2>
<sect2 id="mapping-declaration-property">
<sect2 id="mapping-declaration-property" revision="1">
<title>property</title>
<para>
@ -1025,11 +1158,6 @@
char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob</literal>).
</para>
</listitem>
<listitem>
<para>
The name of a subclass of <literal>PersistentEnum</literal> (eg. <literal>eg.Color</literal>).
</para>
</listitem>
<listitem>
<para>
The name of a serializable Java class.
@ -1061,6 +1189,10 @@
<literal>org.hibernate.property.PropertyAccessor</literal>.
</para>
<para>
TODO: Document the nested column formula="" attribute with an example
</para>
</sect2>
<sect2 id="mapping-declaration-manytoone" revision="1">
@ -1601,6 +1733,22 @@
</sect2>
<sect2 id="mapping-declaration-unionsubclass">
<title>union-subclass</title>
<para>
Another option is to map only the concrete classes of an inheritance hierarchy
(as in table-per-concrete-class) to tables, but not the superclass. This strategy
has some problems with polymorphic associations, which can be avoided with a
<literal>&lt;union-subclass&gt;</literal> mapping.
</para>
<para>
See the documentation chapter about Inheritance Mapping for more details.
</para>
</sect2>
<sect2 id="mapping-declaration-collections">
<title>map, set, list, bag</title>
@ -1693,7 +1841,7 @@
</para>
</sect2>
<sect2 id="mapping-types-basictypes">
<sect2 id="mapping-types-basictypes" revision="1">
<title>Basic value types</title>
<para>
@ -1798,7 +1946,7 @@
Maps serializable Java types to an appropriate SQL binary type. You
may also indicate the Hibernate type <literal>serializable</literal> with
the name of a serializable Java class or interface that does not default
to a basic type or implement <literal>PersistentEnum</literal>.
to a basic type.
</para>
</listitem>
</varlistentry>
@ -1831,47 +1979,6 @@
</sect2>
<sect2 id="mapping-types-enum">
<title>Persistent enum types</title>
<para>
An <emphasis>enumerated</emphasis> type is a common Java idiom where a class has
a constant (small) number of immutable instances. You may create a persistent
enumerated type by implementing <literal>org.hibernate.PersistentEnum</literal>,
defining the operations <literal>toInt()</literal> and <literal>fromInt()</literal>:
</para>
<programlisting><![CDATA[package eg;
import org.hibernate.PersistentEnum;
public class Color implements PersistentEnum {
private final int code;
private Color(int code) {
this.code = code;
}
public static final Color TABBY = new Color(0);
public static final Color GINGER = new Color(1);
public static final Color BLACK = new Color(2);
public int toInt() { return code; }
public static Color fromInt(int code) {
switch (code) {
case 0: return TABBY;
case 1: return GINGER;
case 2: return BLACK;
default: throw new RuntimeException("Unknown color code");
}
}
}]]></programlisting>
<para>
The Hibernate type name is simply the name of the enumerated class, in this case
<literal>eg.Color</literal>.
</para>
</sect2>
<sect2 id="mapping-types-custom">
<title>Custom value types</title>
@ -2051,5 +2158,126 @@ public class Color implements PersistentEnum {
</sect1>
<sect1 id="mapping-xdoclet">
<title>Using XDOclet markup</title>
<para>
Many Hibernate users prefer to embed mapping information directly in sourcecode using
XDoclet <literal>@hibernate.tags</literal>. We will not cover this approach in this
document, since strictly it is considered part of XDoclet. However, we include the
following example of the <literal>Cat</literal> class with XDoclet mappings.
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
import java.util.Date;
/**
* @hibernate.class
* table="CATS"
*/
public class Cat {
private Long id; // identifier
private Date birthdate;
private Cat mate;
private Set kittens
private Color color;
private char sex;
private float weight;
/**
* @hibernate.id
* generator-class="native"
* column="CAT_ID"
*/
public Long getId() {
return id;
}
private void setId(Long id) {
this.id=id;
}
/**
* @hibernate.many-to-one
* column="MATE_ID"
*/
public Cat getMate() {
return mate;
}
void setMate(Cat mate) {
this.mate = mate;
}
/**
* @hibernate.property
* column="BIRTH_DATE"
*/
public Date getBirthdate() {
return birthdate;
}
void setBirthdate(Date date) {
birthdate = date;
}
/**
* @hibernate.property
* column="WEIGHT"
*/
public float getWeight() {
return weight;
}
void setWeight(float weight) {
this.weight = weight;
}
/**
* @hibernate.property
* column="COLOR"
* not-null="true"
*/
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
/**
* @hibernate.set
* lazy="true"
* order-by="BIRTH_DATE"
* @hibernate.collection-key
* column="PARENT_ID"
* @hibernate.collection-one-to-many
*/
public Set getKittens() {
return kittens;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kittens.add(kitten);
}
/**
* @hibernate.property
* column="SEX"
* not-null="true"
* update="false"
*/
public char getSex() {
return sex;
}
void setSex(char sex) {
this.sex=sex;
}
}]]></programlisting>
<para>
See the Hibernate web site for more examples of XDoclet and Hibernate.
</para>
</sect1>
</chapter>

View File

@ -1,7 +1,7 @@
<chapter id="collections">
<title>Collection Mapping</title>
<sect1 id="collections-persistent">
<sect1 id="collections-persistent" revision="1">
<title>Persistent Collections</title>
<para>
@ -74,9 +74,14 @@ kittens = cat.getKittens(); //Okay, kittens collection is a Set
Collection instances are distinguished in the database by a foreign key to
the owning entity. This foreign key is referred to as the
<emphasis>collection key </emphasis>. The collection key is mapped by
the <literal>&lt;key&gt;</literal> element.
the <literal>&lt;key&gt;</literal> element. If you have a foreign-key constraint
set in the database, and have chosen the <literal>ON DELETE CASCADE</literal>
option, always use the <literal>on-delete</literal> attribute on your
<literal>&lt;key&gt;</literal> mappings:
</para>
<programlisting><![CDATA[<key column="CHILD_ID" on-delete="cascade"/>]]></programlisting>
<para>
Collections may contain almost any other Hibernate type, including all basic types,
custom types, entity types and components. This is an important definition: An object

View File

@ -368,7 +368,7 @@ hibernate.dialect = \
are also not available in the <literal>hibernate.cfg.xml</literal> file, discusse later.
</para>
<table frame="topbot" id="configuration-optional-properties" revision="4">
<table frame="topbot" id="configuration-optional-properties" revision="5">
<title>Hibernate Configuration Properties</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="1*"/>
@ -731,7 +731,19 @@ hibernate.dialect = \
</para>
</entry>
</row>
<row>
<entry>
<literal>hibernate.use_sql_comments</literal>
</entry>
<entry>
If turned on, Hibernate will generate comments inside the SQL, for
easier debugging, defaults to <literal>false</literal>.
<para>
<emphasis role="strong">eg.</emphasis>
<literal>true</literal> | <literal>false</literal>
</para>
</entry>
</row>
</tbody>
</tgroup>
</table>

View File

@ -1,7 +1,7 @@
<chapter id="inheritance">
<title>Inheritance Mapping</title>
<sect1 id="inheritance-strategies" revision="1">
<sect1 id="inheritance-strategies" revision="2">
<title>The Three Strategies</title>
<para>
@ -32,7 +32,8 @@
apply as apply to table-per-concrete class mappings. Hibernate does
not support mixing <literal>&lt;subclass&gt;</literal> mappings and
<literal>&lt;joined-subclass&gt;</literal> mappings inside the same
<literal>&lt;class&gt;</literal> element.
<literal>&lt;class&gt;</literal> element. However, it is possible to
use a <literal>&lt;join&gt;</literal> element to map this.
</para>
<para>
@ -105,6 +106,14 @@
correct from a relational point of view.
</para>
<para>
TODO: document usage of join for discriminators in table-per-subclass
</para>
<para>
TODO: document usage of join for mixing inheritance mapping strategies
</para>
<para>
For either of these two mapping strategies, a polymorphic
association to <literal>Payment</literal> is mapped using
@ -224,11 +233,19 @@
not instances of <literal>NonelectronicTransaction</literal>.
</para>
<para>
TODO: Document union-subclass for polymorphic-table-per-concrete-class mappings
</para>
</sect1>
<sect1 id="inheritance-limitations">
<title>Limitations</title>
<para>
TODO: This section needs to be rewritten, Hibernate3 has almost no limitations on inheritance mappings
</para>
<para>
Hibernate assumes that an association maps to exactly one foreign key column.
Multiple associations per foreign key are tolerated (you might need to specify

View File

@ -1118,6 +1118,124 @@ finally {
</sect1>
<sect1 id="manipulatingdata-filters">
<title>Parameterized application views with filters</title>
<para>
Hibernate3 adds the ability to pre-define filter criteria and attach those filters at both
a class and a collection level. A filter criteria is the ability to define a restriction clause
very similiar to the existing "where" attribute available on the class and various collection
elements. Except these filter conditions can be parameterized. The application can then make
the decision at runtime whether given filters should be enabled and what their parameter
values should be. Filters can be used like database views, but parameterized inside the
application.
</para>
<para>
In order to use filters, they must first be defined and then attached to the appropriate
mapping elements. To define a filter, use the <literal>&lt;filter-def/&gt;</literal> element
within a <literal>&lt;hibernate-mapping/&gt;</literal> element:
</para>
<programlisting><![CDATA[<filter-def name="myFilter">
<filter-param name="myFilterParam" type="string"/>
</filter-def>]]></programlisting>
<para>
Then, this filter can be attached to a class:
</para>
<programlisting><![CDATA[<class name="myClass" ...>
...
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</class>]]></programlisting>
<para>
or, to a collection:
</para>
<programlisting><![CDATA[<set ...>
<filter name="myFilter" condition=":myFilterParam = MY_FILTERED_COLUMN"/>
</set>]]></programlisting>
<para>
or, even to both (or multiples of each) at the same time.
</para>
<para>
The methods on <literal>Session</literal> are: <literal>enableFilter(String filterName)</literal>,
<literal>getEnabledFilter(String filterName)</literal>, and <literal>disableFilter(String filterName)</literal>.
By default, filters are <emphasis>not</emphasis> enabled for a given session; they must be explcitly
enabled through use of the <literal>Session.enabledFilter()</literal> method, which returns an
instance of the <literal>Filter</literal> interface. Using the simple filter defined above, this
would look like:
</para>
<programlisting><![CDATA[session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");]]></programlisting>
<para>
Note that methods on the org.hibernate.Filter interface do allow the method-chaining common to much of Hibernate.
</para>
<para>
A full example, using temporal data with an effective record date pattern:
</para>
<programlisting><![CDATA[<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
<class name="Employee" ...>
...
<many-to-one name="department" column="dept_id" class="Department"/>
<property name="effectiveStartDate" type="date" column="eff_start_dt"/>
<property name="effectiveEndDate" type="date" column="eff_end_dt"/>
...
<!--
Note that this assumes non-terminal records have an eff_end_dt set to
a max db date for simplicity-sake
-->
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>
<class name="Department" ...>
...
<set name="employees" lazy="true">
<key column="dept_id"/>
<one-to-many class="Employee"/>
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</set>
</class>]]></programlisting>
<para>
Then, in order to ensure that you always get back currently effective records, simply
enable the filter on the session prior to retrieving employee data:
</para>
<programlisting><![CDATA[Session session = ...;
session.enabledFilter("effectiveDate").setParameter("asOfDate", new Date());
List results = session.createQuery("from Employee as e where e.salary > :targetSalary")
.setLong("targetSalary", new Long(1000000))
.list();
]]></programlisting>
<para>
In the HQL above, even though we only explicitly mentioned a salary constraint on the results,
because of the enabled filter the query will return only currently active employees who have
a salary greater than a million dollars.
</para>
<para>
Note: if you plan on using filters with outer joining (either through HQL or load fetching) be
careful of the direction of the condition expression. Its safest to set this up for left
outer joining; in general, place the parameter first followed by the column name(s) after
the operator.
</para>
</sect1>
<sect1 id="manipulatingdata-interceptors" revision="1">
<title>Interceptors</title>
<para>
@ -1225,7 +1343,98 @@ public class AuditInterceptor implements Interceptor, Serializable {
<programlisting><![CDATA[new Configuration().setInterceptor( new AuditInterceptor() );]]></programlisting>
</sect1>
<sect1 id="manipulatingdata-events">
<title>Event system</title>
<para>
If you have to react to particular events in your persistence layer, you may
also use the Hibernate3 <emphasis>event</emphasis> architecture. The event
system can be used in addition or as a replacement for interceptors.
</para>
<para>
Essentially all of the methods of the <literal>Session</literal> interface correlate
to an event. You have a <literal>LoadEvent</literal>, a <literal>FlushEvent</literal>, etc
(consult the XML configuration-file DTD or the <literal>org.hibernate.event</literal>
package for the full list of defined event types). When a request is made of one of
these methods, the Hibernate <literal>Session</literal> generates an appropriate
event and passes it to the configured event listener for that type. Out-of-the-box,
these listeners implement the same processing in which those methods always resulted.
However, you are free to implement a customization of one of the listener interfaces
(i.e., the <literal>LoadEvent</literal> is processed by the registered implemenation
of the <literal>LoadEventListener</literal> interface), in which case their
implementation would be responsible for processing any <literal>load()</literal> requests
made of the <literal>Session</literal>.
</para>
<para>
The listeners should be considered effectively singletons; meaning, they are shared between
requests, and thus should not save any state as instance variables. The event objects
themselves, however, do hold a lot of the context needed for processing as they are unique
to each request. Custom event listeners may also make use of the event's context for storage
of any needed processing variables. The context is a simple map, but the default listeners
don't use the context map at all, so don't worry about over-writing internally required
context variables.
</para>
<para>
A custom listener should implement the appropriate interface for the event it wants to
process and/or extend one of the convenience base classes (or even the default event
listeners used by Hibernate out-of-the-box as these are declared non-final for this
purpose). Custom listeners can either be registered programatically through the
<literal>Configuration</literal> object, or specified in the Hibernate configuration
XML (declarative configuration through the properties file is not supported). Here's an
example of a custom load event listener:
</para>
<programlisting><![CDATA[public class MyLoadListener extends DefaultLoadEventListener {
// this is the single method defined by the LoadEventListener interface
public Object onLoad(LoadEvent event, LoadEventListener.LoadType loadType)
throws HibernateException {
if ( !MySecurity.isAuthorized( event.getEntityName(), event.getEntityId() ) ) {
throw MySecurityException("Unauthorized access");
}
return super.onLoad(event, loadType);
}
}]]></programlisting>
<para>
You also need a configuration entry telling Hibernate to use the listener instead
of the default listener:
</para>
<programlisting><![CDATA[<hibernate-configuration>
<session-factory>
...
<listener type="load" class="MyLoadListener"/>
</session-factory>
</hibernate-configuration>]]></programlisting>
<para>
Instead, you may register it programatically:
</para>
<programlisting><![CDATA[Configuration cfg = new Configuration();
cfg.getSessionEventListenerConfig().setLoadEventListener( new MyLoadListener() );]]></programlisting>
<para>
Listeners registered declaratively cannot share instances. If the same class name is
used in multiple <literal>&lt;listener/&gt;</literal> elements, each reference will
result in a seperate instance of that class. If you need the capability to share
listener instances between listener types you must use the programatic registration
approach.
</para>
<para>
Why implement an interface and define the specific type during configuration? Well, a
listener implementation could implement multiple event listener interfaces. Having the
type additionally defined during registration makes it easier to turn custom listeners on
or off during configuration.
</para>
</sect1>
<sect1 id="manipulatingdata-metadata">
<title>Metadata API</title>
<para>

View File

@ -408,6 +408,62 @@ ICat fritz = (ICat) iter.next();]]></programlisting>
</para>
</sect1>
<sect1 id="performance-lazyproperties">
<title>Using lazy property fetching</title>
<para>
Hibernate3 supports the lazy fetching of individual properties. This optimization technique
is also known as <emphasis>fetch groups</emphasis>. Please note that this is mostly a
marketing feature, as in practice, optimizing row reads is much more important than
optimization of column reads. However, only loading some properties of a class might
be useful in extreme cases, when legacy tables have hundreds of columns and the data model
can not be improved.
</para>
<para>
To enable lazy property loading, set the <literal>lazy</literal> attribute on your
particular property mappings:
</para>
<programlisting><![CDATA[<class name="Document">
<id name="id">
<generator class="native"/>
</id>
<property name="name" not-null="true" length="50"/>
<property name="summary" not-null="true" length="200" lazy="true"/>
<property name="text" not-null="true" length="2000" lazy="true"/>
</class>]]></programlisting>
<para>
Lazy property loading requires buildtime bytecode instrumentation! If your persistent
classes are not enhanced, Hibernate will silently ignore lazy property settings and
fall back to immediate fetching.
</para>
<para>
For bytecode instrumentation, use the following Ant task:
</para>
<programlisting><![CDATA[<target name="instrument" depends="compile">
<taskdef name="instrument" classname="org.hibernate.tool.instrument.InstrumentTask">
<classpath path="${jar.path}"/>
<classpath path="${classes.dir}"/>
<classpath refid="lib.class.path"/>
</taskdef>
<instrument verbose="true">
<fileset dir="${testclasses.dir}/org/hibernate/auction/model">
<include name="*.class"/>
</fileset>
</instrument>
</target>]]></programlisting>
<para>
TODO: Document issues with lazy property loading
</para>
</sect1>
<sect1 id="performance-cache" revision="1">
<title>The Second Level Cache</title>

View File

@ -1,4 +1,4 @@
<chapter id="persistent-classes">
<chapter id="persistent-classes" revision="1">
<title>Persistent Classes</title>
<para>
@ -10,7 +10,9 @@
<para>
Hibernate works best if these classes follow some simple rules, also known
as the Plain Old Java Object (POJO) programming model.
as the Plain Old Java Object (POJO) programming model. However, Hibernate3
also allows you to express a domain model as nested dynamic <literal>Map</literal>s,
if required.
</para>
<sect1 id="persistent-classes-pojo">
@ -97,7 +99,7 @@ public class Cat {
</para>
<sect2 id="persistent-classes-pojo-accessors">
<sect2 id="persistent-classes-pojo-accessors" revision="1">
<title>Declare accessors and mutators for persistent fields</title>
<para>
@ -106,7 +108,8 @@ public class Cat {
it is far better to decouple this implementation detail from the persistence
mechanism. Hibernate persists JavaBeans style properties, and recognizes method
names of the form <literal>getFoo</literal>, <literal>isFoo</literal> and
<literal>setFoo</literal>.
<literal>setFoo</literal>. You may however switch to direct field access for
particular properties, if needed.
</para>
<para>
@ -116,13 +119,15 @@ public class Cat {
</para>
</sect2>
<sect2 id="persistent-classes-pojo-constructor">
<title>Implement a default constructor</title>
<sect2 id="persistent-classes-pojo-constructor" revision="1">
<title>Implement a no-argument constructor</title>
<para>
<literal>Cat</literal> has an implicit default (no-argument) constructor. All
<literal>Cat</literal> has a no-argument constructor. All
persistent classes must have a default constructor (which may be non-public) so
Hibernate can instantiate them using <literal>Constructor.newInstance()</literal>.
We recommend to give the constructor at least <emphasis>package</emphasis> visibility
for runtime proxy generation in Hibernate.
</para>
</sect2>
@ -135,7 +140,7 @@ public class Cat {
anything, and its type might have been any primitive type, any primitive "wrapper"
type, <literal>java.lang.String</literal> or <literal>java.util.Date</literal>. (If
your legacy database table has composite keys, you can even use a user-defined class
with properties of these types - see the section on composite identifiers below.)
with properties of these types - see the section on composite identifiers later.)
</para>
<para>
@ -277,234 +282,55 @@ public class DomesticCat extends Cat {
</sect1>
<sect1 id="persistent-classes-lifecycle">
<title>Lifecycle Callbacks</title>
<sect1 id="persistent-classes-dynamic">
<title>Dynamic models</title>
<para>
Optionally, a persistent class might implement the interface
<literal>Lifecycle</literal> which provides some callbacks that allow
the persistent object to perform necessary initialization/cleanup after
save or load and before deletion or update.
Hibernate also supports dynamic domain models, using <literal>Map</literal>s of
<literal>Map</literal>s. With this approach, you don't write persistent classes,
a Hibernate mapping file for each "entity" is sufficient:
</para>
<programlisting><![CDATA[<hibernate-mapping>
<dynamic-class entity-name="TestMap">
<id name="id" type="long" column="ID">
<generator class="sequence"/>
</id>
<property name="name" column="NAME" type="string"/>
<property name="address" column="ADDRESS" type="string"/>
<many-to-one name="parent" column="PARENT_ID" class="TestMap"/>
<bag name="children" inverse="true" lazy="false">
<key column="PARENT_ID"/>
<one-to-many class="TestMap"/>
</bag>
</dynamic-class>
</hibernate-mapping>]]></programlisting>
<para>
<!-- TODO: add xref to interceptor -->
The Hibernate <literal>Interceptor</literal> offers a less intrusive
alternative, however.
At runtime, you only use <literal>Map</literal>s and use the Hibernate entity
name to refer to a particular type.
</para>
<programlistingco>
<areaspec>
<area id="lifecycle1" coords="2 70"/>
<area id="lifecycle2" coords="3 70" />
<area id="lifecycle3" coords="4 70"/>
<area id="lifecycle4" coords="5 70" />
</areaspec>
<programlisting><![CDATA[public interface Lifecycle {
public boolean onSave(Session s) throws CallbackException;
public boolean onUpdate(Session s) throws CallbackException;
public boolean onDelete(Session s) throws CallbackException;
public void onLoad(Session s, Serializable id);
}]]></programlisting>
<calloutlist>
<callout arearefs="lifecycle1">
<para>
<literal>onSave</literal> - called just before the object is saved or
inserted
</para>
</callout>
<callout arearefs="lifecycle2">
<para>
<literal>onUpdate</literal> - called just before an object is updated
(when the object is passed to <literal>Session.update()</literal>)
</para>
</callout>
<callout arearefs="lifecycle3">
<para>
<literal>onDelete</literal> - called just before an object is deleted
</para>
</callout>
<callout arearefs="lifecycle4">
<para>
<literal>onLoad</literal> - called just after an object is loaded
</para>
</callout>
</calloutlist>
</programlistingco>
<programlisting><![CDATA[Session s = openSession();
Map parent = new HashMap();
parent.put("type", "TestMap");
parent.put("name", "foo");
parent.put("address", "bar");
Map child = new HashMap();
child.put("type", "TestMap");
child.put("name", "fooTwo");
child.put("address", "barTwo");
child.put("parent", parent);
s.save(parent);
s.save(child);
]]></programlisting>
<!-- TODO: Document user-extension framework in the property and proxy package -->
<para>
<literal>onSave()</literal>, <literal>onDelete()</literal> and
<literal>onUpdate()</literal> may be used to cascade saves and
deletions of dependent objects. This is an alternative to declaring cascaded
operations in the mapping file. <literal>onLoad()</literal> may
be used to initialize transient properties of the object from its persistent
state. It may not be used to load dependent objects since the
<literal>Session</literal> interface may not be invoked from
inside this method. A further intended usage of <literal>onLoad()</literal>,
<literal>onSave()</literal> and <literal>onUpdate()</literal> is to store a
reference to the current <literal>Session</literal> for later use.
TODO: Document user-extension framework in the property and proxy package
</para>
<para>
Note that <literal>onUpdate()</literal> is not called every time the object's
persistent state is updated. It is called only when a transient object is passed
to <literal>Session.update()</literal>.
</para>
<para>
If <literal>onSave()</literal>, <literal>onUpdate()</literal> or
<literal>onDelete()</literal> return <literal>true</literal>, the operation is
silently vetoed. If a <literal>CallbackException</literal> is thrown, the operation
is vetoed and the exception is passed back to the application.
</para>
<para>
Note that <literal>onSave()</literal> is called after an identifier is assigned to
the object, except when native key generation is used.
</para>
</sect1>
<sect1 id="persistent-classes-validatable">
<title>Validatable callback</title>
<para>
If the persistent class needs to check invariants before its state is
persisted, it may implement the following interface:
</para>
<programlisting><![CDATA[public interface Validatable {
public void validate() throws ValidationFailure;
}]]></programlisting>
<para>
The object should throw a <literal>ValidationFailure</literal> if an invariant
was violated. An instance of <literal>Validatable</literal> should not change
its state from inside <literal>validate()</literal>.
</para>
<para>
Unlike the callback methods of the <literal>Lifecycle</literal> interface,
<literal>validate()</literal> might be called at unpredictable times. The
application should not rely upon calls to <literal>validate()</literal> for
business functionality.
</para>
</sect1>
<sect1 id="persistent-classes-xdoclet">
<title>Using XDOclet markup</title>
<para>
In the next chapter we will show how Hibernate mappings may be expressed using
a simple, readable XML format. Many Hibernate users prefer to embed mapping
information directly in sourcecode using XDoclet <literal>@hibernate.tags</literal>.
We will not cover this approach in this document, since strictly it is considered
part of XDoclet. However, we include the following example of the <literal>Cat</literal>
class with XDoclet mappings.
</para>
<programlisting><![CDATA[package eg;
import java.util.Set;
import java.util.Date;
/**
* @hibernate.class
* table="CATS"
*/
public class Cat {
private Long id; // identifier
private Date birthdate;
private Cat mate;
private Set kittens
private Color color;
private char sex;
private float weight;
/**
* @hibernate.id
* generator-class="native"
* column="CAT_ID"
*/
public Long getId() {
return id;
}
private void setId(Long id) {
this.id=id;
}
/**
* @hibernate.many-to-one
* column="MATE_ID"
*/
public Cat getMate() {
return mate;
}
void setMate(Cat mate) {
this.mate = mate;
}
/**
* @hibernate.property
* column="BIRTH_DATE"
*/
public Date getBirthdate() {
return birthdate;
}
void setBirthdate(Date date) {
birthdate = date;
}
/**
* @hibernate.property
* column="WEIGHT"
*/
public float getWeight() {
return weight;
}
void setWeight(float weight) {
this.weight = weight;
}
/**
* @hibernate.property
* column="COLOR"
* not-null="true"
*/
public Color getColor() {
return color;
}
void setColor(Color color) {
this.color = color;
}
/**
* @hibernate.set
* lazy="true"
* order-by="BIRTH_DATE"
* @hibernate.collection-key
* column="PARENT_ID"
* @hibernate.collection-one-to-many
*/
public Set getKittens() {
return kittens;
}
void setKittens(Set kittens) {
this.kittens = kittens;
}
// addKitten not needed by Hibernate
public void addKitten(Cat kitten) {
kittens.add(kitten);
}
/**
* @hibernate.property
* column="SEX"
* not-null="true"
* update="false"
*/
public char getSex() {
return sex;
}
void setSex(char sex) {
this.sex=sex;
}
}]]></programlisting>
</sect1>

View File

@ -1,5 +1,5 @@
<chapter id="querysql">
<title>Native SQL Queries</title>
<chapter id="querysql" revision="1">
<title>Native SQL</title>
<para>
You may also express queries in the native SQL dialect of your database. This is useful if you
@ -8,6 +8,11 @@
Hibernate.
</para>
<para>
Hibernate3 also supports native SQL statements for all create, update, delete, and load
operations.
</para>
<sect1 id="querysql-creating">
<title>Creating a SQL based <literal>Query</literal></title>
@ -101,4 +106,98 @@ List loggedCats = sess.createSQLQuery(sql, "cat", Cat.class)
</sect1>
<sect1 id="querysql-cud">
<title>Custom SQL for CUD</title>
<para>
Hibernate3 can use custom SQL statements for create, update, and delete operations.
The class and collection persisters in Hibernate already contain a set of configuration
time generated strings (insertsql, deletesql, updatesql etc.). The mapping tags
<literal>&lt;sql-insert&gt;</literal>, <literal>&lt;sql-delete&gt;</literal>, and
<literal>&lt;sql-update&gt;</literal> override these strings:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>]]></programlisting>
<para>
The SQL is directly execute in your database, so you are free to use any dialect
you like.
</para>
<para>
Stored procedures are support if the <literal>callable</literal> attribute is set:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>]]></programlisting>
<para>
The stored procedures are in most cases (read: better do it than not) required to
return the number of rows inserted/updated/deleted, as Hibernate has some runtime
checks for the success of the statement. Hibernate always registers the first statement
parameter as a numeric output parameter for the CUD operations:
</para>
<programlisting><![CDATA[CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2)
RETURN NUMBER IS
BEGIN
update PERSON
set
NAME = uname,
where
ID = uid;
return SQL%ROWCOUNT;
END updatePerson;]]></programlisting>
</sect1>
<sect1 id="querysql-load">
<title>Custom SQL for loading</title>
<para>
You may also declare your own SQL (or HQL) queries for entity loading:
</para>
<programlisting><![CDATA[<sql-query name="person">
<return alias="p" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {p.name}, ID AS {p.id} FROM PERSON WHERE ID=? FOR UPDATE
</sql-query>]]></programlisting>
<para>
This is just a named query declaration, as discussed earlier. You may reference this
named query in a class mapping:
</para>
<programlisting><![CDATA[<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class>]]></programlisting>
<para>
TODO: Document synchronized mapping element in named queries
</para>
</sect1>
</chapter>

View File

@ -868,7 +868,7 @@ public class Person implements Serializable, IAuditable {
</sect1>
<sect1 id="toolsetguide-s3">
<sect1 id="toolsetguide-s3" revision="1">
<title>Mapping File Generation</title>
<para>
@ -955,8 +955,7 @@ public class Person implements Serializable, IAuditable {
</listitem>
<listitem>
<para>
For <literal>hibernate.type.Type</literal> custom types and <literal>PersistentEnum</literal>
a simple column is used as well.
For <literal>hibernate.type.Type</literal> custom types a simple column is used as well.
</para>
</listitem>
<listitem>