Feed on
 Posts
 Comments
Java Beans dot Asia

Just a few simple tutorials …

Recently during development, I encountered a situation where I had to map two collections in the same entity, having collections and the entity it self of the same type.

I had persistent entity – Rule. Rule could have children rules: “action” rules and “else” rules. Children rules were also of type Rule.

Basically it was a one-to-many relationship mapping of an entity to itself twice:
Entity Rule could have two different collections of the same object type – Rule. The collections represented “action” and “else” rules.

The challenge that I faced was during XML generation of the persisted entity Rule:

I could successfully persist entity Rule with its children Rules to DB. But, when I was generating the XML, Hibernate was not able to differentiate between “action” and “else” rules and was adding every child Rule to each collection. This was causing the same entity to appear once in each collection:

<?xml version="1.0" encoding="UTF-8"?>
<Rule id="1">
	<ruleType>-1</ruleType>
	<actionChildren>
		<rule>1</rule>
		<rule>2</rule>
	</actionChildren>
	<elseChildren>
		<rule>1</rule>
		<rule>2</rule>
	</elseChildren>
</Rule>

I needed to “tell” somehow to Hibernate which child entity Rule should be added to which collection. I am not sure if my solution is the best in this case, but I created a discriminator property inside entity Rule – “ruleType”.

Respectively, in HBM file, in the mappings of one-to-many relationships, I specified by using SQL query, which rules should be added to the collection when XML is generated. The SQL query was inside where attribute of <list> element.

It done the trick for me – when I was adding a child rule to a parent entity, I was specifying child rule type. Therefore, Hibernate was generating XML correctly by adding “action” rules and “else” rules to their respective collections.

Here is entity Rule:

package org.example.rules;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Rule implements
			Serializable {

private static final long
	serialVersionUID = -76784L;

private int idx;
private int ruleType = -1;
private Rule parentRule = null;

private List<Rule> actionChildren =
		new ArrayList<Rule>();

private List<Rule> elseChildren =
		new ArrayList<Rule>();

/**
 * Default constructor
 */
public Rule() {

}

/**
 * @return the 'action' children
 */
public List<Rule> getActionChildren() {
	return actionChildren;
}

/**
 * @param children
 *            'action' children to set
 */
public void setActionChildren(List<Rule> actionChildren) {
	this.actionChildren = actionChildren;
}

/**
 * @return the 'else' children
 */
public List<Rule> getElseChildren() {
	return elseChildren;
}

/**
 * @param children
 *            'else' children to set
 */
public void setElseChildren(List<Rule> elseChildren) {
	this.elseChildren = elseChildren;
}

/**
 * Adds an 'action' child to the current rule
 *
 * @param actionChild
 *            The rule to be added as a child
 */

public void addActionChild(Rule actionChild) {
	if (!this.actionChildren.contains(actionChild)) {
		actionChild.setParentRule(this);
		this.actionChildren.add(actionChild);
	}
}

/**
 * Adds an 'else' child to the current rule
 *
 * @param elseChild
 *            The rule to be added as a child
 */

public void addElseChild(Rule elseChild) {
	if (!this.elseChildren.contains(elseChild)) {
		elseChild.setParentRule(this);
		this.elseChildren.add(elseChild);
	}
}

/**
 * @return the ruleType
 */
public int getRuleType() {
	return ruleType;
}

/**
 * @param ruleType
 *            the ruleType to set
 */
public void setRuleType(int ruleType) {
	this.ruleType = ruleType;
}

/**
 * @return collection index
 */
public int getIdx() {
	return idx;
}

/**
 * Sets collection index
 * @param collection index
 */
public int setIdx(int idx) {
	this.idx = idx;
}

/**
 * @return parent rule of the current rule
 */
public Rule getParentRule() {
	return parentRule;
}

/**
 * @param ruleParent
 *            the ruleParent to set
 */
public void setParentRule(Rule parentRule) {
	this.parentRule = parentRule;
}
}

Rule HBM file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="org.example.rules.Rule"
	table="org_example_rules_Rule" node="Rule">

<id name="id" column="id" node="@id"
			unsaved-value="0">
	<generator class="native" />
</id>

<!--
Used to help Hibernate to discriminate
between children rules
-->
<property name="ruleType" column="ruleType"
								node="ruleType" />
<!--
Attribute 'where' holds SQL query used to help Hibernate
to discriminate between children rules of type 'action'
and children rules of type 'else'.

Since two children rule lists in the current HBM are of
the same type (org.example.rules.Rule), Hibernate needs
a 'hint' how to discriminate which rules belong to
which list when XML is generated.

If ruleType equals to '1', it means that the child
rule is 'action' rule.

If ruleType equals to '2', it means that the child
rule is 'else' rule.
-->
<list name="actionChildren" embed-xml="true"
		where="ruleType=1" cascade="all">

	<key column="parentrule_id" />
	<list-index column="idx" base="0" />
	<one-to-many class="org.example.rules.Rule"
		node="rule" embed-xml="false" />
</list>

<!--
If ruleType equals to '2', it means that the child
rule is 'else' rule.
-->
<list name="elseChildren" embed-xml="true"
		where="ruleType=2" cascade="all">

	<key column="parentrule_id" />
	<list-index column="idx" base="0" />
	<one-to-many class="org.example.rules.Rule"
		node="rule" embed-xml="false" />

</list>

<!--
Read-only index column, represents position of
the element in the list
-->
<property name="idx" column="idx" node="idx"
		update="false" insert="false" type="int" />

<!--
Embed XML must be equals to 'false',
otherwise when XML is generated for
children rules, parent XML will be
generated recursively
-->
<many-to-one name="parentRule" node="parentRule"
	column="parentrule_id" class="org.example.rules.Rule"
	not-null="false" insert="false" update="false"
	embed-xml="false" />

</class>
</hibernate-mapping>

Generally, I would not advise to design persistent entities in this way, it can create problem later on. For example if there is a need to delete child entities. It can become a bit tricky, since entity has relationship to it self.

Suggestions? Flames?

GD Star Rating
loading...
Hibernate - How To Map Two Collections of The Same Type in The Same Entity, 10.0 out of 10 based on 1 rating

Related posts:

  1. Brainteaser: Hidden Iterators
    … While locking can prevent iterators from throwing ConcurrentMofdificationException, You have to remember to use locking everywhere a shared collection might be iterated....
  2. Brainteaser: Broken Comparator
    Question: The following program returns result “1″, which indicates that first Integer value is greater than the second, why? import java.util.*; public class...
  3. Java and Those Frameworks
    I came across an interesting article that discusses today’s application developers making extensive use of different frameworks in their applications. This is the...
  4. Hibernate Event Interceptor
    Its quite common when you create an application, there is a need to create an audit trail on the application level where all...
  5. Brainteaser: Broken Case of Inheritance
    Consider the following case of inheritance: public class ExtendingHashSet<E> extends HashSet<E> { private int counter = 0; public ExtendingHashSet() { } @Override public...