improved example
git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@4392 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
083a42ea06
commit
f0bcd1cdb6
|
@ -11,8 +11,8 @@
|
||||||
<para>
|
<para>
|
||||||
Hibernate works best if these classes follow some simple rules, also known
|
Hibernate works best if these classes follow some simple rules, also known
|
||||||
as the Plain Old Java Object (POJO) programming model. However, Hibernate3
|
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,
|
allows you to express a domain model in other ways: using trees of
|
||||||
if required.
|
<literal>Map</literal> instances, for example.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<sect1 id="persistent-classes-pojo">
|
<sect1 id="persistent-classes-pojo">
|
||||||
|
@ -28,13 +28,15 @@ import java.util.Date;
|
||||||
|
|
||||||
public class Cat {
|
public class Cat {
|
||||||
private Long id; // identifier
|
private Long id; // identifier
|
||||||
private String name;
|
|
||||||
private Date birthdate;
|
private Date birthdate;
|
||||||
private Cat mate;
|
|
||||||
private Set kittens
|
|
||||||
private Color color;
|
private Color color;
|
||||||
private char sex;
|
private char sex;
|
||||||
private float weight;
|
private float weight;
|
||||||
|
private int litterId;
|
||||||
|
|
||||||
|
private Cat mother;
|
||||||
|
private Set kittens = new HashSet();
|
||||||
|
|
||||||
private void setId(Long id) {
|
private void setId(Long id) {
|
||||||
this.id=id;
|
this.id=id;
|
||||||
|
@ -43,26 +45,13 @@ public class Cat {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMate(Cat mate) {
|
|
||||||
this.mate = mate;
|
|
||||||
}
|
|
||||||
public Cat getMate() {
|
|
||||||
return mate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBirthdate(Date date) {
|
void setBirthdate(Date date) {
|
||||||
birthdate = date;
|
birthdate = date;
|
||||||
}
|
}
|
||||||
public Date getBirthdate() {
|
public Date getBirthdate() {
|
||||||
return birthdate;
|
return birthdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setWeight(float weight) {
|
void setWeight(float weight) {
|
||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
}
|
}
|
||||||
|
@ -76,22 +65,40 @@ public class Cat {
|
||||||
void setColor(Color color) {
|
void setColor(Color color) {
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSex(char sex) {
|
||||||
|
this.sex=sex;
|
||||||
|
}
|
||||||
|
public char getSex() {
|
||||||
|
return sex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLitterId(int id) {
|
||||||
|
this.litterId = id;
|
||||||
|
}
|
||||||
|
public int getLitterId() {
|
||||||
|
return litterId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMother(Cat mother) {
|
||||||
|
this.mother = mother;
|
||||||
|
}
|
||||||
|
public Cat getMother() {
|
||||||
|
return mother;
|
||||||
|
}
|
||||||
void setKittens(Set kittens) {
|
void setKittens(Set kittens) {
|
||||||
this.kittens = kittens;
|
this.kittens = kittens;
|
||||||
}
|
}
|
||||||
public Set getKittens() {
|
public Set getKittens() {
|
||||||
return kittens;
|
return kittens;
|
||||||
}
|
}
|
||||||
|
|
||||||
// addKitten not needed by Hibernate
|
// addKitten not needed by Hibernate
|
||||||
public void addKitten(Cat kitten) {
|
public void addKitten(Cat kitten) {
|
||||||
|
kitten.setMother(this);
|
||||||
|
kitten.setLitterId( kittens.size() );
|
||||||
kittens.add(kitten);
|
kittens.add(kitten);
|
||||||
}
|
}
|
||||||
void setSex(char sex) {
|
|
||||||
this.sex=sex;
|
|
||||||
}
|
|
||||||
public char getSex() {
|
|
||||||
return sex;
|
|
||||||
}
|
|
||||||
}]]></programlisting>
|
}]]></programlisting>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -126,8 +133,8 @@ public class Cat {
|
||||||
<literal>Cat</literal> has a 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
|
persistent classes must have a default constructor (which may be non-public) so
|
||||||
Hibernate can instantiate them using <literal>Constructor.newInstance()</literal>.
|
Hibernate can instantiate them using <literal>Constructor.newInstance()</literal>.
|
||||||
We recommend to give the constructor at least <emphasis>package</emphasis> visibility
|
We recommend having a constructor with at least <emphasis>package</emphasis>
|
||||||
for runtime proxy generation in Hibernate.
|
visibility for runtime proxy generation in Hibernate.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
@ -157,7 +164,8 @@ public class Cat {
|
||||||
<itemizedlist spacing="compact">
|
<itemizedlist spacing="compact">
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Cascaded updates (see "Lifecycle Objects")
|
Transitive reattachment for detached objects (cascade update) -
|
||||||
|
see "Lifecycle Objects"
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -182,8 +190,8 @@ public class Cat {
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
You can persist <literal>final</literal> classes that do not implement an interface
|
You can persist <literal>final</literal> classes that do not implement an interface
|
||||||
with Hibernate, but you won't be able to use proxies - which will limit your options
|
with Hibernate, but you won't be able to use proxies for lazy associationfetching -
|
||||||
for performance tuning somewhat.
|
which will limit your options for performance tuning.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
@ -194,7 +202,7 @@ public class Cat {
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
A subclass must also observe the first and second rules. It inherits its
|
A subclass must also observe the first and second rules. It inherits its
|
||||||
identifier property from <literal>Cat</literal>.
|
identifier property from the superclass, <literal>Cat</literal>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<programlisting><![CDATA[package eg;
|
<programlisting><![CDATA[package eg;
|
||||||
|
@ -216,20 +224,29 @@ public class DomesticCat extends Cat {
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
|
You have to override the <literal>equals()</literal> and <literal>hashCode()</literal>
|
||||||
methods if you intend to mix objects of persistent classes (e.g. in a <literal>Set</literal>).
|
methods if you
|
||||||
</para>
|
</para>
|
||||||
|
<itemizedlist spacing="compact">
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
intend to put instances of persistent classes in a <literal>Set</literal>
|
||||||
|
(the recommended way to represent many-valued associations)
|
||||||
|
<emphasis>and</emphasis>
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
intend to use reattachment of detached instances
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<emphasis>This only applies if these objects are loaded in two different
|
Hibernate guarantees equivalence of persistent identity (database row) and Java identity
|
||||||
<literal>Session</literal>s, as Hibernate only guarantees JVM identity (<literal> a == b </literal>,
|
only inside a particular session scope. So as soon as we mix instances retrieved in
|
||||||
the default implementation of <literal>equals()</literal>) inside a single
|
different sessions, we must implement <literal>equals()</literal> and
|
||||||
<literal>Session</literal>!</emphasis>
|
<literal>hashCode()</literal> if we wish to have meaningful semantics for
|
||||||
</para>
|
<literal>Set</literal>s.
|
||||||
|
|
||||||
<para>
|
|
||||||
Even if both objecs <literal>a</literal> and <literal>b</literal> are the same database row
|
|
||||||
(they have the same primary key value as their identifier), we can't guarantee that they are
|
|
||||||
the same Java instance outside of a particular <literal>Session</literal> context.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -237,9 +254,9 @@ public class DomesticCat extends Cat {
|
||||||
by comparing the identifier value of both objects. If the value is the same, both must
|
by comparing the identifier value of both objects. If the value is the same, both must
|
||||||
be the same database row, they are therefore equal (if both are added to a <literal>Set</literal>,
|
be the same database row, they are therefore equal (if both are added to a <literal>Set</literal>,
|
||||||
we will only have one element in the <literal>Set</literal>). Unfortunately, we can't use that
|
we will only have one element in the <literal>Set</literal>). Unfortunately, we can't use that
|
||||||
approach. Hibernate will only assign identifier values to objects that are persistent,
|
approach with generated identifiers! Hibernate will only assign identifier values to objects
|
||||||
a newly created instance will not have any identifier value! We recommend implementing
|
that are persistent, a newly created instance will not have any identifier value! We recommend
|
||||||
<literal>equals()</literal> and <literal>hashCode()</literal> using
|
implementing <literal>equals()</literal> and <literal>hashCode()</literal> using
|
||||||
<emphasis>Business key equality</emphasis>.
|
<emphasis>Business key equality</emphasis>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -254,32 +271,25 @@ public class DomesticCat extends Cat {
|
||||||
...
|
...
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (this == other) return true;
|
if (this == other) return true;
|
||||||
if (!(other instanceof Cat)) return false;
|
if ( !(other instanceof Cat) ) return false;
|
||||||
|
|
||||||
final Cat cat = (Cat) other;
|
final Cat cat = (Cat) other;
|
||||||
|
|
||||||
if (!getName().equals(cat.getName())) return false;
|
if ( !cat.getLitterId().equals( getLitterId() ) ) return false;
|
||||||
if (!getBirthday().equals(cat.getBirthday())) return false;
|
if ( !cat.getMother().equals( getMother() ) ) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result;
|
int result;
|
||||||
result = getName().hashCode();
|
result = getMother().hashCode();
|
||||||
result = 29 * result + getBirthday().hashCode();
|
result = 29 * result + getLitterId();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}]]></programlisting>
|
}]]></programlisting>
|
||||||
|
|
||||||
<para>
|
|
||||||
Keep in mind that our candidate key (in this case a composite of name and birthday)
|
|
||||||
has to be only valid for a particular comparison operation (maybe even only in a
|
|
||||||
single use case). We don't need the stability criteria we usually apply to a real
|
|
||||||
primary key!
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="persistent-classes-dynamic">
|
<sect1 id="persistent-classes-dynamic">
|
||||||
|
@ -290,42 +300,62 @@ public class DomesticCat extends Cat {
|
||||||
<literal>Map</literal>s. With this approach, you don't write persistent classes,
|
<literal>Map</literal>s. With this approach, you don't write persistent classes,
|
||||||
a Hibernate mapping file for each "entity" is sufficient:
|
a Hibernate mapping file for each "entity" is sufficient:
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<!-- TODO: move this somewhere else, we didn't get up to mapings yet!! -->
|
||||||
|
|
||||||
<programlisting><![CDATA[<hibernate-mapping>
|
<programlisting><![CDATA[<hibernate-mapping>
|
||||||
|
|
||||||
<dynamic-class entity-name="TestMap">
|
<dynamic-class entity-name="TestMap">
|
||||||
<id name="id" type="long" column="ID">
|
|
||||||
|
<id name="id"
|
||||||
|
type="long"
|
||||||
|
column="ID">
|
||||||
<generator class="sequence"/>
|
<generator class="sequence"/>
|
||||||
</id>
|
</id>
|
||||||
<property name="name" column="NAME" type="string"/>
|
|
||||||
<property name="address" column="ADDRESS" type="string"/>
|
<property name="name"
|
||||||
<many-to-one name="parent" column="PARENT_ID" class="TestMap"/>
|
column="NAME"
|
||||||
<bag name="children" inverse="true" lazy="false">
|
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"
|
||||||
|
cascade="all">
|
||||||
<key column="PARENT_ID"/>
|
<key column="PARENT_ID"/>
|
||||||
<one-to-many class="TestMap"/>
|
<one-to-many class="TestMap"/>
|
||||||
</bag>
|
</bag>
|
||||||
|
|
||||||
</dynamic-class>
|
</dynamic-class>
|
||||||
|
|
||||||
</hibernate-mapping>]]></programlisting>
|
</hibernate-mapping>]]></programlisting>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
At runtime, you only use <literal>Map</literal>s and use the Hibernate entity
|
At runtime, you just instantiate <literal>HashMap</literal>s and use
|
||||||
name to refer to a particular type.
|
the Hibernate entity name to refer to a particular type.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<programlisting><![CDATA[Session s = openSession();
|
<programlisting><![CDATA[Session s = openSession();
|
||||||
|
Transaction t = s.beginTransaction();
|
||||||
Map parent = new HashMap();
|
Map parent = new HashMap();
|
||||||
parent.put("type", "TestMap");
|
|
||||||
parent.put("name", "foo");
|
parent.put("name", "foo");
|
||||||
parent.put("address", "bar");
|
parent.put("address", "bar");
|
||||||
|
|
||||||
Map child = new HashMap();
|
Map child = new HashMap();
|
||||||
child.put("type", "TestMap");
|
|
||||||
child.put("name", "fooTwo");
|
child.put("name", "fooTwo");
|
||||||
child.put("address", "barTwo");
|
child.put("address", "barTwo");
|
||||||
child.put("parent", parent);
|
child.put("parent", parent);
|
||||||
|
|
||||||
s.save(parent);
|
s.save("TestMap", parent);
|
||||||
s.save(child);
|
t.commit();
|
||||||
]]></programlisting>
|
s.close();]]></programlisting>
|
||||||
|
|
||||||
<!-- TODO: Document user-extension framework in the property and proxy package -->
|
<!-- TODO: Document user-extension framework in the property and proxy package -->
|
||||||
<para>
|
<para>
|
||||||
|
|
Loading…
Reference in New Issue