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 entities, 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
[6] Managing relationships and indexed properties in the UI
Create Weblog.java, Entry.java and add XDoclet tags [#1]
For our purposes, the Weblog entity is a list of available weblogs. A Weblog object has the following properties an id, a username (for the user who created the blog), 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.
[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 by the entity classes. 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.
[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. Hibernate tags are used to establish this relationship using a bag as the Hibernate collection type.
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.
/**
* @hibernate.class table="weblog"
*/ public class Weblog extends BaseObject {
private Long weblogId; private String username; private Date dateCreated; private String blogTitle; private Integer version; private List entries; private Set users = new HashSet();
/**
* @return Returns the blogTitle.
*
* @hibernate.property column="blog_title"
*/ public String getBlogTitle() { return blogTitle;
}
/**
* @param blogTitle
* The blogTitle to set.
*/ public void setBlogTitle(String blogTitle) { this.blogTitle = blogTitle;
}
/**
* @return Returns the dateCreated.
*
* @hibernate.property column="date_created"
*/ public Date getDateCreated() { return dateCreated;
}
/**
* @param dateCreated
* The dateCreated to set.
*/ public void setDateCreated(Date dateCreated) { this.dateCreated = dateCreated;
}
/**
* @return Returns the entries.
*
* @hibernate.bag name="entries" lazy="true" inverse="true" cascade="delete"
* @hibernate.collection-key column="entry_id"
* @hibernate.collection-one-to-many class="org.appfuse.model.Entry"
*/ public List getEntries() { return entries;
}
/**
* @param entries
* The entries to set.
*/ public void setEntries(List entries) { this.entries = entries;
}
/**
* @return Returns the id.
*
* @hibernate.id column="weblog_id" generator-class="native"
* unsaved-value="null"
*/ public Long getWeblogId() { return weblogId;
}
/**
* @param id
* The id to set.
*/ public void setWeblogId(Long weblogId) { this.weblogId = weblogId;
}
/**
* @return Returns the username.
*
* @hibernate.property column="username"
*/ public String getUsername() { return username;
}
/**
* @param username
* The username to set.
*/ public void setUsername(String username) { this.username = username;
}
/**
* @return Returns the users.
*
* @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;
}
/**
* Adds a user for the weblog
*
* @param rolename
*/ public void addUser(User user) {
getUsers().add(user);
}
/**
* @param users
* The users to set.
*/ public void setUsers(Set users) { this.users = users;
}
/**
* @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;
}
/**
* Returns the address.
*
* @return Address
*
* @hibernate.component
*/ public Address getAddress() { return address;
}
/**
* Returns the email. This is an optional field for specifying a different
* e-mail than the username.
*
* @return String
*
* @hibernate.property name="email" not-null="true" unique="true"
*/ public String getEmail() { return email;
}
/**
* Returns the user's roles.
*
* @return Set
*
* @hibernate.set table="user_role" cascade="save-update" lazy="false"
* @hibernate.collection-key column="username"
* @hibernate.collection-many-to-many class="org.appfuse.model.Role"
* column="role_name"
*/ public Set getRoles() { return roles;
}
/**
* Adds a role for the user
*
* @param rolename
*/ public void addRole(Role role) {
getRoles().add(role);
}
/**
* Sets the username.
*
* @param username
* The username to set
* @spring.validator type="required"
*/ public void setUsername(String username) { this.username = username;
}
/**
* Sets the password.
*
* @param password
* The password to set
*
* @spring.validator type="required"
* @spring.validator type="twofields" msgkey="errors.twofields"
* @spring.validator-args arg1resource="user.password"
* @spring.validator-args arg1resource="user.confirmPassword"
* @spring.validator-var name="secondProperty" value="confirmPassword"
*/ public void setPassword(String password) { this.password = password;
}
/**
* Sets the confirmedPassword.
*
* @param confirmPassword
* The confirmed password to set
* @spring.validator type="required"
*/ public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword;
}
/**
* Sets the firstName.
*
* @spring.validator type="required"
* @param firstName
* The firstName to set
*/ public void setFirstName(String firstName) { this.firstName = firstName;
}
/**
* Sets the lastName.
*
* @param lastName
* The lastName to set
*
* @spring.validator type="required"
*/ public void setLastName(String lastName) { this.lastName = lastName;
}
/**
* Sets the address.
*
* @param address
* The address to set
*
* @spring.validator
*/ public void setAddress(Address address) { this.address = address;
}
/**
* Sets the email.
*
* @param email
* The email to set
*
* @spring.validator type="required"
* @spring.validator type="email"
*/ public void setEmail(String email) { this.email = email;
}
/**
* Sets the phoneNumber.
*
* @param phoneNumber
* The phoneNumber to set
*
* @spring.validator type="mask" msgkey="errors.phone"
* @spring.validator-var name="mask" value="${phone}"
*/ public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber;
}
/**
* Sets the website.
*
* @param website
* The website to set
*/ public void setWebsite(String website) { this.website = website;
}
/**
* @param passwordHint
* The password hint to set
*
* @spring.validator type="required"
*/ public void setPasswordHint(String passwordHint) { this.passwordHint = passwordHint;
}
/**
* Sets the roles.
*
* @param roles
* The roles to set
*/ public void setRoles(Set roles) { this.roles = roles;
}
/**
* @return Returns the updated timestamp.
* @hibernate.version
*/ public Integer getVersion() { return version;
}
/**
* @param updated
* The updated version to set.
*/ public void setVersion(Integer version) { this.version = version;
}
/**
* @return Returns the weblogs.
*/ public Set getWeblogs() { return weblogs;
}
/**
* @param weblogs
* The weblogs to set.
*/ public void setWeblogs(Set weblogs) { this.weblogs = weblogs;
}
/**
* @return Returns the enabled.
* @hibernate.property column="enabled"
*/ public Boolean getEnabled() { return enabled;
}
/**
* @param enabled
* The enabled to set.
*/ public void setEnabled(Boolean enabled) { this.enabled = enabled;
}
/**
* Convert user roles to LabelValue objects for convenience.
*/ public List getRoleList() {
List userRoles = new ArrayList();
if (this.roles != null) { for (Iterator it = roles.iterator(); it.hasNext();) {
Role role = (Role) it.next();
// convert the user's roles to LabelValue Objects
userRoles.add(new LabelValue(role.getName(), role.getName()));
}
}
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");
/**
* This class interacts with Spring's HibernateTemplate to save/delete and
* retrieve Weblog objects.
*
* <p>
* <a href="WeblogDAOHibernate.java.html"><i>View Source</i></a>
* </p>
*
*/ public class WeblogDAOHibernate extends BaseDaoHibernate implements WeblogDAO {
if (weblog == null) {
log.warn("uh oh, weblog '" + weblogId + "' not found..."); throw new ObjectRetrievalFailureException(Weblog.class, weblogId);
}
return weblog;
}
/**
* @see org.appfuse.dao.WeblogDAO#getWeblogs(org.appfuse.model.Weblog)
*/ public List getWeblogs(Weblog weblog) { return getHibernateTemplate().find("from Weblog wl order by upper(wl.blogTitle)");
}
/**
* @see org.appfuse.dao.WeblogDAO#saveWeblog(org.appfuse.model.Weblog)
*/ public void saveWeblog(final Weblog weblog) { if (log.isDebugEnabled()) {
log.debug("weblog's title: " + weblog.getBlogTitle());
}
getHibernateTemplate().saveOrUpdate(weblog);
// necessary to throw a DataIntegrityViolation and catch it in WeblogManager
getHibernateTemplate().flush();
}