Hibernate [Electronic resources] : A Developers Notebook نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Hibernate [Electronic resources] : A Developers Notebook - نسخه متنی

James Elliott

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید










2.2 Generating Some Class



Our mapping contains information about both the database and the Java
class between which it maps. We can use it to help us create both. Let's
look at the class first.



2.2.1 How do I do that?



The Hibernate Extensions you installed in Chapter 1 included a tool that
can write Java source matching the specifications in a mapping document,
and an Ant task that makes it easy to invoke from within an Ant
build file. Edit build.xml to add the portions shown in bold in
Example 2-2.



Example 2-2. The Ant build file updated for code generation



1 <project name="Harnessing Hibernate: The Developer's Notebook"
2 default="db" basedir=".">
3 <!-- Set up properties containing important project directories -->
4 <property name="source.root" value="src"/>
5 <property name="class.root" value="classes"/>
6 <property name="lib.dir" value="lib"/>
7 <property name="data.dir" value="data"/>
8
9 <!-- Set up the class path for compilation and execution -->
10 <path id=">
11 <!-- Include our own classes, of course -->
12 <pathelement location="${class.root}" />
13 <!-- Include jars in the project library directory -->
14 <fileset dir="${lib.dir}">
15 <include name="*.jar"/>
16 </fileset>
17 </path>
18
19 <target name="db" description="Runs HSQLDB database management UI
20 against the database file--use when application is not running">
21 <java classname="org.hsqldb.util.DatabaseManager"
22 fork="yes">
23 <classpath refid="/>
24 <arg value="-driver"/>
25 <arg value="org.hsqldb.jdbcDriver"/>
26 <arg value="-url"/>
27 <arg value="jdbc:hsqldb:${data.dir}/music"/>
28 <arg value="-user"/>
29 <arg value="sa"/>
30 </java>
31 </target>
32
33 <!-- Teach Ant how to use Hibernate's code generation tool -->
34 <taskdef name="hbm2java"
35 classname="net.sf.hibernate.tool.hbm2java.Hbm2JavaTask"
36 classpathref="/>
37
38 <!-- Generate the java code for all mapping files in our source tree -->
39 <target name="codegen"
40 description="Generate Java source from the O/R mapping files">
41 <hbm2java output="${source.root}">
42 <fileset dir="${source.root}">
43 <include name="**/*.hbm.xml"/>
44 </fileset>
45 </hbm2java>
46 </target>
47
48 </project>


We added a taskdef (task definition) and a new target to the build file.
The task definition at line 33 teaches Ant a new trick: it tells Ant how to
use the hbm2java tool that is part of the Hibernate Extensions, with the
help of a class provided for this purpose. Note that it also specifies the
class path to be used when invoking this tool, using the definition found earlier in the file.


The codegen target at line 38 uses the new hbm2java task to run Hibernate's
code generator on any mapping documents found in the src tree,
writing the corresponding Java source. The pattern '**/*.hbm.xml'
means 'any file ending in .hbm.xml, within the specified directory, or any
subdirectory, however deeply nested.'


Let's try it! From within your top-level project directory (the folder containing
build.xml), type the following command:



ant codegen


You should see output like this:


     Buildfile: build.xml


codegen:
[hbm2java] Processing 1 files.
[hbm2java] Building hibernate objects
[hbm2java] log4j:WARN No appenders could be found for logger (net.sf.
hibernate.util.DTDEntityResolver).
[hbm2java] log4j:WARN Please initialize the log4j system properly.


The warnings are griping about the fact that we haven't taken the trouble
to set up the logging environment that Hibernate expects. We'll see
how to do that in the next example. For now, if you look in the directory
src/com/oreilly/hh, you'll see that a new file named Track.java has
appeared, with the content shown in Example 2-3.



Example 2-3. Code generated from the Track mapping document



1 package com.oreilly.hh;
2
3 import java.io.Serializable;
4 import java.util.Date;
5 import org.apache.commons.lang.builder.EqualsBuilder;
6 import org.apache.commons.lang.builder.HashCodeBuilder;
7 import org.apache.commons.lang.builder.ToStringBuilder;
8
9 /**
10 * Represents a single playable track in the music database.
11 * @author Jim Elliott (with help from Hibernate)
12 *
13 */
14 public class Track implements Serializable {
15
16 /** identifier field */
17 private Integer id;
18
19 /** persistent field */
20 private String title;
21
22 /** persistent field */
23 private String filePath;
24
25 /** nullable persistent field */
26 private Date playTime;
27
28 /** nullable persistent field */
29 private Date added;
30
31 /** nullable persistent field */
32 private short volume;
33
34 /** full constructor */
35 public Track(String title, String filePath, Date playTime,
Date added, short volume) {
36 this.title = title;
37 this.filePath = filePath;
38 this.playTime = playTime;
39 this.added = added;
40 this.volume = volume;
41 }
42
43 /** default constructor */
44 public Track() {
45 }
46
47 /** minimal constructor */
48 public Track(String title, String filePath) {
49 this.title = title;
50 this.filePath = filePath;
51 }
52
53 public Integer getId() {
54 return this.id;
55 }
56
57 protected void setId(Integer id) {
58 this.id = id;
59 }
60
61 public String getTitle() {
62 return this.title;
63 }
64
65 public void setTitle(String title) {
66 this.title = title;
67 }
68
69 public String getFilePath() {
70 return this.filePath;
71 }
72
73 public void setFilePath(String filePath) {
74 this.filePath = filePath;
75 }
76
77 /**
78 * Playing time
79 */
80 public Date getPlayTime() {
81 return this.playTime;
82 }
83
84 public void setPlayTime(Date playTime) {
85 this.playTime = playTime;
86 }
87
88 /**
89 * When the track was created
90 */
91 public Date getAdded() {
92 return this.added;
93 }
94
95 public void setAdded(Date added) {
96 this.added = added;
97 }
98
99 /**
100 * How loud to play the track
101 */
102 public short getVolume() {
103 return this.volume;
104 }
105
106 public void setVolume(short volume) {
107 this.volume = volume;
108 }
109
110 public String toString() {
111 return new ToStringBuilder(this)
112 .append("id", getId())
113 .toString();
114 }
115
116 public boolean equals(Object other) {
117 if ( !(other instanceof Track) ) return false;
118 Track castOther = (Track) other;
119 return new EqualsBuilder()
120 .append(this.getId(), castOther.getId())
121 .isEquals();
122 }
123
124 public int hashCode() {
125 return new HashCodeBuilder()
126 .append(getId())
127 .toHashCode();
128 }
129
130 }



