Announcement Announcement Module
Collapse
No announcement yet.
Hibernate Bidirectional Association Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate Bidirectional Association

    Hi, everybodies!

    I would like to develop classic category object with bidirectional association between categories and subcategories.

    With the following solution I get a one-way association. I have tried many alternative ways to get also son->parent association but nothig good.

    Code:
    public class Category extends BaseObject{
    	private Integer id_category;
    	private String name;
            private Category parent;
    	private Set subCategories= new HashSet();
    
    	public Categoria(){}
    
            ...Setters and Getters...
    
    	public void removeSubCategory(Category subCategory){
    		this.subCategories.remove(subCategory);
    	}
    
    	public void addSubCategory(Category subCategory) {
    		if (subCategory== null) throw new IllegalArgumentException("NULL!");
    		this.subCategories.add(subCategory);
    	}
    }
    with this MySql code..

    Code:
    CREATE TABLE category (
    	id_category  SMALLINT(5) UNSIGNED AUTO_INCREMENT,
    	nameVARCHAR(200) NOT NULL,
    	PRIMARY KEY(id_category)
    )
    ENGINE=INNODB;
    
    CREATE TABLE category_has_subcategory(
    	id_category SMALLINT(5) UNSIGNED,
    	id_subcategory SMALLINT(5) UNSIGNED,
    	PRIMARY KEY (id_category ,id_subcategory ),
    	FOREIGN KEY (id_category )
    		REFERENCES category(id_category )
    		ON UPDATE CASCADE
    		ON DELETE NO ACTION,
    	FOREIGN KEY (id_subcategory )
    		REFERENCES category(id_category )
    		ON UPDATE CASCADE
    		ON DELETE NO ACTION
    )
    ENGINE=INNODB;
    and with this is my Category.hbm.xml

    Code:
    <class name="model.Categoria" table="category" schema="test">
    
    		<id name="id_category" column="id_categoria" type="java.lang.Integer">
                <generator class="increment"/>
    		</id>
                    <property name="name" type="string" />
    		<set name="subCategories" table="category_has_sottocategory" inverse="true">
    			<key column="id_category" />
    			<many-to-many column="id_subcategory" class="model.Category" />
    		</set>
    	</class>
    Thanks,
    Magnusa.

  • #2
    Maybe..

    The problem is only how can I do a object self-associated?

    category has 1..n sub_category
    sub_category has 1..1 parent_category

    I need help

    Comment


    • #3
      Possible changes

      Hi Magnusa,

      Here are some thoughts:

      1) Personally, I would implement this recursive relationship on the DB-side with a single table:

      Code:
      CREATE TABLE category (
      	id_category SMALLINT(5) UNSIGNED AUTO_INCREMENT,
      	name VARCHAR(200) NOT NULL,
      	parent_id SMALLINT(5) UNSIGNED,
      	PRIMARY KEY(id_category),
      	FOREIGN KEY (parent_id)
      		REFERENCES category(id_category)
      		ON UPDATE CASCADE
      		ON DELETE NO ACTION
      )
      ENGINE=INNODB;
      2) I would then make the "parent" property of Category a persistent property (by adding it to the mapping file), and make the following changes to the "set" property:

      Code:
      <class name="model.Category" table="category" schema="test">
      
         <id name="id_category" column="id_category" type="java.lang.Integer">
            <generator class="increment"/>
         </id>
      
         <property name="name" type="string" />
      
         <many-to-one name="parent" column="parent_id" class="model.Category"/>
      
         <set name="subCategories" table="category" inverse="true">
            <key column="parent_id" />
            <one-to-many class="model.Category" />
         </set>
      
      </class>
      3) Lastly, I would be sure to maintain the bi-directional relationship on the Java-side as follows:

      Code:
      public void addSubCategory(Category subCategory) {
         if (subCategory== null) {
            throw new IllegalArgumentException("subcategory cannot be null");
         }
         this.subCategories.add(subCategory);
         // Also need to set the subCategory's parent
         subCategory.setParent(this);
      }
      I think your original problem might have to do with your using the inverse="true" attribute on the set; this tells Hibernate to only synchronize changes on the child end of the association. This is fine, but you didn't maintain the parent association on the child end, and even if you did, you did not make the association persistent.

      I hope this helps! If you have any other questions, re-post.

      -Arthur Loder

      Comment


      • #4
        Thanks

        Hi Arthur,
        at first time your solution does not work. I was a little sad because I had try this solution also by my self before and Tomcat shows me a lot of errors.

        Reading log I uderstand that the problem was in model.Category..
        Code:
        18/10/2006 11:16:36 ERROR [org.hibernate.LazyInitializationException] - <illegal access to loading collection>
        org.hibernate.LazyInitializationException: illegal access to loading collection
        	at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:341)
        	at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
        	at org.hibernate.collection.PersistentSet.hashCode(PersistentSet.java:355)
        In particular in hashCode there was something wrong. At the beginning of this work I saw in some example a solution for hashCode really cool and I used it. It was:

        Code:
        import java.io.Serializable;
        import org.apache.commons.lang.builder.EqualsBuilder;
        import org.apache.commons.lang.builder.HashCodeBuilder;
        import org.apache.commons.lang.builder.ToStringBuilder;
        import org.apache.commons.lang.builder.ToStringStyle;
        
        
        public class BaseObject implements Serializable {
            public String toString() {
                return ToStringBuilder.reflectionToString(this,
                        ToStringStyle.MULTI_LINE_STYLE);
            }
        
            public boolean equals(Object o) {
                return EqualsBuilder.reflectionEquals(this, o);
            }
        
            public int hashCode() {
                return HashCodeBuilder.reflectionHashCode(this);
            }
        }
        Then in my class I extended BaseObject, I don't know why but this was the cause of all my trouble. So now te simple solution (Ockham’s Razor rulez) is to put in my class:

        Code:
        	public boolean equals(Object other) {
        		if (this==other) return true;
        		if (id_category==null) return false;
        		if ( !(other instanceof Category) ) return false;
        		final Categoria that = (Category) other;
        		return this.id_category.equals( that.getId_category() );
        	}
        
        	public int hashCode() {
        		return id_category==null ? System.identityHashCode(this) :  id_category.hashCode();
        	}
        Thanks! Now I'm happy

        Comment

        Working...
        X