fixing further bean field property mapping probelm, contributes to #3841: resolve...
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / dao / initializer / AdvancedBeanInitializer.java
index 579befc507545b45c04fe3e5e5222ed61e03fc53..5e75c9dbd9dc90ad89c112d6df6670cab129529d 100644 (file)
@@ -1,13 +1,12 @@
 /**\r
- * \r
+ *\r
  */\r
 package eu.etaxonomy.cdm.persistence.dao.initializer;\r
 \r
 import java.beans.PropertyDescriptor;\r
+import java.io.Serializable;\r
 import java.lang.reflect.InvocationTargetException;\r
-import java.lang.reflect.Method;\r
-import java.lang.reflect.ParameterizedType;\r
-import java.lang.reflect.Type;\r
+import java.util.ArrayList;\r
 import java.util.Collection;\r
 import java.util.Collections;\r
 import java.util.HashSet;\r
@@ -16,298 +15,512 @@ import java.util.Map;
 import java.util.Set;\r
 \r
 import org.apache.commons.beanutils.PropertyUtils;\r
-import org.apache.commons.lang.StringUtils;\r
 import org.apache.log4j.Logger;\r
+import org.hibernate.Hibernate;\r
+import org.hibernate.HibernateException;\r
+import org.hibernate.Query;\r
+import org.hibernate.collection.internal.AbstractPersistentCollection;\r
+import org.hibernate.proxy.HibernateProxy;\r
+import org.springframework.beans.factory.annotation.Autowired;\r
 \r
 import eu.etaxonomy.cdm.model.common.CdmBase;\r