2.2.2 What just happened?



Ant found all files in our source tree ending in .hbm.xml (just one, so far)
and fed it to the Hibernate code generator, which analyzed it, and wrote a
Java class meeting the specifications we provided for the Track mapping.



NOTE


That can save a
lot of time and
fairly repetitive
activity. I could
get used to it.


You may find it worthwhile to compare the generated Java source with
the mapping specification from which it arose (Example 2-1). The source
starts out with the proper package declaration, which is easy for
hbm2java to figure out from the fully qualified class name required in the
mapping file. There are a couple of imports to make the source more
readable. The three potentially unfamiliar entries (lines 5-7) are utilities
from the Jakarta Commons project that help in the creation of correctly
implemented and useful toString(), equals(), and hashCode() methods.


The class-level JavaDoc at line 10 should look familiar, since it comes
right from the 'class-description' meta tag in our mapping document. The
field declarations are derived from the id (line 17) and property (lines
20-32
) tags defined in the mapping. The Java types used are derived
from the property types in the mapping document. We'll delve into the
full set of value types supported by Hibernate later on. For now, the relationship
between the types in the mapping document and the Java types
used in the generated code should be fairly clear.


One curious detail is that an Integer wrapper has been used for id, while
volume is declared as a simple, unwrapped short. Why the difference? It
relates to the fact that the ID/key property has many important roles to
play in the O/R mapping process (which is why it gets a special XML tag in
the mapping document, rather than being just another property). Although
we left it out in our specification, one of the choices you need to make
when setting up an ID is to pick a special value to indicate that a particular
instance has not yet been saved into the database. Leaving out this
unsaved-value attribute, as we did, tells Hibernate to use its default interpretation,
which is that unsaved values are indicated by an ID of null.
Since native int values can't be null, they must be wrapped in a
java.lang.Integer, and Hibernate took care of this for us.


When it comes to the volume property, Hibernate has no special need or
use for it, so it trusts us to know what we're doing. If we want to be able
to store null values for volume, perhaps to indicate 'no change,' we
need to explicitly use java.lang.Short rather than short in our mapping
document. (Had we not been sneakily pointing out this difference,
our example would be better off explicitly using java.lang.Integer in
our ID mapping too, just for clarity.)



NOTE


I know, I'm a
perfectionist. I
only bother to pick
nits because I
think Hibernate is
so useful!


Another thing you might notice about these field declarations is that their
JavaDoc is quite genericyou may be wondering what happened to the
'field-description' meta tags we put in the mapping document for
playTime, added and volume. It turns out they appear only later, in the
JavaDoc for the getter methods. They are not used in the setters, the
actual field declarations, nor as @param entries for the constructor. As an
avid user of a code-completing Java editor, I count on pop-up JavaDoc as
I fill in arguments to method calls, so I'm a little disappointed by this limitation.
Of course, since this is an open source project, any of us can get
involved and propose or undertake this simple fix. Indeed, you may find
this already remedied by the time you read this book. Once robust field
and parameter documentation is in place, I'd definitely advocate always
providing a brief but accurate field-description entry for your properties.


After the field declarations come a trio of constructors. The first (line 35)
establishes values for all properties, the second (line 44) allows instantiation
without any arguments (this is required if you want the class to be
usable as a bean, such as on a Java Server Page, a very common use for
data classes like this), and the last (line 48) fills in just the values we've
indicated must not be null. Notice that none of the constructors set the
value of id; this is the responsibility of Hibernate when we get the object
out of the database, or insert it for the first time.


Consistent with that, the setId() method on line 57 is protected, as
requested in our id mapping. The rest of the getters and setters are not
surprising; this is all pretty much boilerplate code (which we've all written
too many times), which is why it's so nice to be able to have the
Hibernate extensions generate it for us.




If you want to use Hibernate's generated code as a starting point
and then add some business logic or other features to the generated
class, be aware that all your changes will be silently discarded
the next time you run the code generator. In such a project you will
want to be sure the hand-tweaked classes are not regenerated by
any Ant build target.



Even though we're having Hibernate generate our data classes in this
example, it's important to point out that the getters and setters it creates
are more than a nice touch. You need to put these in your persistent
classes for any properties you want to persist, since Hibernate's fundamental
persistence architecture is based on reflective access to Java-
Beans™-style properties. They don't need to be public if you don't want
them to; Hibernate has ways of getting at even properties declared
protected or private, but they do need accessor methods. Think of it as
enforcing good object design; the Hibernate team wants to keep the
implementation details of actual instance variables cleanly separated
from the persistence mechanism.



/ 65