2011-01-26 19 views
11

Puedo cargar asociaciones uno a muchos y muchos a uno, pero no puedo con las asociaciones de muchos a muchos.Cómo cargar perezoso una colección de muchos a muchos en hibernación?

Tenemos una ciudad en la que tenemos comerciantes que tienen direcciones. Los comerciantes pueden tener múltiples direcciones y múltiples comerciantes pueden tener las mismas direcciones.

Cuando cargamos un comerciante con un encuentro,

Merchant merchant = (Merchant) hib_session.get(Merchant.class, id); 
System.out.println(merchant.getName()); 

está bien, direcciones no se carga hasta que iteramos sobre ellos.

Pero cuando cargamos una lista de comerciantes,

City city = (City) hib_session.get(City.class, city_name); 
for(Merchant merchant : city.getMerchants()) { 
    System.out.println(merchant.getName()); 
} 

incluso si no obtenemos las direcciones, hibernación automáticamente los carga.

Here's an example of my problem.

El mapeo:

<class name="Merchant" table="Merchants" lazy="true"> 
    <id name="id" 
    type="long" 
    column="id"> 
    <generator class="native"></generator> 
    </id> 
    <set name="addresses" table="AdressesMerchant" lazy="true"> 
    <key column="merchant_id"></key> 
    <many-to-many class="Adresses" column="address_id"/> 
    </set> 
</class> 

<class name="Address" table="Adresses" lazy="true"> 
    <id name="id" 
    type="long" 
    column="id"> 
    <generator class="native"></generator> 
    </id> 
    <set name="merchants" table="AdressesMerchant" lazy="true"> 
    <key column="adress_id"/> 
    <many-to-many column="merchant_id" class="Merchant"/> 
    </set> 
</class> 

¿Alguna idea?

+1

que suena extraño. ¿Podrías confirmar el comportamiento? ¿Cómo se mapean sus colecciones? – Bozho

+0

@Bozho Puedo confirmar el comportamiento registrando las consultas y veo que Hibernate carga las direcciones. Agregué las asignaciones en la pregunta. – codea

+1

Ese no es el tema, pero ¿no debería ser una de las relaciones de muchos a muchos marcados con inversa? – Ralph

Respuesta

-1

Puede usar el objeto de criterios para consultar y usar FetchMode.EAGER.

+0

OP dijo que ** NO ** quiere cargar las otras entidades asociadas. – luksch

1

Hay dos soluciones que he encontrado para esto. Lo fácil es tener una transacción. Si inicia una transacción en su método de negocio, podrá inicializarla de forma lenta en cualquier momento dentro del ciclo de vida de ese método. Si sus transacciones son administradas por contenedor, un simple @TransactionAttribute(TransactionAttributeType.REQUIRED) en ese método será suficiente para lograr esto. Otra forma es usar Hibernate.initialize(object.getDesiredColletion()), esto también traerá sus objetos pero también se requiere una transacción.

Mi última solución es si no tiene una transacción. Este método genérico básicamente obtendrá sus colecciones y usará el método setter para configurarlas en su objeto principal. Puede mejorar este proceso al no ingresar una identificación y obtenerla genéricamente y, si no le importa cambiar la configuración de seguridad en java, puede establecer las colecciones directamente a su objeto principal (incluso si es privado), en cuyo caso gran parte de este código se puede reducir grately.

public Object fetchCollections(Object parent, Long id, Class<?>... childs) { 

    logger.debug("Need to fetch " + (childs.length) + " collections"); 
    String fieldName = ""; 
    String query = ""; 
    for (int i = 0; i < childs.length; i++) { 
     logger.debug("Fetching colletion " + (i + 1) + " of " 
       + (childs.length)); 
     logger.debug("Collection type is " + childs[i].getSimpleName()); 

     fieldName = findFieldName(childs[i], parent.getClass()); 
     if (fieldName == null) { 
      logger.debug("Trying to search with parent class"); 
      logger.debug(parent.getClass().getSuperclass()); 
      fieldName = findFieldName(childs[i], parent.getClass() 
        .getSuperclass()); 

     } 
     logger.debug("Creating query"); 
     query = "from " + childs[i].getSimpleName() + " obj " + "where " 
     + " obj." + fieldName + ".id=" + id; 
     logger.debug("Query= " + query); 
     Set collection = new HashSet(em.createQuery(query).getResultList()); 
     setCollection(parent, collection, fieldName, childs[i]); 

    } 

    return parent; 

} 


private String findFieldName(Class parentClass, Class childClass) { 
    String fieldName = null; 
    boolean isCollection = false; 
    logger.debug("Searching for field of type " 
      + childClass.getSimpleName()); 
    for (Field f : parentClass.getDeclaredFields()) { 

     String type = f.getGenericType().toString(); 
     if (f.getType().isInterface() 
       && f.getGenericType().toString().contains("java.util.Set")) { 
      logger.debug("This field is a collection"); 
      isCollection = true; 
      type = type.substring(type.indexOf("<") + 1); 
      type = type.substring(0, type.length() - 1); 
     } 

     if (isCollection) { 
      logger.debug("Field= " + f.getName() + " " 
        + f.getGenericType()); 
      if (type.equals(childClass.getName())) { 
       logger.debug("*****MATCH FOUND"); 
       fieldName = f.getName(); 
       break; 
      } 
     } else { 
      logger.debug("Type=" + f.getType().getName() + " childType=" 
        + childClass.getName()); 
      if (f.getType().getName().equals(childClass.getName())) { 
       logger.debug("*****MATCH FOUND"); 
       fieldName = f.getName(); 
       break; 
      } 

     } 

    } 

    return fieldName; 
} 


    private void setCollection(Object result, Set collection, String fieldName, 
     Class childClass) { 

    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() 
    + fieldName.substring(1); 
    logger.debug("trivial setter is :" + methodName); 
    Class<?>[] args = new Class<?>[] { java.util.Set.class }; 
    // try the trivial case 
    boolean methodFound = false; 
    Method method = null; 
    try { 
     method = result.getClass().getMethod(methodName, args); 
     methodFound = true; 
    } catch (SecurityException e) { 
     e.printStackTrace(); 
    } catch (NoSuchMethodException e) { 
     logger.debug("Method not found by trivial method"); 

    } 

    if (!methodFound) { 
     FindMethod: for (Method m : result.getClass().getMethods()) { 
      // logger.debug(m.getName()); 
      for (Type t : m.getGenericParameterTypes()) { 
       // logger.debug("\t"+t); 
       String type = t.toString(); 
       type = type.substring(type.indexOf("<") + 1); 
       type = type.substring(0, type.length() - 1); 
       if (type.equals(childClass.getName())) { 
        logger.debug("***Found the Setter Method"); 
        method = m; 
        break FindMethod; 
       } 
      }// end for parameter Types 

     }// end for Methods 

    }// end if 

    invokeMethod(method, result, false, collection); 

} 



private void invokeMethod(Method method, Object obj, boolean initialize, 
     Object... args) { 

    try { 
     if (method != null) { 
      if (initialize) 
       Hibernate.initialize(method.invoke(obj, args)); 
      else 
       method.invoke(obj, args); 

     } 
     logger.debug("Method executed successfully"); 
    } catch (IllegalArgumentException e) { 

     e.printStackTrace(); 
    } catch (IllegalAccessException e) { 
     e.printStackTrace(); 
    } catch (InvocationTargetException e) { 
     e.printStackTrace(); 
    } 

} 
+2

No creo que esto solucione mi problema. Cuando cargo una lista de objetos, no deseo que se carguen los elementos secundarios de este objeto. Gracias. – codea

Cuestiones relacionadas