Raible's Wiki

Raible Designs
Wiki Home
News
Recent Changes

AppFuse

Homepage
  - Korean
  - Chinese
  - Italian
  - Japanese

QuickStart Guide
  - Chinese
  - French
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish
  - Japanese

User Guide
  - Korean
  - Chinese

Tutorials
  - Chinese
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish

FAQ
  - Korean

Latest Downloads

Other Applications

Struts Resume
Security Example
Struts Menu

Set your name in
UserPreferences


Referenced by
Articles
Articles_cn
Articles_de
Articles_pt
Articles_zh
HibernateRelationshi...




JSPWiki v2.2.33

[RSS]


Hide Menu

HibernateRelationships


Difference between version 107 and version 26:

At line 2 changed 1 line.
This is a tutorial to show how to create and manage Hibernate relationships within [AppFuse]. This tutorial was written for AppFuse 1.8 and the AppGen pieces may not work with previous versions.
This is a tutorial to show how to create and manage Hibernate relationships within [AppFuse]. This tutorial was written using AppFuse 1.8.2. All of the code for this tutorial is downloadable at [http://static.appfuse.org/downloads/appfuse-hr.zip].
At line 4 changed 1 line.
;:''For further details about the specifics regarding creating entities, XDoclet tags for Hibernate, DAO development, etc., please refer to [Creating new DAOs and Objects in AppFuse|CreateDAO].''
;:''For further details about the specifics regarding creating objects, XDoclet tags for Hibernate, DAO development, etc., please refer to [Creating new DAOs and Objects in AppFuse|CreateDAO].''
At line 6 added 2 lines.
%%note __NOTE:__ Copying the Java code in this tutorials [doesn't work in Firefox|http://raibledesigns.com/page/rd?anchor=java2html_plugin_for_jspwiki_causes]. A workaround is to CTRL+Click (Command+Click on OS X) the code block and then copy it.%%
At line 7 changed 2 lines.
* [1] Create Weblog.java, Entry.java and add XDoclet tags
* [2] [[Many-to-One] Create a new Category object and modify Entry.java to use it
* [1] Create Weblog.java, Entry.java and Category.java domain objects
* [2] [[Many-to-Many] A Weblogs can have many Users, a User can have many Weblogs
At line 10 changed 3 lines.
* [4] [[Many-to-Many] ???
* [5] Lazy-Loading Issues
* [6] Managing relationships and indexed properties in the UI
* [4] [[Many-to-One] A Category object can be assigned to many entries
At line 15 removed 2 lines.
For our purposes, the Weblog entity is a list of available weblogs. A “Weblog” object has the following properties – an id, a username, a dateCreated, and a blogTitle. Every Entry object has an id, a text, and a timeCreated property.
The first thing to do is create some objects to persist. Let's create both a "Weblog" object and an “Entry” object (in the src/dao/**/model directory). Basic XDoclet tags for these entities are show as well.
At line 18 changed 1 line.
[{Java2HtmlPlugin
The ''Weblog'' object is used to indentify a person's blog. This class has the following properties:
At line 20 changed 7 lines.
/*
* Created on Apr 17, 2005
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package org.appfuse.model;
* weblogId
* blogTitle
* dateCreated
At line 28 changed 1 line.
import java.util.Date;
The ''Entry'' object is used to contain a listing of a person's blog entries in their Weblog. This class contains the following properties:
At line 30 changed 4 lines.
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
* entryId
* text
* timeCreated
At line 35 changed 4 lines.
/**
* @hibernate.class table="weblog"
*/
public class Weblog extends BaseObject {
%%note __NOTE:__ The primary keys are prefixed with their entity name to avoid confusion. I generally recommend using "id" for your entities, but wanted to make thing clearer in this tutorial.%%
At line 40 changed 1 line.
private Long weblogId;
Below is a class diagram of these two objects, as well as the others you'll create in this tutorial.
At line 42 changed 1 line.
private String username;
[ER-Diagram.jpg]
At line 44 changed 1 line.
private Date dateCreated;
The first thing you need to do in this tutorial is these two object to persist. Create a __Weblog.java__ class and an __Entry.java__ class (in the src/dao/**/model directory). The necessary XDoclet tags for these entities is included on the ''getter'' method's javadoc. You can download these files using the links below. Note that javadocs have been eliminated for brevity.
At line 46 changed 1 line.
private String blogTitle;
* [Download Weblog.java|Weblog.java]
* [Download Entry.java|Entry.java]
At line 48 changed 1 line.
private Integer version;
;:''Rather than fill up this tutorial with large blocks of Java code, the necessary files are attached and linked to. Small code snippets are used where appropriate. You should be able to easily download the files by right-clicking on them and selecting "Save Target As...".''
At line 50 changed 8 lines.
/**
* @return Returns the blogTitle.
*
* @hibernate.property column="blog_title"
*/
public String getBlogTitle() {
return blogTitle;
}
Create a __Category.java__ object to act as an entity for persisting category information about weblog entries. Each category can have many entries. This class contains the following properties:
At line 59 changed 7 lines.
/**
* @param blogTitle
* The blogTitle to set.
*/
public void setBlogTitle(String blogTitle) {
this.blogTitle = blogTitle;
}
* categoryId
* name
* description
At line 67 changed 8 lines.
/**
* @return Returns the dateCreated.
*
* @hibernate.property column="date_created"
*/
public Date getDateCreated() {
return dateCreated;
}
* [Download Category.java|Category.java]
At line 76 changed 7 lines.
/**
* @param dateCreated
* The dateCreated to set.
*/
public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}
!Configure Spring
At line 84 changed 9 lines.
/**
* @return Returns the id.
*
* @hibernate.id column="weblog_id" generator-class="native"
* unsaved-value="null"
*/
public Long getWeblogId() {
return weblogId;
}
Add the 3 new mapping files (that will be generated) to the "sessionFactory" bean's ''mappingResources'' property in __src/org/appfuse/dao/hibernate/applicationContext-hibernate.xml__.
At line 94 changed 7 lines.
/**
* @param id
* The id to set.
*/
public void setWeblogId(Long weblogId) {
this.weblogId = weblogId;
}
[{Java2HtmlPlugin
At line 102 changed 8 lines.
/**
* @return Returns the username.
*
* @hibernate.property column="username"
*/
public String getUsername() {
return username;
}
<property name="mappingResources">
<list>
<value>org/appfuse/model/Role.hbm.xml</value>
<value>org/appfuse/model/User.hbm.xml</value>
<value>org/appfuse/model/Weblog.hbm.xml</value>
<value>org/appfuse/model/Entry.hbm.xml</value>
<value>org/appfuse/model/Category.hbm.xml</value>
</list>
</property>
}]
At line 111 changed 7 lines.
/**
* @param username
* The username to set.
*/
public void setUsername(String username) {
this.username = username;
}
!![[Many-to-Many] A Weblogs can have many Users, a User can have many Weblogs [#2]
At line 119 changed 8 lines.
/**
* @return Returns the version.
*
* @hibernate.version
*/
public Integer getVersion() {
return version;
}
A Weblog can have many Users. Basically the idea is of a shared weblog that is a place where many users can express themselves about a particular topic of interest. For this bit of functionality the User object will be modified to have a many-to-many relationship with Weblog.
At line 128 changed 7 lines.
/**
* @param version
* The version to set.
*/
public void setVersion(Integer version) {
this.version = version;
}
Add the following ''users'' property and accessor methods to __Weblog.java__.
At line 136 changed 13 lines.
/**
* @see java.lang.Object#equals(Object)
*/
public boolean equals(Object object) {
if (!(object instanceof Weblog)) {
return false;
}
Weblog rhs = (Weblog) object;
return new EqualsBuilder().append(this.blogTitle, rhs.blogTitle)
.append(this.username, rhs.username).append(this.dateCreated,
rhs.dateCreated).append(this.weblogId, rhs.weblogId).append(
this.version, rhs.version).isEquals();
}
[{Java2HtmlPlugin
At line 150 changed 8 lines.
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return new HashCodeBuilder(2066507029, -390167195).append(
this.blogTitle).append(this.username).append(this.dateCreated)
.append(this.weblogId).append(this.version).toHashCode();
}
private List users = new ArrayList();
/**
* @hibernate.bag table="weblog_user" cascade="save-update" lazy="true"
* @hibernate.collection-key column="weblog_id"
* @hibernate.collection-many-to-many class="org.appfuse.model.User" column="username"
*/
public List getUsers() {
return users;
}
At line 159 changed 11 lines.
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", this.weblogId).append("version", this.version).append(
"username", this.username).append("blogTitle",
this.blogTitle).append("dateCreated", this.dateCreated)
.toString();
}
}
public void addUser(User user) {
getUsers().add(user);
}
At line 89 added 3 lines.
public void setUsers(List users) {
this.users = users;
}
At line 173 changed 1 line.
[{Java2HtmlPlugin
%%note __NOTE:__ The reason a java.util.List is used instead of a java.util.Set is because this is because [Sets aren't supported for indexed properties in Struts|http://www.mail-archive.com/[email protected]/msg26289.html].%%
At line 175 changed 1 line.
package org.appfuse.model;
!User modifications
At line 177 changed 1 line.
import java.util.Date;
To allow navigation from a User objec to a list of Weblogs, you need to add a List of Weblogs to the User object. Modify ''User.java'' to add a ''weblogs'' List and accessor methods.
At line 179 changed 4 lines.
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
[{Java2HtmlPlugin
At line 184 changed 4 lines.
/**
* @hibernate.class table="entry"
*/
public class Entry extends BaseObject {
protected List weblogs;
At line 189 changed 1 line.
private Long id;
/**
* @hibernate.bag table="weblog_user" cascade="save-update" lazy="true"
* @hibernate.collection-key column="username"
* @hibernate.collection-many-to-many class="org.appfuse.model.Weblog" column="weblog_id"
*/
public List getWeblogs() {
return weblogs;
}
At line 191 changed 1 line.
private String text;
public void setWeblogs(List weblogs) {
this.weblogs = weblogs;
}
}]
At line 193 changed 1 line.
private Date timeCreated;
!Test it!
At line 195 changed 1 line.
private Integer version;
Create a unit test so you can verify that everything actually works. Create a __WeblogDaoTest__ class in your test/dao/**/dao directory. This file should extend GenericDaoTest. If you're using anything less than AppFuse 1.9, you may have to modify GenericDAOTest's "dao" variable so its __protected__ instead of private. Copy the code below into this test:
At line 197 changed 4 lines.
/*
* Generate your getters and setters using your favorite IDE: In Eclipse:
* Right-click -> Source -> Generate Getters and Setters
*/
[{Java2HtmlPlugin
At line 202 changed 9 lines.
/**
* @return Returns the id.
*
* @hibernate.id column="entry_id" generator-class="native"
* unsaved-value="null"
*/
public Long getId() {
return id;
}
package org.appfuse.dao;
At line 212 changed 7 lines.
/**
* @param id
* The id to set.
*/
public void setId(Long id) {
this.id = id;
}
import java.util.Date;
At line 220 changed 8 lines.
/**
* @return Returns the text.
*
* @hibernate.property column="entry_text"
*/
public String getText() {
return text;
}
import org.appfuse.model.User;
import org.appfuse.model.Weblog;
At line 229 changed 73 lines.
/**
* @param text
* The text to set.
*/
public void setText(String text) {
this.text = text;
}
/**
* @return Returns the timeCreated.
*
* @hibernate.property column="time_created"
*/
public Date getTimeCreated() {
return timeCreated;
}
/**
* @param timeCreated
* The timeCreated to set.
*/
public void setTimeCreated(Date timeCreated) {
this.timeCreated = timeCreated;
}
/**
* @return Returns the version.
*
* @hibernate.version
*/
public Integer getVersion() {
return version;
}
/**
* @param version
* The version to set.
*/
public void setVersion(Integer version) {
this.version = version;
}
/**
* @see java.lang.Object#equals(Object)
*/
public boolean equals(Object object) {
if (!(object instanceof Entry)) {
return false;
}
Entry rhs = (Entry) object;
return new EqualsBuilder().append(this.text, rhs.text).append(
this.timeCreated, rhs.timeCreated).append(this.id, rhs.id)
.append(this.version, rhs.version).isEquals();
}
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return new HashCodeBuilder(-880342185, -1668369001).append(this.text)
.append(this.timeCreated).append(this.id).append(this.version)
.toHashCode();
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("text", this.text).append("id", this.id).append(
"version", this.version).append("timeCreated",
this.timeCreated).toString();
}
public class WeblogDaoTest extends GenericDAOTest {
public void testWeblogAndUsers() throws Exception {
Weblog w = new Weblog();
w.setBlogTitle("My New Weblog");
w.setDateCreated(new Date());
// add it to the database
dao.saveObject(w);
w = (Weblog) dao.getObject(Weblog.class, w.getWeblogId());
assertTrue(w.getUsers().isEmpty());
// add a user
User u = (User) dao.getObject(User.class, "mraible");
w.addUser(u);
dao.saveObject(w);
w = (Weblog) dao.getObject(Weblog.class, w.getWeblogId());
assertTrue(w.getUsers().size() == 1);
// remove the user
w.setUsers(null);
dao.saveObject(w);
w = (Weblog) dao.getObject(Weblog.class, w.getWeblogId());
assertNull(w.getUsers());
}
At line 166 added 1 line.
Make sure you run __ant setup-db__ before running __ant test-dao -Dtestcase=UserDAO__. When running the test, you'll probably get a ''LazyInitializationException''. To solve this, change BaseDaoTestCase to extend Spring's [AbstractTransactionalDataSourceSpringContextTests|http://www.springframework.org/docs/api/org/springframework/test/AbstractTransactionalDataSourceSpringContextTests.html] if it doesn't already. In additional, you'll need to make a few changes to the existing tests so all the tests pass. [Here is a patch|LazyDaoTests.diff]. At a minimum, you'll need to fix BaseDAOTestCase and GenericDAOTest (onSetUp() -> onSetupBeforeTransaction()).
At line 168 added 1 line.
Because the WeblogDaoTest is now extending AbstractTransactionalDataSourceSpringContextTests (ATDSSCT), there is a "jdbcTemplate" variable you can use to do additional querying. For instance, you could add the following at the end of your ''testWeblogAndUsers()'' method:
At line 170 added 1 line.
[{Java2HtmlPlugin
At line 308 changed 1 line.
!![[Many-to-One] Create a new Category object and modify Entry.java to use it [#2]
String qry = "select count(*) from weblog_user where username = 'mraible' " +
" and weblog_id = " + w.getWeblogId();
assertEquals(0, jdbcTemplate.queryForInt(qry));
}]
At line 310 changed 1 line.
A category object will act as an entity for persisting category information about weblog entries. A category object will consist of an id (primary key), category name, and a description. Each category can have many entries.
!![[One-to-Many] A Weblog object can have many Entries [#3]
At line 179 added 2 lines.
Now you'll modify the __Weblog__ object and __Entry__ object to represent the multiplicity of a weblog that can have many entries. This relationship is set on the Weblog class using a java.util.List. XDoclet tags are used to establish this relationship using a __bag__ as the Hibernate collection type. Add the following code to your __Weblog.java__ class.
At line 314 changed 1 line.
package org.appfuse.model;
private List entries;
At line 316 changed 4 lines.
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
/**
* @return Returns the entries.
*
* @hibernate.bag name="entries" lazy="false" cascade="all"
* @hibernate.collection-key column="weblog_id"
* @hibernate.collection-one-to-many class="org.appfuse.model.Entry"
*/
public List getEntries() {
return entries;
}
At line 321 changed 4 lines.
/**
* @hibernate.class table="category"
*/
public class Category extends BaseObject {
public void setEntries(List entries) {
this.entries = entries;
}
}]
At line 326 changed 1 line.
private Long id;
Modify the __Entry__ class is so it contains a foreign key to its parent __Weblog__ class.
At line 328 changed 1 line.
private String categoryName;
[{Java2HtmlPlugin
At line 330 changed 1 line.
private String categoryDescription;
private Long weblogId;
At line 332 changed 1 line.
private Integer version;
/**
* @hibernate.property column="weblog_id"
*/
public Long getWeblogId() {
return weblogId;
}
At line 334 changed 8 lines.
/**
* @return Returns the categoryDescription.
*
* @hibernate.property column="category_description"
*/
public String getCategoryDescription() {
return categoryDescription;
}
public void setWeblogId(Long weblogId) {
this.weblogId = weblogId;
}
}]
At line 343 changed 7 lines.
/**
* @param categoryDescription
* The categoryDescription to set.
*/
public void setCategoryDescription(String categoryDescription) {
this.categoryDescription = categoryDescription;
}
At this point, you could add an additional test method to WeblogDaoTest to test that this relationship works. Of course, you'll need to run __ant setup-db__ to make sure your database knows about the relationship.
At line 351 changed 8 lines.
/**
* @return Returns the categoryName.
*
* @hibernate.property column="category_name"
*/
public String getCategoryName() {
return categoryName;
}
!![[Many-to-One] A Category object can be assigned to many entries [#4]
At line 360 changed 7 lines.
/**
* @param categoryName
* The categoryName to set.
*/
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
The ''many-to-one'' relationship between __Category__ and __Entry__ can be established using XDoclet tags. Hibernate relationships can be established on either side of the relationship or bi-directionally. For this tutorial, this relationship is maintained by a __categoryId__ property and a __Category__ object in __Entry__. The list of possible categories for a weblog entry will eventually be represented as a drop-down on the UI. Add the following code to your __Entry.java__ class. Notice that the relationship to __Category__ has ''insert="false" update="false"''. This is because the object is read-only. This can be useful for displaying a category's name on the UI when you're viewing an Entry.
At line 368 changed 9 lines.
/**
* @return Returns the id.
*
* @hibernate.id column="category_id" generator-class="native"
* unsaved-value="null"
*/
public Long getId() {
return id;
}
[{Java2HtmlPlugin
At line 378 changed 7 lines.
/**
* @param id
* The id to set.
*/
public void setId(Long id) {
this.id = id;
}
private Long categoryId;
private Category category;
At line 386 changed 8 lines.
/**
* @return Returns the version.
*
* @hibernate.version
*/
public Integer getVersion() {
return version;
}
/**
* @hibernate.many-to-one insert="false" update="false" cascade="none"
* column="category_id" outer-join="true"
*/
public Category getCategory() {
return category;
}
At line 395 changed 7 lines.
/**
* @param version
* The version to set.
*/
public void setVersion(Integer version) {
this.version = version;
}
public void setCategory(Category category) {
this.category = category;
}
At line 403 changed 13 lines.
/**
* @see java.lang.Object#equals(Object)
*/
public boolean equals(Object object) {
if (!(object instanceof Category)) {
return false;
}
Category rhs = (Category) object;
return new EqualsBuilder().append(this.categoryDescription,
rhs.categoryDescription).append(this.categoryName,
rhs.categoryName).append(this.id, rhs.id).append(this.version,
rhs.version).isEquals();
}
/**
* @hibernate.property column="category_id"
*/
public Long getCategoryId() {
return categoryId;
}
At line 417 changed 21 lines.
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return new HashCodeBuilder(-837614407, -1874103513).append(
this.categoryDescription).append(this.categoryName).append(
this.id).append(this.version).toHashCode();
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("id", this.id)
.append("categoryName", this.categoryName).append("version",
this.version).append("categoryDescription",
this.categoryDescription).toString();
}
}
public void setCategoryId(Long categoryId) {
this.categoryId = categoryId;
}
At line 440 changed 1 line.
!![[One-to-Many] A Weblog object can have many Entries [#3]
After making these changes, your __WeblogDaoTest__ should still pass. Verify it does by running __ant setup-db test-dao -Dtestcase=Weblog__.
At line 442 changed 1 line.
!![[Many-to-Many] ??? [#4]
''__Yeah baby, Yeah!__''
%%(color: green)
BUILD SUCCESSFUL\\
Total time: 24 seconds
%%
At line 444 changed 1 line.
!!Lazy-Loading Issues [#5]
Before working on the UI, it's helpful to have some sample data so you don't have to do manual data entry. Add the following XML to the bottom of __metadata/sql/sample-data.xml__.
At line 446 changed 1 line.
!!Managing relationships and indexed properties in the UI [#6]
<div style="color: blue !important; margin-left: 50px">
{{{
<table name='weblog'>
<column>weblog_id</column>
<column>blog_title</column>
<column>date_created</column>
<row>
<value>1</value>
<value><![CDATA[Sponge Bob is Cool]]></value>
<value>2004-03-31</value>
</row>
<row>
<value>2</value>
<value><![CDATA[Java Development = Fun]]></value>
<value>2005-01-05</value>
</row>
</table>
<table name='weblog_user'>
<column>weblog_id</column>
<column>username</column>
<row>
<value>1</value>
<value>tomcat</value>
</row>
<row>
<value>1</value>
<value>mraible</value>
</row>
<row>
<value>2</value>
<value>mraible</value>
</row>
</table>
<table name='category'>
<column>category_id</column>
<column>name</column>
<column>description</column>
<row>
<value>1</value>
<value><![CDATA[Struts vs. Spring MVC]]></value>
<value><![CDATA[Comparing implementations of the MVC Design Pattern]]></value>
</row>
<row>
<value>2</value>
<value><![CDATA[Cycling Notes]]></value>
<value><![CDATA[All about cycling in the US.]]></value>
</row>
<row>
<value>3</value>
<value><![CDATA[Cyclocross]]></value>
<value><![CDATA[Bog Trotters Unite!]]></value>
</row>
</table>
<table name='entry'>
<column>entry_id</column>
<column>entry_text</column>
<column>time_created</column>
<column>weblog_id</column>
<column>category_id</column>
<row>
<value>1</value>
<value><![CDATA[Testing]]></value>
<value>2005-04-11 01:02:03.4</value>
<value>1</value>
<value>1</value>
</row>
<row>
<value>2</value>
<value><![CDATA[Test Value]]></value>
<value>2005-04-12 01:02:03.4</value>
<value>1</value>
<value>1</value>
</row>
<row>
<value>3</value>
<value><![CDATA[Test Value 3]]></value>
<value>2005-04-12 01:02:03.4</value>
<value>2</value>
<value>3</value>
</row>
</table>
}}}
</div>
At line 348 added 3 lines.
----
''Next Up:'' __Part II:__ [Create Weblog UI|HibernateRelationshipsUI] - Creating a UI to manage the relationships created in this tutorial.

Back to HibernateRelationships, or to the Page History.