Aug 08, 2014 4:48:23 PM org.apache.openejb.core.transaction.EjbTransactionUtil handleSystemException
Schwerwiegend: EjbTransactionUtil.handleSystemException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
javax.persistence.PersistenceException: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:516)
at org.hibernate.ejb.criteria.CriteriaQueryCompiler.compile(CriteriaQueryCompiler.java:221)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:633)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.apache.openejb.persistence.JtaEntityManager.createQuery(JtaEntityManager.java:331)
at org.apache.openejb.persistence.JtaEntityManager.typedProxyIfNoTx(JtaEntityManager.java:326)
at org.apache.openejb.persistence.JtaEntityManager.createQuery(JtaEntityManager.java:385)
at de.starwit.lirejarp.ejb.impl.AbstractServiceImpl.findByIdWithRelations(AbstractServiceImpl.java:119)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)The code that leads to this exception can be found here. So a workaround is necessary and this article describes two ways I came up so far to address this problem. Unfortunately both ideas have some drawbacks and therefore any help is appreciated ;)
Possibility 1
If one uses Set instead of List to model relations in entities then you won't get aforementioned exception. Although this is working you however have to be careful, as lists have properties that sets have not. For example you will obviously loose any sorting done DB. Also changing the result type for entity attributes will have great impact for code that uses those entities. So if you do that implementation of the relation method can be used unchanged and therefore look like that: public E findByIdWithRelations(Long id, String... relations) {
Class<E> clazz = getParentClass();
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<E> criteria = cb.createQuery(clazz);
Root<E> r = criteria.from(clazz);
for (String attributeName : relations) {
r.fetch(attributeName, JoinType.LEFT);
}
criteria.where(cb.equal(r.get("id"), id));
criteria.select(r);
TypedQuery<E> query = getEntityManager().createQuery(criteria);
return query.getSingleResult();
}Possibility 2
The second idea is to force loading of related lists. This can be done by calling the getter method for every attribute representing a relation in database. So if you call the getter methods, according select statements will be executed to populate lists. You will end up with one select-statement for each attribute. Major drawback of that workaround is that for item in your result list ALL selected attributes will be read from database. This is obviously pretty bad in terms of memory, network I/O and CPU usage. public E findByIdWithRelations(Long id, String... relations) {
Class<E> clazz = getParentClass();
CriteriaBuilder cb = getEntityManager().getCriteriaBuilder();
CriteriaQuery<E> criteria = cb.createQuery(clazz);
Root<E> root = criteria.from(clazz);
criteria.where(cb.equal(root.get("id"), id));
criteria.select(root);
TypedQuery<E> query = getEntityManager().createQuery(criteria);
E result = query.getSingleResult();
for (String attributeName : relations) {
try {
Collection<?> value = (Collection<?>) PropertyUtils.getProperty(result, attributeName);
value.size();
} catch (IllegalAccessException | InvocationTargetException
| NoSuchMethodException e) {
LOG.error("Method for class " + clazz + " of property" + attributeName + " could not be resolved.", e);
}
}
return result;
}Conclusion:
Try to avoid fetching multiple one-to-many relations if you can. You can create an new Object and fill in the result of a JPA-Query to get only the attributes you need of each Entity.Complete sourcecode can be found here
There is a really useful article about the problem: JPA 2 | Fetch Joins and whether we should use them
Keine Kommentare :
Kommentar veröffentlichen