-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 
Author Message
 Post subject: Will Inheritance Mapping solve my problem?
PostPosted: Mon Mar 02, 2009 6:00 am 
Beginner
Beginner

Joined: Fri Mar 21, 2008 8:07 pm
Posts: 23
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 code
When 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


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 02, 2009 6:50 am 
Beginner
Beginner

Joined: Fri Mar 21, 2008 8:07 pm
Posts: 23
OMG... I need to head to bed.

It looks like my problem wasn't related to abstract classes or inheritance or anything like that. I just wasn't saving my objects in the right order.

I realized that person hadn't been saved yet when the tag was being saved, which explains the "not-null property references a null or transient value". So I moved personDao.save(person) to before the for() loop and it all works.

Code:
   for (int i = 0; i < USERNAMES.length; i++) {
      Person person= new Person();
      person.setUsername(USERNAMES[i]);
      person.setPassword(USERNAMES[i]);
      personDao.save(person);
      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); // NO MORE EXCEPTION
      }
   }


Thanks,
Tauren


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 02, 2009 7:07 am 
Beginner
Beginner

Joined: Wed Nov 19, 2008 8:25 am
Posts: 46
Location: Saint Petersburg, Russian Federation
Optimizations:

1. Specify 'cascade' attribute for the Person.tags association and remove unnecessary calls to tagDao.save(tag);
2. Introduce tag.setIdentity(this); to the person.addTag(tag) and remove that explicit call from the dao;


Top
 Profile  
 
 Post subject:
PostPosted: Mon Mar 02, 2009 8:36 pm 
Beginner
Beginner

Joined: Fri Mar 21, 2008 8:07 pm
Posts: 23
denis.zhdanov wrote:
Optimizations:

1. Specify 'cascade' attribute for the Person.tags association and remove unnecessary calls to tagDao.save(tag);
2. Introduce tag.setIdentity(this); to the person.addTag(tag) and remove that explicit call from the dao;


Thanks, these are both great optimizations and I've applied both to my code.

Tauren


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.