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


This is version 103. It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]


About this tutorial

This is a tutorial to show how to create and manage Hibernate relationships within AppFuse. This tutorial was written using AppFuse 1.8. All of the code for this tutorial is downloadable. To begin, I created a new application with an app/db name of blog/blog.
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.
NOTE: Copying the Java code in this tutorials doesn't work in Firefox. A workaround is to CTRL+Click (Command+Click on OS X) the code block and then copy it.

Table of Contents

  • [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
  • [3] [One-to-Many] A Weblog object can have many Entries
  • [4] [Many-to-One] A Category object can be assigned to many entries

Create Weblog.java, Entry.java and add XDoclet tags [#1]

The Weblog object is used to indentify a person's blog. This class has the following properties:

  • weblogId
  • blogTitle
  • dateCreated

The Entry object is used to contain a listing of a person's blog entries in their Weblog. This class contains the following properties:

  • entryId
  • text
  • timeCreated
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.

Below is a class diagram of these two objects, as well as the others you'll create in this tutorial.

ER-Diagram.jpg

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.

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...".

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:

  • categoryId
  • name
  • description

Configure Spring

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.


<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> 

[Many-to-Many] A Weblogs can have many Users, a User can have many Weblogs [#2]

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.

Add the following users property and accessor methods to Weblog.java.


    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;
    }

    public void addUser(User user) {
        getUsers().add(user);
    }

    public void setUsers(List users) {
        this.users = users;
    }

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.

User modifications

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.


    protected List weblogs;

    /**
     * @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;
    }

    public void setWeblogs(List weblogs) {
        this.weblogs = weblogs;
    }

Test it!

Create a unit test so you can verify that everything actually works. Create a WeblogDaoTest class in your test/dao/**/model 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:


package org.appfuse.dao;

import java.util.Date;

import org.appfuse.model.User;
import org.appfuse.model.Weblog;

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 = (Weblogdao.getObject(Weblog.class, w.getWeblogId());
        
        assertTrue(w.getUsers().isEmpty());
        
        // add a user
        User u = (Userdao.getObject(User.class, "mraible");
        w.addUser(u);
        
        dao.saveObject(w);
        
        w = (Weblogdao.getObject(Weblog.class, w.getWeblogId());
        
        assertTrue(w.getUsers().size() == 1);
        
        // remove the user
        w.setUsers(null);
        dao.saveObject(w);
        
        w = (Weblogdao.getObject(Weblog.class, w.getWeblogId());
        
        assertNull(w.getUsers());
    }
}

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 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. At a minimum, you'll need to fix BaseDAOTestCase and GenericDAOTest (onSetUp() -> onSetupBeforeTransaction()).

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:


        String qry = "select count(*) from weblog_user where username = 'mraible' " +
                     " and weblog_id = " + w.getWeblogId();
        assertEquals(0, jdbcTemplate.queryForInt(qry));

[One-to-Many] A Weblog object can have many Entries [#3]

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.


    private List entries;

    /**
     @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;
    }

    public void setEntries(List entries) {
        this.entries = entries;
    }

Modify the Entry class is so it contains a foreign key to its parent Weblog class.


    private Long weblogId;

    /**
     * @hibernate.property column="weblog_id"
     */
    public Long getWeblogId() {
        return weblogId;
    }

    public void setWeblogId(Long weblogId) {
        this.weblogId = weblogId;
    }

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.

[Many-to-One] A Category object can be assigned to many entries [#4]

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.


    private Long categoryId;
    private Category category;

    /**
     * @hibernate.many-to-one insert="false" update="false" cascade="none"
     *  column="category_id" outer-join="true"
     */
    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    /**
     * @hibernate.property column="category_id"
     */
    public Long getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Long categoryId) {
        this.categoryId = categoryId;
    }

After making these changes, your WeblogDaoTest should still pass. Verify it does by running ant setup-db test-dao -Dtestcase=Weblog.

Yeah baby, Yeah!

BUILD SUCCESSFUL
Total time: 24 seconds

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.

  <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>

Next Up: Part II: Create Weblog UI - Creating a UI to manage the relationships created in this tutorial.


Attachments:
Category.java Info on Category.java 1985 bytes
Entry.java Info on Entry.java 1947 bytes
Weblog.java Info on Weblog.java 2066 bytes
ER-Diagram.jpg Info on ER-Diagram.jpg 31720 bytes
LazyDAOTests.diff Info on LazyDAOTests.diff 4070 bytes


Go to top   More info...   Attach file...
This particular version was published on 06-Nov-2006 13:52:45 MST by MattRaible.