Consider for a moment a Blogging application where a blog owner has a custom list of Tags they can use. So I have a Person entity that has a list of Tag entities. Each person in the system has their own unique list of Tags, so a Tag also refers back to the Person that owns it.
Obviously, this is simple enough to do with hibernate (in fact, I already have that working). But now I need to do this with the different elements in separate projects, where one project doesn't know about the other one. And I'm running into troubles.
Here is an overview of the two projects I'm working with for this example (using Hibernate 3.3):
First project: Common
- Contains common classes used in many other projects
- Does not know about second project (Application project below) -- application.jar is not included
- Contains an abstract common.entity.Identity class
Code:
public abstract class Identity implements Serializable {
private Long id;
private String first;
private String middle;
private String last;
}
- Contains a concrete common.entity.Tag class which has an Identity property
Code:
public class Tag implements Serializable
{
private Long id;
private String name;
private Identity identity;
}
- Contains a mapping file Tag.hbm.xml
Code:
<hibernate-mapping package="common.entity" default-access="field">
<class name="Tag" table="tag">
<cache usage="read-write"/>
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="name" length="10"/>
<many-to-one name="identity" not-null="true" cascade="none" />
</class>
</hibernate-mapping>
Second project: Application- An application that includes the common.jar file (Common project above)
- Contains a concrete application.entity.Person class that extends common.entity.Identity
Code:
public class Person extends Identity
{
private String username;
private String password;
private List<Tag> tags = new ArrayList<Tag>();
}
- Contains a mapping file Person.hbm.xml
Code:
<hibernate-mapping package="application.entity" default-access="field">
<class name="Person" table="person">
<cache usage="read-write"/>
<id name="id" column="id">
<generator class="identity"/>
</id>
<property name="first" length="25"/>
<property name="middle" length="25"/>
<property name="last" length="50"/>
<property name="username" length="50"/>
<property name="password" length="50"/>
<list name="tags" table="personTag">
<key column="person" />
<list-index column="idx" />
<many-to-many class="common.entity.Tag" column="tag" />
</list>
</class>
</hibernate-mapping>
Problem codeWhen I attempt to populate some test data (using a Spring InitializingBean), I get an exception. Here is the code:
Code:
public class PersonDataGenerator implements InitializingBean {
private PersonDao personDao;
private TagDao tagDao;
private static final String[] USERNAMES = {
"sally",
"bill",
"mark"
};
private static final String[][] TAGS = {
{"Photography", "Sports", "Friends", "Science"},
{"Animals", "Movies", "Counseling"},
{"Computers","Cameras"}
};
public void afterPropertiesSet() throws Exception {
if (personDao.count() == 0) {
for (int i = 0; i < USERNAMES.length; i++) {
Person person= new Person();
person.setUsername(USERNAMES[i]);
person.setPassword(USERNAMES[i]);
for (int j = 0; j < TAGS[i].length; j++) {
Tag tag = new Tag();
tag.setName(TAGS[i][j]);
tag.setIdentity(person);
person.addTag(tag);
tagDao.save(tag); // EXCEPTION HERE
}
personDao.save(person);
}
}
}
Here is the exception:
Code:
ERROR - log - Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'personDataGenerator' defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: common.entity.Tag.identity:
org.hibernate.PropertyValueException: not-null property references a null or transient value: common.entity.Tag.identity
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:72)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:290)
My Questions- Is it possible to have the Tag.hbm.xml mapping file refer to the abstract class Identity?
- Do I need to move the Tag.hbm.xml file into the Application project and change it to refer to a Person instead of an Identity?
- Is there a proper way to accomplish what I'm trying to do using Inheritance mapping? The examples in the manual don't seem to match what I'm doing. Person is the only class that extends Identity in the application (other applications will extend Identity differently), so I only need one Person table, not multiple tables like can be used with inheritance mapping.
- Can anyone give me examples of how to do this correctly? I'm a bit confused on the proper solution to this.
If you need any further information, please let me know. Note that I've simplified the code and mappings above to make this post easier to understand.
Also, please don't ask me to move my Person object from Application to Common or Tag to Application, as that will not work for my use case. Unless no solution exists, I need to retain this project structure.
Thank you in advance!
Tauren