+import eu.etaxonomy.cdm.persistence.dao.common.ICdmGenericDao;\r
 import eu.etaxonomy.cdm.persistence.dao.hibernate.HibernateBeanInitializer;\r
 \r
 /**\r
- * For now this is to test if we can improve performance for bean initializing\r
+ * For now this is a test if we can improve performance for bean initializing\r
  * @author a.mueller\r
  * @date 2013-10-25\r
  *\r
  */\r
 public class AdvancedBeanInitializer extends HibernateBeanInitializer {\r
 \r
-          public static final Logger logger = Logger.getLogger(AdvancedBeanInitializer.class);\r
-       \r
-           /* (non-Javadoc)\r
-            * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#load(eu.etaxonomy.cdm.model.common.CdmBase)\r
-            */\r
-           @Override\r
-           public void load(Object bean) {\r
-               initializeBean(bean, true, false);\r
-           }\r
-\r
-           /* (non-Javadoc)\r
-            * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#loadFully(eu.etaxonomy.cdm.model.common.CdmBase)\r
-            */\r
-           @Override\r
-           public void loadFully(Object bean) {\r
-               initializeBean(bean, true, true);\r
-           }\r
-\r
-           /**\r
-            * Initializes all *toOne relations of the given bean and all *toMany\r
-            * relations, depending on the state of the boolean parameters\r
-            * <code>cdmEntities</code> and <code>collections</code>\r
-            *\r
-            * @param bean\r
-            *            the bean to initialize\r
-            * @param cdmEntities\r
-            *            initialize all *toOne relations to cdm entities\r
-            * @param collections\r
-            *            initialize all *toMany relations\r
-            */\r
-           public void initializeBean(Object bean, boolean cdmEntities, boolean collections){\r
-\r
-               if(logger.isDebugEnabled()){\r
-                   logger.debug(">> starting initializeBean() of " + bean + " ;class:" + bean.getClass().getSimpleName());\r
-               }\r
-               Set<Class> restrictions = new HashSet<Class>();\r
-               if(cdmEntities){\r
-                   restrictions.add(CdmBase.class);\r
-               }\r
-               if(collections){\r
-                   restrictions.add(Collections.class);\r
-               }\r
-               Set<PropertyDescriptor> props = getProperties(bean, restrictions);\r
-               for(PropertyDescriptor propertyDescriptor : props){\r
-                   try {\r
-\r
-                       invokeInitialization(bean, propertyDescriptor);\r
-\r
-                   } catch (IllegalAccessException e) {\r
-                       logger.error("Illegal access on property " + propertyDescriptor.getName());\r
-                   } catch (InvocationTargetException e) {\r
-                       logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");\r
-                   } catch (NoSuchMethodException e) {\r
-                       logger.info("Property " + propertyDescriptor.getName() + " not found");\r
-                   }\r
-               }\r
-               if(logger.isDebugEnabled()){\r
-                   logger.debug("  completed initializeBean() of " + bean);\r
-               }\r
-           }\r
-\r
-           /* (non-Javadoc)\r
-            * @see eu.etaxonomy.cdm.persistence.dao.BeanInitializer#initializeProperties(java.lang.Object, java.util.List)\r
-            */\r
-           //TODO optimize algorithm ..\r
-           @Override\r
-           public void initialize(Object bean, List<String> propertyPaths) {\r
-\r
-               invokePropertyAutoInitializers(bean);\r
-\r
-               if(propertyPaths == null){\r
-                   return;\r
-               }\r
-\r
-               if(logger.isDebugEnabled()){\r
-                   logger.debug(">> starting to initialize " + bean + " ;class:" + bean.getClass().getSimpleName());\r
-               }\r
-               \r
-               //new\r
-               BeanInitNode rootInitializer = BeanInitNode.createInitTree(propertyPaths);\r
-               System.out.println(rootInitializer.toStringTree());\r
-               initializeBean(bean, rootInitializer);\r
-               \r
-               \r
-               //old\r
-               Collections.sort(propertyPaths);\r
-               for(String propPath : propertyPaths){\r
-                   initializePropertyPath(bean, propPath);\r
-               }\r
-               if(logger.isDebugEnabled()){\r
-                   logger.debug("   Completed initialization of " + bean);\r
-               }\r
-\r
-           }\r
-           \r
-\r
-               //new\r
-           \r
-           public void initializeBean(Object bean, BeanInitNode rootInitializer) {\r
-                       initializePropertyPath(bean, rootInitializer);\r
-                       for (BeanInitNode child : rootInitializer.getChildrenList()){\r
-                               initializeBean(bean, child);\r
-                       }\r
-               }\r
-           \r
-\r
-           @Override\r
-           public <C extends Collection<?>> C initializeAll(C beanList,  List<String> propertyPaths) {\r
-               if(propertyPaths != null){\r
-                   for(Object bean : beanList){\r
-                       initialize(bean, propertyPaths);\r
-                   }\r
-               }\r
-               return beanList;\r
-           }\r
-\r
-           /**\r
-            * Initializes the given single <code>propPath</code> String.\r
-            *\r
-            * @param bean\r
-            * @param propPath\r
-            */\r
-           void initializePropertyPath(Object bean, BeanInitNode node) {\r
-               if(logger.isDebugEnabled()){logger.debug("processing " + node.toString());}\r
-               if (StringUtils.isBlank(node.getPath())){\r
-                       return;\r
-               }\r
-\r
-               if (node.isWildcard()){\r
-                       initializeWildcardPropertyPath(bean, node);\r
-               } else {\r
-                   initializeNoWildcardPropertyPath(bean, node);\r
-               }\r
-           }\r
-\r
-               // if propPath only contains a wildcard (* or $)\r
+        public static final Logger logger = Logger.getLogger(AdvancedBeanInitializer.class);\r
+\r
+        @Autowired\r
+        ICdmGenericDao genericDao;\r
+\r
+        @Override\r
+        public void initialize(Object bean, List<String> propertyPaths) {\r
+            List<Object> beanList = new ArrayList<Object>(1);\r
+            beanList.add(bean);\r
+            initializeAll(beanList, propertyPaths);\r
+        }\r
+\r
+        //TODO optimize algorithm ..\r
+        @Override\r
+        public <C extends Collection<?>> C initializeAll(C beanList,  List<String> propertyPaths) {\r
+\r
+            if (beanList == null || beanList.isEmpty()){\r
+                return beanList;\r
+            }\r
+\r
+            //autoinitialize\r
+            for (Object bean : beanList){\r
+                autoinitializeBean(bean);\r
+            }\r
+\r
+            if(propertyPaths == null){\r
+                return beanList;\r
+            }\r
+\r
+\r
+            //new\r
+             BeanInitNode rootPath = BeanInitNode.createInitTree(propertyPaths);\r
+            if (logger.isTraceEnabled()){logger.trace(rootPath.toStringTree());}\r
+\r
+\r
+            if(logger.isDebugEnabled()){ logger.debug(">> starting to initialize beanlist ; class(e.g.):" + beanList.iterator().next().getClass().getSimpleName());}\r
+            rootPath.addBeans(beanList);\r
+            initializeNodeRecursive(rootPath);\r
+\r
+\r
+            //old - keep for safety (this may help to initialize those beans that are not yet correctly initialized by the AdvancedBeanInitializer\r
+            if(logger.isTraceEnabled()){logger.trace("Start old initalizer ... ");};\r
+            for (Object bean :beanList){\r
+                Collections.sort(propertyPaths);\r
+                for(String propPath : propertyPaths){\r
+//                         initializePropertyPath(bean, propPath);\r
+                }\r
+            }\r
+\r
+            if(logger.isDebugEnabled()){ logger.debug("   Completed initialization of beanlist "); }\r
+            return beanList;\r
+\r
+        }\r
+\r
+\r
+        //new\r
+        private void initializeNodeRecursive(BeanInitNode rootPath) {\r
+            initializeNode(rootPath);\r
+            for (BeanInitNode childPath : rootPath.getChildrenList()){\r
+                initializeNodeRecursive(childPath);\r
+            }\r
+            rootPath.resetBeans();\r
+        }\r
+\r
+        /**\r
+         * Initializes the given single <code>propPath</code> String.\r
+         *\r
+         * @param bean\r
+         * @param propPath\r
+         */\r
+        private void initializeNode(BeanInitNode node) {\r
+            if(logger.isDebugEnabled()){logger.debug(" processing " + node.toString());}\r
+            if (node.isRoot()){\r
+                return;\r
+            }else if (node.isWildcard()){\r
+                initializeNodeWildcard(node);\r
+            } else {\r
+                initializeNodeNoWildcard(node);\r
+            }\r
+        }\r
+\r
+        // if propPath only contains a wildcard (* or $)\r
         // => do a batch initialization of *toOne or *toMany relations\r
-        private void initializeWildcardPropertyPath(Object bean, BeanInitNode node) {\r
-                       boolean initToMany = node.getPath().equals(LOAD_2ONE_2MANY_WILDCARD);\r
-                   if(Collection.class.isAssignableFrom(bean.getClass())){\r
-                       initializeAllEntries((Collection)bean, true, initToMany);\r
-                   } else if(Map.class.isAssignableFrom(bean.getClass())) {\r
-                       initializeAllEntries(((Map)bean).values(), true, initToMany);\r
-                   } else{\r
-                       initializeBean(bean, true, initToMany);\r
-                   }\r
+        private void initializeNodeWildcard(BeanInitNode node) {\r
+//                     boolean initToMany = node.isToManyWildcard();\r
+            Map<Class<?>, Set<Object>> parentBeans = node.getParentBeans();\r
+            for (Class<?> clazz : parentBeans.keySet()){\r
+                //new\r
+                for (Object bean : parentBeans.get(clazz)){\r
+\r
+                    if(Collection.class.isAssignableFrom(bean.getClass())){\r
+//                                     old: initializeAllEntries((Collection<?>)bean, true, initToMany);  //TODO is this a possible case at all??\r
+                        throw new RuntimeException("Collection no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");\r
+                    } else if(Map.class.isAssignableFrom(bean.getClass())) {\r
+//                                     old: initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);  ////TODO is this a possible case at all??\r
+                        throw new RuntimeException("Map no longer expected in 'initializeNodeWildcard()'. Therefore an exception is thrown.");\r
+                    } else{\r
+                        prepareBeanWildcardForBulkLoad(node, bean);\r
+                    }\r
+                }\r
+                //end new\r
+\r
+//                     initializeNodeWildcardOld(initToMany, beans, clazz);  //if switched on move bulkLoadLazies up\r
+            }\r
+\r
+            //\r
+            bulkLoadLazies(node);\r
+        }\r
+\r
+        /**\r
+         * @param initToMany\r
+         * @param beans\r
+         * @param clazz\r
+         */\r
+        private void initializeNodeWildcardOld(boolean initToMany,\r
+                Map<Class<?>, Set<Object>> beans, Class<?> clazz) {\r
+            for (Object bean : beans.get(clazz)){\r
+\r
+                if(Collection.class.isAssignableFrom(bean.getClass())){\r
+                    initializeAllEntries((Collection<?>)bean, true, initToMany);\r
+                } else if(Map.class.isAssignableFrom(bean.getClass())) {\r
+                    initializeAllEntries(((Map<?,?>)bean).values(), true, initToMany);\r
+                } else{\r
+                    initializeBean(bean, true, initToMany);\r
+                }\r
+            }\r
+        }\r
+\r
+        private void prepareBeanWildcardForBulkLoad(BeanInitNode node, Object bean){\r
+\r
+            if(logger.isTraceEnabled()){logger.trace(">> prepare bulk wildcard initialization of a bean of type " + bean.getClass().getSimpleName()); }\r
+            Set<Class<?>> restrictions = new HashSet<Class<?>>();\r
+            restrictions.add(CdmBase.class);\r
+            if(node.isToManyWildcard()){\r
+                restrictions.add(Collection.class);\r
+            }\r
+            Set<PropertyDescriptor> props = getProperties(bean, restrictions);\r
+            for(PropertyDescriptor propertyDescriptor : props){\r
+                try {\r
+                    String property = propertyDescriptor.getName();\r
+\r
+//                  invokeInitialization(bean, propertyDescriptor);\r
+                    Object propertyValue = PropertyUtils.getProperty( bean, property);\r
+\r
+                    preparePropertyValueForBulkLoadOrStore(node, bean, property,  propertyValue );\r
+\r
+                } catch (IllegalAccessException e) {\r
+                    logger.error("Illegal access on property " + propertyDescriptor.getName());\r
+                } catch (InvocationTargetException e) {\r
+                    logger.info("Cannot invoke property " + propertyDescriptor.getName() + " not found");\r
+                } catch (NoSuchMethodException e) {\r
+                    logger.info("Property " + propertyDescriptor.getName() + " not found");\r
+                }\r
+            }\r
+            if(logger.isTraceEnabled()){logger.trace(" completed bulk wildcard initialization of a bean");}\r
+        }\r
+\r
+\r
+\r
+        // propPath contains either a single field or a nested path\r
+        // split next path token off and keep the remaining as nestedPath\r
+        private void initializeNodeNoWildcard(BeanInitNode node) {\r
+\r
+            String property = node.getPath();\r
+            int pos;\r
+\r
+            // is the property indexed?\r
+            Integer index = null;\r
+            if((pos = property.indexOf('[')) > 0){\r
+                String indexString = property.substring(pos + 1, property.indexOf(']'));\r
+                index = Integer.valueOf(indexString);\r
+                property = property.substring(0, pos);\r
+            }\r
+\r
+            //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging\r
+\r
+            for (Class<?> parentClazz : node.getParentBeans().keySet()){\r
+                if (logger.isTraceEnabled()){logger.trace(" invoke initialization on "+ node.toString()+ " beans of class " + parentClazz.getSimpleName() + " ... ");}\r
+\r
+                Set<Object> parentBeans = node.getParentBeans().get(parentClazz);\r
+\r
+                if (index != null){\r
+                    logger.warn("Property path index not yet implemented for 'new'");\r
+                }\r
+                //new\r
+                for (Object parentBean : parentBeans){\r
+                    try{\r
+                        Object propertyValue = PropertyUtils.getProperty(parentBean, mapFieldToPropertyName(property, parentBean.getClass().getSimpleName()));\r
+                        preparePropertyValueForBulkLoadOrStore(node, parentBean, property, propertyValue);\r
+                    } catch (IllegalAccessException e) {\r
+                        logger.error("Illegal access on property " + property);\r
+                    } catch (InvocationTargetException e) {\r
+                        logger.error("Cannot invoke property " + property + " not found");\r
+                    } catch (NoSuchMethodException e) {\r
+                        if (logger.isDebugEnabled()){logger.debug("Property " + property + " not found for class " + parentClazz);}\r
+                    }\r
+                }\r
+\r
+                //end new\r
+\r
+//                             initializeNodeNoWildcardOld(node, property, index, parentBeans);  //move bulkLoadLazies up again, if uncomment this line\r
+            }\r
+            bulkLoadLazies(node);\r
+\r
+        }\r
+\r
+        /**\r
+         * @param node\r
+         * @param property\r
+         * @param index\r
+         * @param parentBeans\r
+         * @throws IllegalAccessException\r
+         * @throws InvocationTargetException\r
+         * @throws NoSuchMethodException\r
+         */\r
+        private void initializeNodeNoWildcardOld(BeanInitNode node,\r
+                String property, Integer index, Set<Object> parentBeans)\r
+                throws IllegalAccessException, InvocationTargetException,\r
+                NoSuchMethodException {\r
+            for (Object bean : parentBeans){\r
+\r
+                PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);\r
+                if (logger.isTraceEnabled()){logger.trace("   unwrap " + node.toStringNoWildcard() + " ... ");}\r
+                // [1] initialize the bean named by property\r
+                Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);\r
+                if (logger.isTraceEnabled()){logger.trace("   unwrap " + node.toStringNoWildcard() + " - DONE ");}\r
+\r
+\r
+                // [2]\r
+                // handle property\r
+                if(unwrappedPropertyBean != null ){\r
+                    initializeNodeSinglePropertyOld(node, property, index, bean, unwrappedPropertyBean);\r
+                }\r
+            }\r
+        }\r
+\r
+        /**\r
+         * @param node\r
+         * @param propertyValue\r
+         * @param parentBean\r
+         * @param param\r
+         */\r
+        private void preparePropertyValueForBulkLoadOrStore(BeanInitNode node, Object parentBean, String param, Object propertyValue) {\r
+            BeanInitNode sibling = node.getSibling(param);\r
+\r
+            if (propertyValue instanceof AbstractPersistentCollection ){\r
+                //collections\r
+                if (!node.hasWildcardToManySibling()){  //if wildcard sibling exists the lazies are already prepared there\r
+                    AbstractPersistentCollection collection = (AbstractPersistentCollection)propertyValue;\r
+                    if (collection.wasInitialized()){\r
+                        storeInitializedCollection(collection, node, param);\r
+                    }else{\r
+//                                             Class<?> parentClass = parentBean.getClass();\r
+//                                             int parentId = ((CdmBase)parentBean).getId();\r
+                        if (sibling != null){\r
+                            sibling.putLazyCollection(collection);\r
+                        }else{\r
+                            node.putLazyCollection(collection);\r
+                        }\r
+                    }\r
+                }\r
+            }else{\r
+                //singles\r
+                if (!node.hasWildcardToOneSibling()){  //if wildcard exists the lazies are already prepared there\r
+                    if (! Hibernate.isInitialized(propertyValue)){\r
+                        if (propertyValue instanceof HibernateProxy){\r
+                            Serializable id = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getIdentifier();\r
+                            Class<?> persistedClass = ((HibernateProxy)propertyValue).getHibernateLazyInitializer().getPersistentClass();\r
+                            if (sibling != null){\r
+                                sibling.putLazyBean(persistedClass, id);\r
+                            }else{\r
+                                node.putLazyBean(persistedClass, id);\r
+                            }\r
+\r
+                        }else{\r
+                            logger.warn("Lazy value is not of type HibernateProxy. This is not yet handled.");\r
+                        }\r
+                    }else if (propertyValue == null){\r
+                        // do nothing\r
+                    }else{\r
+                        if (propertyValue instanceof HibernateProxy){  //TODO remove hibernate dependency\r
+                            propertyValue = initializeInstance(propertyValue);\r
+                        }\r
+                        autoinitializeBean(propertyValue);\r
+                        node.addBean(propertyValue);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+\r
+        private void autoinitializeBean(Object bean) {\r
+            invokePropertyAutoInitializers(bean);\r
+        }\r
+\r
+               private void storeInitializedCollection(AbstractPersistentCollection persistedCollection,\r
+                               BeanInitNode node, String param) {\r
+                       Collection<?> collection;\r
+\r
+                       if (persistedCollection  instanceof Collection) {\r
+                               collection = (Collection<?>) persistedCollection;\r
+                       }else if (persistedCollection instanceof Map) {\r
+                               collection = ((Map<?,?>)persistedCollection).values();\r
+                       }else{\r
+                               throw new RuntimeException ("Non Map and non Collection cas not handled in storeInitializedCollection()");\r
+                       }\r
+                       for (Object value : collection){\r
+                               preparePropertyValueForBulkLoadOrStore(node, null, param, value);\r
+                       }\r
                }\r
 \r
-       // propPath contains either a single field or a nested path\r
-               // split next path token off and keep the remaining as nestedPath\r
-        private void initializeNoWildcardPropertyPath(Object bean, BeanInitNode node) {\r
-                       \r
-               String property = node.getPath();\r
-                       int pos;\r
-\r
-                       // is the property indexed?\r
-                       Integer index = null;\r
-                       if((pos = property.indexOf('[')) > 0){\r
-                           String indexString = property.substring(pos + 1, property.indexOf(']'));\r
-                           index = Integer.valueOf(indexString);\r
-                           property = property.substring(0, pos);\r
+               private void bulkLoadLazies(BeanInitNode node) {\r
+\r
+                       if (logger.isTraceEnabled()){logger.trace("bulk load " +  node);}\r
+\r
+                       //beans\r
+                       for (Class<?> clazz : node.getLazyBeans().keySet()){\r
+                               Set<Serializable> idSet = node.getLazyBeans().get(clazz);\r
+                               if (idSet != null && ! idSet.isEmpty()){\r
+\r
+                                       if (logger.isTraceEnabled()){logger.trace("bulk load beans of class " +  clazz.getSimpleName());}\r
+                                       //TODO use entity name\r
+                                       String hql = " SELECT c FROM %s as c %s WHERE c.id IN (:idSet) ";\r
+                                       hql = String.format(hql, clazz.getSimpleName(), addAutoinitFetchLoading(clazz, "c"));\r
+                                       if (logger.isTraceEnabled()){logger.trace(hql);}\r
+                                       Query query = genericDao.getHqlQuery(hql);\r
+                                       query.setParameterList("idSet", idSet);\r
+                                       List<Object> list = query.list();\r
+\r
+                                       if (logger.isTraceEnabled()){logger.trace("initialize bulk loaded beans of class " +  clazz.getSimpleName());}\r
+                                       for (Object object : list){\r
+                                               if (object instanceof HibernateProxy){  //TODO remove hibernate dependency\r
+                                                       object = initializeInstance(object);\r
+                                               }\r
+                                               autoinitializeBean(object);\r
+                                               node.addBean(object);\r
+                                       }\r
+                                       if (logger.isTraceEnabled()){logger.trace("bulk load - DONE");}\r
+                               }\r
                        }\r
-\r
-                       try {\r
-                           //Class targetClass = HibernateProxyHelper.getClassWithoutInitializingProxy(bean); // used for debugging\r
-\r
-                           // [2.a] initialize the bean named by property\r
-\r
-                           PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(bean, property);\r
-                           if (logger.isDebugEnabled()){logger.debug("invokeInitialization "+node+" ... ");}\r
-                           Object unwrappedPropertyBean = invokeInitialization(bean, propertyDescriptor);\r
-                           if (logger.isDebugEnabled()){logger.debug("invokeInitialization "+node+" - DONE ");}\r
-                               //TODO continue\r
-//                         node.addBean(unwrappedPropertyBean);\r
-                           \r
-                           // [2.b]\r
-                           // recurse into nested properties\r
-                           if(unwrappedPropertyBean != null ){\r
-                               for (BeanInitNode childNode : node.getChildrenList()){\r
-                                       Collection<?> collection = null;\r
-                                       if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
-                                               collection = ((Map<?,?>)unwrappedPropertyBean).values();\r
-                                       }else if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
-                                       collection =  (Collection<?>) unwrappedPropertyBean;    \r
-                                       }\r
-                                       if (collection != null){\r
-                                       //collection or map\r
-                                               if (logger.isDebugEnabled()){logger.debug(" initialize collection for " + childNode.toString() + " ... ");}\r
-                                           int i = 0;\r
-                                               for (Object entrybean : collection) {\r
-                                           if(index == null){\r
-                                               initializePropertyPath(entrybean, childNode);\r
-                                           } else if(index.equals(i)){\r
-                                               initializePropertyPath(entrybean, childNode);\r
-                                               break;\r
-                                           }\r
-                                           i++;\r
-                                       }\r
-                                               if (logger.isDebugEnabled()){logger.debug(" initialize collection for " + childNode.toString() + " - DONE ");}\r
-                                           \r
-                                   }else {\r
-                                       // nested bean\r
-                                       initializePropertyPath(unwrappedPropertyBean, childNode);\r
-                                       setProperty(bean, property, unwrappedPropertyBean);\r
-                                   }\r
-                               }\r
-                           }\r
-\r
-                       } catch (IllegalAccessException e) {\r
-                           logger.error("Illegal access on property " + property);\r
-                       } catch (InvocationTargetException e) {\r
-                           logger.error("Cannot invoke property " + property + " not found");\r
-                       } catch (NoSuchMethodException e) {\r
-                           logger.info("Property " + property + " not found");\r
+                       node.resetLazyBeans();\r
+\r
+                       //collections\r
+                       for (Class<?> ownerClazz : node.getLazyCollections().keySet()){\r
+                               Map<String, Set<Serializable>> lazyParams = node.getLazyCollections().get(ownerClazz);\r
+                               for (String param : lazyParams.keySet()){\r
+                                       Set<Serializable> idSet = lazyParams.get(param);\r
+                                       if (idSet != null && ! idSet.isEmpty()){\r
+                                               if (logger.isTraceEnabled()){logger.trace("bulk load " + node + " collections ; ownerClass=" +  ownerClazz.getSimpleName() + " ; param = " + param);}\r
+\r
+                                               //TODO use entity name ??\r
+                                               //get from repository\r
+                                               List<Object[]> list;\r
+                                               String hql = "SELECT oc " +\r
+                                                               " FROM %s as oc JOIN FETCH oc.%s as col %s " +\r
+                                                               " WHERE oc.id IN (:idSet) ";\r
+\r
+//                                             String hql = "SELECT oc.%s " +\r
+//                                                             " FROM %s as oc WHERE oc.id IN (:idSet) ";\r
+                                               hql = String.format(hql, ownerClazz.getSimpleName(), param,\r
+                                                               "" /*addAutoinitFetchLoading(clazz, "col")*/);\r
+\r
+                                               try {\r
+                                                       if (logger.isTraceEnabled()){logger.trace(hql);}\r
+                                                       Query query = genericDao.getHqlQuery(hql);\r
+                                                       query.setParameterList("idSet", idSet);\r
+                                                       list = query.list();\r
+                                               } catch (HibernateException e) {\r
+                                                       e.printStackTrace();\r
+                                                       throw e;\r
+                                               }\r
+\r
+                                               //getTarget and add to child node\r
+                                               if (logger.isTraceEnabled()){logger.trace("initialize bulk loaded " + node + " collections - DONE");}\r
+                                               for (Object parentBean : list){\r
+                            try {\r
+                                                           Object propValue = PropertyUtils.getProperty(\r
+                                                                   parentBean,\r
+                                                                   mapFieldToPropertyName(param, parentBean.getClass().getSimpleName())\r
+                                                                 );\r
+\r
+                                                       if (propValue == null){\r
+                                                           logger.trace("Collection is null");\r
+                                                       }else {\r
+                                                           for(Object newBean : (Collection<Object>)propValue ) {\r
+                                                               if (newBean instanceof HibernateProxy){\r
+                                                                   newBean = initializeInstance(newBean);\r
+                                                               }\r
+                                                               autoinitializeBean(newBean);\r
+                                                               node.addBean(newBean);\r
+                                                           }\r
+                                                       }\r
+                            } catch (Exception e) {\r
+                                // TODO better throw an exception ?\r
+                                logger.error("error while getting collection property", e);\r
+                            }\r
+                                               }\r
+                                               if (logger.isTraceEnabled()){logger.trace("bulk load " + node + " collections - DONE");}\r
+                                       }\r
+                               }\r
                        }\r
+                       for (AbstractPersistentCollection collection : node.getUninitializedCollections()){\r
+                               if (! collection.wasInitialized()){  //should not happen anymore\r
+                                       collection.forceInitialization();\r
+                               }\r
+                       }\r
+\r
+                       node.resetLazyCollections();\r
+\r
+                       if (logger.isDebugEnabled()){logger.debug("bulk load " +  node + " - DONE ");}\r
+\r
                }\r
 \r
-           //TODO check if needed in advanced\r
-           private Object invokeInitialization(Object bean, PropertyDescriptor propertyDescriptor) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {\r
-\r
-               if(propertyDescriptor == null || bean == null){\r
-                   return null;\r
-               }\r
-\r
-               // (1)\r
-               // initialialization of the bean\r
-               //\r
-               Object propertyProxy = PropertyUtils.getProperty( bean, propertyDescriptor.getName());\r
-               Object propertyBean = initializeInstance(propertyProxy);\r
-\r
-               if(propertyBean != null){\r
-                   // (2)\r
-                   // auto initialialization of sub properties\r
-                   //\r
-                   if(CdmBase.class.isAssignableFrom(propertyBean.getClass())){\r
-\r
-                       // initialization of a single bean\r
-                       CdmBase cdmBaseBean = (CdmBase)propertyBean;\r
-                       invokePropertyAutoInitializers(cdmBaseBean);\r
-\r
-                   } else if(Collection.class.isAssignableFrom(propertyBean.getClass()) ||\r
-                           Map.class.isAssignableFrom(propertyBean.getClass()) ) {\r
-\r
-                       // it is a collection or map\r
-                       Method readMethod = propertyDescriptor.getReadMethod();\r
-                       Type genericReturnType = readMethod.getGenericReturnType();\r
-\r
-                       if(genericReturnType instanceof ParameterizedType){\r
-                           ParameterizedType type = (ParameterizedType) genericReturnType;\r
-                           Type[] typeArguments = type.getActualTypeArguments();\r
-\r
-                           if(typeArguments.length > 0\r
-                                   && typeArguments[0] instanceof Class<?>\r
-                                   && CdmBase.class.isAssignableFrom((Class<?>) typeArguments[0])){\r
-\r
-                               if(Collection.class.isAssignableFrom((Class<?>) type.getRawType())){\r
-                                   for(CdmBase entry : ((Collection<CdmBase>)propertyBean)){\r
-                                       invokePropertyAutoInitializers(entry);\r
-                                   }\r
-                               }\r
-                           }\r
-\r
-                       }\r
-                   }\r
-               }\r
-\r
-               return propertyBean;\r
-           }\r
-\r
-           /**\r
-            * @param collection of which all entities are to be initialized\r
-            * @param cdmEntities initialize all *toOne relations to cdm entities\r
-            * @param collections initialize all *toMany relations\r
-            */\r
-           private void initializeAllEntries(Collection collection, boolean cdmEntities, boolean collections) {\r
-               for(Object bean : collection){\r
-                   initializeBean(bean, cdmEntities, collections);\r
-               }\r
-           }\r
+\r
+        private String addAutoinitFetchLoading(Class<?> clazz, String beanAlias) {\r
+            Set<AutoPropertyInitializer<CdmBase>> inits = getAutoInitializers(clazz);\r
+            String result = "";\r
+            for (AutoPropertyInitializer<CdmBase> init: inits){\r
+                result +=init.hibernateFetchJoin(clazz, beanAlias);\r
+            }\r
+            return result;\r
+        }\r
+\r
+        private Set<AutoPropertyInitializer<CdmBase>> getAutoInitializers(Class<?> clazz) {\r
+            Set<AutoPropertyInitializer<CdmBase>> result = new HashSet<AutoPropertyInitializer<CdmBase>>();\r
+            for(Class<? extends CdmBase> superClass : getBeanAutoInitializers().keySet()){\r
+                if(superClass.isAssignableFrom(clazz)){\r
+                    result.add(getBeanAutoInitializers().get(superClass));\r
+                }\r
+            }\r
+            return result;\r
+        }\r
+\r
+        /**\r
+         * Rename hibernate (field) attribute to Bean property name, due to bean inconsistencies\r
+         * #3841\r
+         * @param param\r
+         * @param ownerClass\r
+         * @return\r
+         */\r
+        private String mapFieldToPropertyName(String param, String ownerClass) {\r
+            if (ownerClass.contains("Description") && param.equals("descriptionElements")){\r
+                return "elements";\r
+            }\r
+            if (ownerClass.startsWith("FeatureNode") && param.equals("children")) {\r
+                return "childNodes";\r
+            }\r
+            else{\r
+                return param;\r
+            }\r
+        }\r
+\r
+        /**\r
+         * @param node\r
+         * @param property\r
+         * @param index\r
+         * @param bean\r
+         * @param unwrappedPropertyBean\r
+         */\r
+        private void initializeNodeSinglePropertyOld(BeanInitNode node, String property,\r
+                Integer index, Object bean, Object unwrappedPropertyBean) {\r
+            Collection<?> collection = null;\r
+            if(Map.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
+                collection = ((Map<?,?>)unwrappedPropertyBean).values();\r
+            }else if (Collection.class.isAssignableFrom(unwrappedPropertyBean.getClass())) {\r
+                collection =  (Collection<?>) unwrappedPropertyBean;\r
+            }\r
+            if (collection != null){\r
+                //collection or map\r
+                if (logger.isTraceEnabled()){logger.trace(" initialize collection for " + node.toStringNoWildcard() + " ... ");}\r
+                int i = 0;\r
+                for (Object entrybean : collection) {\r
+                    if(index == null){\r
+                        node.addBean(entrybean);\r
+                    } else if(index.equals(i)){\r
+                        node.addBean(entrybean);\r
+                        break;\r
+                    }\r
+                    i++;\r
+                }\r
+                if (logger.isTraceEnabled()){logger.trace(" initialize collection for " + node.toString() + " - DONE ");}\r
+\r
+            }else {\r
+                // nested bean\r
+                node.addBean(unwrappedPropertyBean);\r
+                setProperty(bean, property, unwrappedPropertyBean);\r
+            }\r
+        }\r
 \r
 }\r