Scott & Raphael,
I know what is happening in my situation. In short, I am receiving a "org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags" error. Based on some research and then verification, I have found that this is a Hibernate issue that results from using a FetchType.EAGER on a @OneToMany entity attribute where the attribute is a List collection. To fix the issue, there are two things that can be done: 1) do not use FetchType.EAGER (use the default of LAZY) or 2) use a Set as the collection type for the @OneToMany instead of a List.
I will provide sample code below that will demonstrate the problem.
1. "TestDomain" - An Entity object that contains two collections. Both are @OneToMany that use an EAGER fetch strategy and are contained in List collections. (This is the problem: can't use EAGER or can't use List; use Set, for example.)
package foo.domain;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="test")
public class TestDomain {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@Column(name="name", length=255, nullable=false)
private String name;
@OneToMany(cascade={CascadeType.REMOVE}, fetch=FetchType.EAGER, mappedBy="test")
private List<TestCommentDomain> comments;
@OneToMany(cascade={CascadeType.REMOVE}, fetch=FetchType.EAGER, mappedBy="test")
private List<TestPersonDomain> persons;
public Long getId() {
return id;
}
public void ListId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void ListName(String name) {
this.name = name;
}
public List<TestCommentDomain> getComments() {
return comments;
}
public void ListComments(List<TestCommentDomain> comments) {
this.comments = comments;
}
public List<TestPersonDomain> getPersons() {
return persons;
}
public void ListPersons(List<TestPersonDomain> persons) {
this.persons = persons;
}
}
2. "TestCommentDomain" - An Entity object that is referenced by "TestDomain"
package foo.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="test_comment")
public class TestCommentDomain {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@ManyToOne(optional=false)
@JoinColumn(name="test_id", nullable=false)
private TestDomain test;
@Column(name="comment", nullable=false)
private String comment;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public TestDomain getTest() {
return test;
}
public void setTest(TestDomain test) {
this.test = test;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
}
3. "TestPersonDomain" - A second Entity that is referenced by "TestDomain"
package foo.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="test_person")
public class TestPersonDomain {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
private Long id;
@ManyToOne(optional=false)
@JoinColumn(name="test_id", nullable=false)
private TestDomain test;
@Column(name="name", nullable=false)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public TestDomain getTest() {
return test;
}
public void setTest(TestDomain test) {
this.test = test;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4. "TestBean" - Remote interface for EJB stateless session bean.
package foo.bean;
import javax.ejb.Remote;
import foo.domain.TestDomain;
@Remote
public interface TestBean {
public TestDomain find(Long id);
}
5. "TestBeanImpl" - EJB stateless session bean implementation. This is where the PersistenceContext is used.
package foo.bean.impl;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import foo.bean.TestBean;
import foo.domain.TestDomain;
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class TestBeanImpl implements TestBean {
@PersistenceContext(unitName="bobo")
private EntityManager manager;
public TestDomain find(Long id) {
return manager.find(TestDomain.class, id);
}
}
6. persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="bobo">
<jta-data-source>java:jboss/datasources/testDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="show_sql" value="false" />
<property name="format_sql" value="true" />
<property name="use_sql_comments" value="false" />
</properties>
</persistence-unit>
</persistence>
7. portion of standalone.xml
| <subsystem xmlns="urn:jboss:domain:datasources:1.0"> |
| <datasources> |
| <datasource jndi-name="java:jboss/datasources/testDS" pool-name="testDS" enabled="true" jta="true" use-java-context="true" use-ccm="true"> |
| <connection-url> |
| jdbc:mysql://localhost:3306/bobo |
| </connection-url> |
| <driver> |
| mysql-connector-java-5.1.7-bin.jar |
| </driver> |
| <pool> |
| <min-pool-size> |
| 1 |
| </min-pool-size> |
| <max-pool-size> |
| 5 |
| </max-pool-size> |
| <prefill> |
| true |
| </prefill> |
| <use-strict-min> |
| false |
| </use-strict-min> |
| <flush-strategy> |
| FailingConnectionOnly |
| </flush-strategy> |
| </pool> |
| <security> |
| <user-name> |
| test |
| </user-name> |
| <password> |
| test |
| </password> |
| </security> |
| </datasource> |
| <drivers> |
| <driver name="h2" module="com.h2database.h2"> |
| <xa-datasource-class> |
| org.h2.jdbcx.JdbcDataSource |
| </xa-datasource-class> |
| </driver> |
| </drivers> |
| </datasources> |
| </subsystem> |
8. Packaged into a JAR:
foo\bean\TestBean.class
foo\bean\impl\TestBeanImpl.class
foo\domain\TestCommentDomain.class
foo\domain\TestDomain.class
foo\domain\TestPersonDomain.class
META-INF\MANIFEST.MF
META-INF\persistence.xml
log4j.properties
9. A portion of the stack trace:
11:53:52,662 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-8) MSC00001: Failed to start service jboss.persistenceunit."test.jar#bobo": org.jboss.msc.service.StartException in service jboss.persistenceunit."test.jar#bobo": Failed to start service
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1786)
at org.jboss.msc.service.ServiceControllerImpl$ClearTCCLTask.run(ServiceControllerImpl.java:2291)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) [:1.6.0_14]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) [:1.6.0_14]
at java.lang.Thread.run(Thread.java:619) [:1.6.0_14]
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: bobo] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:903)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:879)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:73)
at org.jboss.as.jpa.service.PersistenceUnitService.createContainerEntityManagerFactory(PersistenceUnitService.java:170)
at org.jboss.as.jpa.service.PersistenceUnitService.start(PersistenceUnitService.java:80)
at org.jboss.msc.service.ServiceControllerImpl$StartTask.run(ServiceControllerImpl.java:1765)
... 4 more
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:92)
at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:118)
at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:70)
at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:53)
at org.hibernate.loader.entity.BatchingEntityLoader.createBatchingEntityLoader(BatchingEntityLoader.java:131)
at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1976)
at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1999)
at org.hibernate.persister.entity.AbstractEntityPersister.createLoaders(AbstractEntityPersister.java:3248)
at org.hibernate.persister.entity.AbstractEntityPersister.postInstantiate(AbstractEntityPersister.java:3234)
at org.hibernate.persister.entity.SingleTableEntityPersister.postInstantiate(SingleTableEntityPersister.java:770)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:419)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1720)
at org.hibernate.ejb.EntityManagerFactoryImpl.<init>(EntityManagerFactoryImpl.java:77)
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:894)
... 9 more
11:53:52,912 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) Stopped deployment test.jar in 6ms
11:53:52,912 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) Starting deployment of "test.jar"
11:53:52,912 INFO [org.jboss.as.server.controller] (DeploymentScanner-threads - 1) Replacement of deployment "test.jar" by deployment "test.jar" was rolled back with failure message {"Failed services" => {"jboss.persistenceunit.\"test.jar#bobo\"" => "org.jboss.msc.service.StartException in service jboss.persistenceunit.\"test.jar#bobo\": Failed to start service"},"Services with missing/unavailable dependencies" => ["jboss.deployment.unit.\"test.jar\".jndiDependencyService missing [ jboss.naming.context.java.comp.test.test.TestBeanImpl.\"env/foo.bean.impl.TestBeanImpl/manager\" ]","jboss.deployment.unit.\"test.jar\".component.TestBeanImpl.START missing [ jboss.naming.context.java.comp.test.test.TestBeanImpl.\"env/foo.bean.impl.TestBeanImpl/manager\" ]"]}
11:53:52,912 INFO [org.jboss.jpa] (MSC service thread 1-3) read persistence.xml for bobo
11:53:52,928 INFO [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-7) JNDI bindings for session bean named TestBeanImpl in deployment unit deployment "test.jar" are as follows:
java:global/test/TestBeanImpl!foo.bean.TestBean
java:app/test/TestBeanImpl!foo.bean.TestBean
java:module/TestBeanImpl!foo.bean.TestBean
java:global/test/TestBeanImpl
java:app/test/TestBeanImpl
java:module/TestBeanImpl