I get a "found shared references to a collection" exception and I can't see that
any of the usual explanations (assigning members of an entity's collection to
another entity's collection) fit to my situation. Now I succeeded in reducing
my code to a very simple example that reproduces this exception. I think that it
is very similar to the problem Antoine Oberlaender describes
(
http://jira.jboss.com/jira/browse/EJBTHREE-348) even if I don't use an
application server to execute the code. At least I have a circular
reference in my model like Antoine.
I'd be happy if anyone could help me with this. So this is the example:
Code:
rel
Group n --- 1 User
m ^
| | inh
| rel |
--------- n Member
rel stands for relation (association)
inh stands for inheritance
The exception is thrown after Member objects have successfully been created and
then have been read from the database. After the last Member object was read the
transaction is committed but this fails.
Here is the code:
Group
Code:
import java.util.Set;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Table(name = "Group")
@Inheritance(strategy = InheritanceType.JOINED)
public class Group
implements Serializable
{
@Id(generate = GeneratorType.AUTO)
private long id;
private String name;
@ManyToMany
@JoinTable(
table = @Table(name = "Group___Member"),
joinColumns = { @JoinColumn(name = "idGroup")},
inverseJoinColumns = { @JoinColumn(name = "idMember")})
private Set<Member> members;
@ManyToOne
@JoinColumn(name = "idRelatedUserUser")
private User user;
public Group()
{
}
@Id(generate = GeneratorType.AUTO)
public long getId()
{
return this.id;
}
protected void setId(long id)
{
this.id = id;
}
@ManyToMany
@JoinTable(
table = @Table(name = "Group___Member"),
joinColumns = { @JoinColumn(name = "idGroup")},
inverseJoinColumns = { @JoinColumn(name = "idMember")})
public Set<Member> getMembers()
{
return this.members;
}
public void setMembers(Set<Member> members)
{
this.members = members;
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
@ManyToOne
@JoinColumn(name = "idRelatedUserUser")
public User getUser()
{
return this.user;
}
public void setUser(User user)
{
this.user = user;
}
public boolean equalsIgnorePrimaryKey(Group other)
{
if (!(this.getName().equals(other.getName())))
{
return false;
}
return true;
}
public boolean equals(Group other)
{
if (other == null)
{
return false;
}
if (!(this.getId() == other.getId()))
{
return false;
}
return this.equalsIgnorePrimaryKey(other);
}
@Override
public String toString()
{
StringBuffer result = new StringBuffer();
result.append("id: [" + this.getId() + "]").append(
"\nname: [" + this.getName().toString() + "]");
return result.toString();
}
}
User
Code:
import java.util.HashSet;
import java.util.Set;
import javax.persistence.OneToMany;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Table(name = "Sc000_User")
@Inheritance(strategy = InheritanceType.JOINED)
public class User
implements Serializable
{
@Id(generate = GeneratorType.AUTO)
private long id;
@OneToMany(mappedBy = "user")
private Set<Group> groups;
public User()
{
}
@Id(generate = GeneratorType.AUTO)
public long getId()
{
return this.id;
}
protected void setId(long id)
{
this.id = id;
}
@OneToMany(mappedBy = "user")
public Set<Group> getGroups()
{
if (this.groups == null)
{
this.groups = new HashSet<Group>();
}
return this.groups;
}
public void setGroups(Set<Group> groups)
{
this.groups = groups;
}
public boolean equalsIgnorePrimaryKey(User other)
{
return true;
}
public boolean equals(User other)
{
if (other == null)
{
return false;
}
if (!(this.getId() == other.getId()))
{
return false;
}
return this.equalsIgnorePrimaryKey(other);
}
@Override
public String toString()
{
StringBuffer result = new StringBuffer();
result.append("id: [" + this.getId() + "]");
return result.toString();
}
}
Member
Code:
import java.util.Set;
import javax.persistence.ManyToMany;
import javax.persistence.PrimaryKeyJoinColumn;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
@Entity
@Table(name = "Member")
@PrimaryKeyJoinColumn(name = "idSuperUser")
public class Member extends User
implements Serializable
{
private String name;
@ManyToMany(mappedBy = "members")
private Set<Group> groups;
public Member()
{
}
@ManyToMany(mappedBy = "members")
public Set<Group> getGroups()
{
return this.groups;
}
public void setGroups(Set<Group> groups)
{
this.groups = groups;
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
this.name = name;
}
public boolean equalsIgnorePrimaryKey(Member other)
{
if (!(this.getName().equals(other.getName())))
{
return false;
}
return true;
}
public boolean equals(Member other)
{
if (other == null)
{
return false;
}
if (!(super.getId() == other.getId()))
{
return false;
}
return this.equalsIgnorePrimaryKey(other);
}
@Override
public String toString()
{
StringBuffer result = new StringBuffer();
result.append("id: [" + super.getId() + "]").append(
"\nname: [" + this.getName().toString() + "]");
return result.toString();
}
}
JUnit-Test reproducing the exception
Code:
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
import org.apache.log4j.Logger;
import de.ruu.util.ClassUtil;
import de.ruu.util.RuntimeUtil;
import de.ruu.util.j2ee.ejb.EntityManagerUtil;
public class JUTLifecycleMember extends TestCase
{
private static final Class CLASS = RuntimeUtil.getThisClass();
private static final Logger LOGGER = ClassUtil.getClassLogger();
private EntityManager entityManager = null;
private EntityTransaction entityTransaction = null;
/**
* stores the number of <code>Member</code> and related objects
* that are to be created during this test
*/
private static final int OBJECT_COUNT = 5;
/**
* stores the Member objects that were created during this test
*/
private static final List<Member> CREATED = new ArrayList<Member>(
OBJECT_COUNT);
public JUTLifecycleMember()
{
super(CLASS.getName());
}
public JUTLifecycleMember(String name)
{
super(name);
}
@Override
protected void setUp() throws Exception
{
this.entityManager = EntityManagerUtil.getCurrentEntityManager();
this.entityTransaction = this.entityManager.getTransaction();
this.entityTransaction.begin();
}
@Override
protected void tearDown() throws Exception
{
this.entityTransaction.commit();
}
public static void main(String[] args)
{
TestRunner.run(JUTLifecycleMember.suite());
}
public static Test suite()
{
TestSuite suite = new TestSuite(CLASS.getName());
suite.addTest(new JUTLifecycleMember("testCreate"));
suite.addTest(new JUTLifecycleMember("testRead"));
suite.addTest(new JUTLifecycleMember("testUpdate"));
suite.addTest(new JUTLifecycleMember("testRead"));
suite.addTest(new JUTLifecycleMember("testDelete"));
suite.addTest(new JUTLifecycleMember("testReadDeleted"));
return suite;
}
public void testCreate() throws Exception
{
Member object = null;
for (int i = 0; i < OBJECT_COUNT; i++)
{
object = new Member();
object.setName("name." + i);
this.entityManager.persist(object);
CREATED.add(object);
}
}
public void testRead() throws Exception
{
for (Member object : CREATED)
{
object = this.entityManager.find(Member.class, object.getId());
LOGGER.info(
"success reading [" + Member.class.getName() + "] object:\n"
+ object.toString());
if (!(this.isInList(object, CREATED)))
{
throw new Exception(
"failure, read [" + Member.class.getName()
+ "] object that is not contained in the list of created objects");
}
}
}
public void testUpdate() throws Exception
{
for (Member object : CREATED)
{
object.setName(object.getName() + ".updated");
object = this.entityManager.merge(object);
LOGGER.info(
"success updating [" + Member.class.getName()
+ "] object:\n" + object.toString());
}
}
public void testDelete() throws Exception
{
for (Member object : CREATED)
{
this.entityManager.remove(object);
LOGGER.info(
"success deleting [" + Member.class.getName()
+ "] object:\n" + object.toString());
}
}
public void testReadDeleted() throws Exception
{
for (Member object : CREATED)
{
object = this.entityManager.find(Member.class, object.getId());
if (object != null)
{
throw new Exception(
"failure, read [" + Member.class.getName()
+ "] object that should be deleted already:\n"
+ object.toString());
}
}
}
private boolean isInList(
Member object, List<Member> objectList)
{
for (Member listElement : objectList)
{
if (object.equals(listElement))
{
return true;
}
}
return false;
}
}
DDL
Code:
CREATE TABLE gsd.Group
(
/* ordinary columns */
name varchar(50) default NULL,
/* primary key columns */
id bigint(20) default NULL NOT NULL auto_increment,
idRelatedUserUser bigint(20),
PRIMARY KEY
(
id
),
KEY keyRelatedUserUser
(
idRelatedUserUser
)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE gsd.Member
(
/* ordinary columns */
name varchar(50) default NULL,
/* primary key columns */
id bigint(20) NOT NULL auto_increment,
idSuperUser bigint(20) default NULL,
PRIMARY KEY
(
id
),
KEY keySuperUser
(
idSuperUser
)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE gsd.User
(
/* ordinary columns */
/* primary key columns */
id bigint(20) default NULL NOT NULL auto_increment,
PRIMARY KEY
(
id
)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE gsd.Group___Member
(
idGroup bigint(20) NOT NULL,
idMember bigint(20) NOT NULL,
KEY keyManySc000
(
idGroup
),
KEY keyManyMember
(
idMember
)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE gsd.Group
ADD CONSTRAINT fkr_Group2User_User FOREIGN KEY
(
idRelatedUserUser
) REFERENCES User
(
id
);
ALTER TABLE gsd.Member
ADD CONSTRAINT fks_User FOREIGN KEY
(
idSuperUser
) REFERENCES User
(
id
);
ALTER TABLE gsd.Group___Member ADD CONSTRAINT fkManyOwner_Group___Member FOREIGN KEY
(
idGroup
) REFERENCES Group
(
id
);
ALTER TABLE gsd.Group___Member ADD CONSTRAINT fkManyOwned_Group___Member FOREIGN KEY
(
idMember
) REFERENCES Member
(
id
);
Hibernate version: 3.1 rc2 Mapping documents: none, using annotationsCode between sessionFactory.openSession() and session.close(): see belowFull stack trace of any exception that occurs:
org.hibernate.HibernateException: Found shared references to a collection: Member.groups
at org.hibernate.engine.Collections.processReachableCollection(Collections.java:146)
at org.hibernate.event.def.FlushVisitor.processCollection(FlushVisitor.java:37)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:101)
at org.hibernate.event.def.AbstractVisitor.processValue(AbstractVisitor.java:61)
at org.hibernate.event.def.AbstractVisitor.processEntityPropertyValues(AbstractVisitor.java:55)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:115)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:195)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:905)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:345)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:36)
at JUTLifecycleMember.tearDown(JUTLifecycleMember.java:87)
at junit.framework.TestCase.runBare(TestCase.java:130)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Name and version of the database you are using: MySql 4.1.9The generated SQL (show_sql=true):Debug level Hibernate log excerpt:Code: