Hi All,
I have a problem with Hibernate. I spent several weeks trying to figure out what the problem is including browsing through this forum. I have a many-to-many unidirectional association between an entity A and B. So A holds several instances of B in a set, but B has no reference to A. In the relational schema a defined a mapping table BtoAMap for the mapping. Now within a single session I have multiple queries which return the same object of Type A. After each query I map an object of type B to the retrieved object of type A by inserting it into the Bs set of A. So in the end the single object of type A should contain the two Bs in its B set. This works fine. Unfortunately sometimes additional Bs get mapped to the A as well, without me triggering a corresponding action. In the Java layer the mapping from Bs to As has been done correctly but it does not get written into the database correctly. And whether the error happens or not depends on what data is already in the mapping table. This is a very strange error and I think I did something wrong, but I am having troubles figuring out what exactly. If anyone can point me to the right direction, I would be more than happy. I added some code below.
Many Thanks
Hibernate version:
3.1.2
Mapping document for A:
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="A" table="A" catalog="s">
<id name="identifier" type="java.lang.String">
<column name="identifier"/>
<generator class="assigned"/>
</id>
<set name="B" inverse="false" table="BtoAMap" lazy="false">
<key>
<column name="AIdentifier" not-null="true"/>
</key>
<many-to-many class="B">
<column name="BIdentifier" not-null="true"/>
</many-to-many>
</set>
</class>
</hibernate-mapping>
Java Code for ACode:
public class A extends PersistentObject {
private static final long serialVersionUID = -1589544330614217874L;
private Set<B> Bs = new HashSet<B>(0);
/**
* Constructs an <code>A</code> object.
*/
public A() {
}
/**
* Constructs an <code>A</code> object with the specified data.
*
* @param identifier the identifier
* @param Bs the Bs
*/
public A
(
String identifier,
Set<B> Bs
) {
setIdentifier(identifier);
this.Bs = Bs;
}
/**
* Gets the Bs.
*
* @return the Bs
*/
public Set<B> getBs() {
return this.Bs;
}
/**
* Sets the Bs.
*
* @param Bs the Bs
*/
public void setBs(Set<B> Bs) {
this.Bs = Bs;
}
}
Mapping document for B:Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="B" table="B" catalog="scviz">
<id name="identifier" type="java.lang.String">
<column name="identifier"/>
<generator class="assigned"/>
</id>
<property name="description" type="string">
<column name="description" not-null="true"/>
</property>
</class>
</hibernate-mapping>
Java Code for BCode:
public class B extends PersistentObject {
private static final long serialVersionUID = 5642250875210836705L;
private String description;
/**
* Constructs an <code>B</code> object.
*/
public B() {
}
/**
* Constructs an <code>B</code> object with the specified data.
*
* @param description the description
*/
public B(String description) {
this.description = description;
}
/**
* Constructs an <code>B</code> object with the specified data.
*
* @param identifier the identifier
* @param description the description
*/
public B(String identifier, String description) {
setIdentifier(identifier);
this.description = description;
}
/**
* Gets the description.
*
* @return the description
*/
public String getDescription() {
return this.description;
}
/**
* Sets the description.
*
* @param description the description
*/
public void setDescription(String description) {
this.description = description;
}
}
Java Code for PersistentObjectCode:
import java.util.UUID;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
/**
* Super class for all persistent objects, providing identification management and common functionality.
*/
public abstract class PersistentObject implements java.io.Serializable {
private String identifier = UUID.randomUUID().toString();
public String getIdentifier() {
return identifier;
}
protected void setIdentifier(String identifier) {
this.identifier = identifier;
}
public int hashCode() {
return new HashCodeBuilder()
.append(identifier)
.toHashCode();
}
public boolean equals(Object obj) {
if (obj instanceof PersistentObject == false) {
return false;
}
if (this == obj) {
return true;
}
if (identifier == null) {
return false;
}
PersistentObject other = (PersistentObject) obj;
return new EqualsBuilder()
.append(identifier, other.identifier)
.isEquals();
}
public String toString() {
return new ToStringBuilder(this)
.append("identifier", identifier)
.toString();
}
}
Code between sessionFactory.openSession() and session.close():
I used Spring's HibernateTemplate with AOP transaction demarcation. But abstractly the code is like this:
b1 = getHibernateTemplate().find(...);
b2 = getHibernateTemplate().find(...);
queryResultA = getHibernateTemplate().findByNamedParam(...);
iterator = queryResultA.iterator();
while (iterator.hasNext()) {
a = iterator.next();
a.getBs.add(b1);
}
queryResultB = getHibernateTemplate().findByNamedParam(...);
/** the same object as before can be returned by the different query **/
iterator = queryResultB.iterator();
while (iterator.hasNext()) {
a = iterator.next();
a.getBs.add(b2);
}
Full stack trace of any exception that occurs:
No exception occurs.
Name and version of the database you are using:
MySQL 5.0.38-Debian_1-log
The generated SQL (show_sql=true):
There are the SQL insert statements for the mapping table between A and B. Unfortunately someitmes additional ones get created.