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 58. 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 for AppFuse 1.8 and the AppGen pieces may not work with previous versions.
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.

Table of Contents

  • [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
  • [3] [One-to-Many] A Weblog object can have many Entries
  • [4] [Many-to-Many] Shared Weblogs are possible for multiple Users
  • [5] Lazy-Loading Issues
  • [6] Managing relationships and indexed properties in the UI

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

In this example, the Weblog object is used to indentify a person's blog. This class has the following properties:

  • weblogId
  • username
  • 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.

The first thing you need to do in this tutorial is these two object to persist. Create both 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.

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.
Tip: The equals(), hashCode() and toString() methods can be generated by your IDE. If you're using Eclipse, get the Commonclipse plugin. For IDEA, the first two are built into the "Generate..." dialog, and toString() is a plugin you can download.

[Many-to-One] Create a new Category object and modify Entry.java to use it [#2]

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.

The many-to-one relationship between entry and category can be established using XDoclet tags. Hibernate relationships can typically be established on either side of the relationship or as a bi-directional one as well. For our purposes,this relationship will be maintained by an extra property and collection held by Entry. The list of possible categories for a weblog entry will eventually be represented as a drop-down on the UI.


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

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

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 list. XDoclet tags are used to establish this relationship using a bag as the Hibernate collection type.


    private List entries;

    /**
     @return Returns the entries.
     
     * @hibernate.bag name="entries" lazy="true" inverse="true" cascade="delete"
     * @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;
    }

The Entry class is modified slightly to provide a placeholder for the relationships between Weblog and Entry.


    private Long weblogId;

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

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

[Many-to-Many] [#4]

The Weblog system that we are developing allows one additional bit of functionality. A particular 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 Set users = new HashSet();
    
    /**
     * @hibernate.set table="weblog_user" cascade="none" lazy="false"
     * @hibernate.collection-key column="weblog_id"
     * @hibernate.collection-many-to-many class="org.appfuse.model.User" column="username"
     */
    public Set getUsers() {
        return users;
    }

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

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

User modifications

In addition to allow navigation from a Weblog object to their list of users, you need to add a Set of Weblogs to the User object - allowing that path of navigation as well. Modify User.java to add a weblogs Set and accessor methods.


    protected Set weblogs;
  
    public Set getWeblogs() {
        return weblogs;
    }

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

DAO Stuff

In order to test that all of this works, you need to create a WeblogDaoTest.java class. Create this class in test/dao/**/dao and fill it with the following code.

WeblogDaoTest


package org.appfuse.dao;

import java.util.Date;

import org.appfuse.Constants;
import org.appfuse.model.Address;
import org.appfuse.model.Weblog;
import org.appfuse.model.User;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;

public class WeblogDaoTest extends BaseDaoTestCase {
    
    private WeblogDAO wldao = null;
    private Weblog weblog = null;
    private UserDAO udao = null;
    private User user = null;
    
    protected void setUp() throws Exception {
        super.setUp();
        wldao = (WeblogDAOctx.getBean("weblogDAO");
        udao = (UserDAOctx.getBean("userDAO");
    }
    
    protected void tearDown() throws Exception {
        super.tearDown();
        wldao = null;
    }
    
    public void testGetWeblog() throws Exception {
        weblog = wldao.getWeblog(new Long(1));

        assertNotNull(weblog);
        assertEquals(2, weblog.getUsers().size());
    }
    
    public void testUpdateWeblog() throws Exception {
        weblog = wldao.getWeblog(new Long(2));
        weblog.setBlogTitle("Testing Update Weblog");
        wldao.saveWeblog(weblog);
        assertEquals("Testing Update Weblog", weblog.getBlogTitle());
    }
    
    public void testAddWeblogUser() throws Exception {
        weblog = wldao.getWeblog(new Long(2));

        assertEquals(1, weblog.getUsers().size());
        
        user = udao.getUser("tomcat");
        weblog.addUser(user);
        wldao.saveWeblog(weblog);
        
        assertEquals(2, weblog.getUsers().size());
        
        // add the same user twice - should result in no additional user
        weblog.addUser(user);
        wldao.saveWeblog(weblog);

        assertEquals("more than 2 of the same users"2, weblog.getUsers().size());
        
        weblog.getUsers().remove(user);
        wldao.saveWeblog(weblog);

        assertEquals(1, weblog.getUsers().size());
    }
    
    public void testAddAndRemoveWeblog() throws Exception {
        weblog = new Weblog();
        weblog.setBlogTitle("A new test for adding and then deleting a weblog");
        weblog.setDateCreated(new Date(2005-04-17));
        weblog.setUsername("Cartman");
        
        wldao.saveWeblog(weblog);
        
        assertNotNull(weblog.getBlogTitle());
        assertEquals("Cartman", weblog.getUsername());
        
        wldao.removeWeblog(weblog.getWeblogId());
        
        try {
            weblog = wldao.getWeblog(weblog.getWeblogId());
            fail("saveWeblog didn't throw DataAccessException");
        catch (DataAccessException d) {
            assertNotNull(d);
        }
    }
}

This test relies on some sample data, so add the following XML to metadata/sql/sample-data.xml.

<table name='weblog'>
    <column>weblog_id</column>
    <column>blog_title</column>
    <column>date_created</column>
    <column>username</column>
    <row>
        <value>1</value>
        <value><![CDATA[Sponge Bob is Cool]]></value>
        <value>2004-03-31</value>
        <value>tomcat</value>
    </row>
    <row>
        <value>2</value>
        <value><![CDATA[Java Development = Fun]]></value>
        <value>2005-01-05</value>
        <value>mraible</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>

Before this test will compile, you need to create the WeblogDAO interface and implementation. Create a WeblogDAO.java interface in src/dao/**/dao:


package org.appfuse.dao;

import java.util.List;
import org.appfuse.model.Weblog;

public interface WeblogDAO extends Dao{
    public Weblog getWeblog(Long weblogId);
    public List getWeblogs(Weblog weblog);
    public void saveWeblog(Weblog weblog);
    public void removeWeblog(Long weblogId);
}

Then create the Hibernate implementation of this interface in the src/dao/**/dao/hibernate directory.


package org.appfuse.dao.hibernate;

import java.util.List;

import org.appfuse.dao.WeblogDAO;
import org.appfuse.model.Weblog;
import org.springframework.orm.ObjectRetrievalFailureException;

public class WeblogDAOHibernate extends BaseDaoHibernate implements WeblogDAO {
    
    public Weblog getWeblog(Long weblogId) {
        Weblog weblog = (WebloggetHibernateTemplate().get(Weblog.class, weblogId);

        if (weblog == null) {
            log.warn("uh oh, weblog '" + weblogId + "' not found...");
            throw new ObjectRetrievalFailureException(Weblog.class, weblogId);
        }

        return weblog;
    }

    public List getWeblogs(Weblog weblog) {
        return getHibernateTemplate().find("from Weblog wl order by upper(wl.blogTitle)");
    }

    public void saveWeblog(final Weblog weblog) {
        getHibernateTemplate().saveOrUpdate(weblog);
    }

    public void removeWeblog(Long weblogId) {
        getHibernateTemplate().delete(getWeblog(weblogId));
    }
}

Configure Spring for WeblogDAO

Modifications need to be made in applicationContext-hibernate.xml for the 3 new entities you created in this tutorial.


<property name="mappingResources"
    <list> 
        <value>org/appfuse/model/Role.hbm.xml</value>
        <value>org/appfuse/model/User.hbm.xml</value>
        <value>org/appfuse/model/UserCookie.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> 

Also, you need to add the "weblogDAO" bean definition.


<!-- WeblogDAO: Hibernate implementation --> 
<bean id="weblogDAO" class="org.appfuse.dao.hibernate.WeblogDAOHibernate"
    <property name="sessionFactory"><ref local="sessionFactory"/></property> 
</bean> 

After making these changes, you should be able to run ant test-dao -Dtestcase=WeblogDAO. All of the tests should pass successfully.

Lazy-Loading Issues [#5]

Managing relationships and indexed properties in the UI [#6]


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:44 MST by RanceShields.