2011-01-26 12 views
11

Mogę leniwy ładować relacje jeden do wielu i wiele do jednego, ale nie mogę z powiązaniami wiele do wielu.Jak leniwie załadować kolekcję wiele do wielu w stanie hibernacji?

Mamy miasto, w którym mamy kupców, którzy mają adresy. Sprzedawcy mogą mieć wiele adresów, a wielu sprzedawców może mieć te same adresy.

Kiedy załadować kupca z GET,

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

jest ok, adresy nie są załadować aż iteracyjne nad nimi.

Ale kiedy załadować listę kupców,

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

nawet jeśli nie mamy adresy, hibernacji automatycznie ładuje je.

Here's an example of my problem.

Odwzorowanie:

<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> 

Jakieś pomysły?

+1

To brzmi dziwnie. Czy możesz potwierdzić zachowanie? W jaki sposób są odwzorowywane twoje kolekcje? – Bozho

+0

@Bozho Mogę potwierdzić zachowanie logując kwerendy i widzę, że Hibernate ładuje adresy. Dodałem mapowania w pytaniu. – codea

+1

To nie jest temat, ale nie powinno być jednym z wielu relacji oznaczonych odwrotnie? – Ralph

Odpowiedz

-1

Możesz użyć obiektu kryterium do zapytania i użyć FetchMode.EAGER.

+0

PO stwierdził, że robi ** NIE ** chce załadować inne powiązane obiekty. – luksch

1

Są dwie poprawki, które znalazłem. Łatwo jest dokonać transakcji. Jeśli rozpoczniesz transakcję w swojej biznesowej metodzie, będziesz mógł ją leniwie zainicjować w dowolnym momencie w ramach cyklu życia tej metody. Jeśli twoje transakcje są zarządzane przez kontener, wystarczy prosty @TransactionAttribute(TransactionAttributeType.REQUIRED) dla tej metody. Innym sposobem jest użycie Hibernate.initialize(object.getDesiredColletion()) spowoduje to również pobranie twoich obiektów, ale wymagana jest również transakcja.

Moje ostatnie rozwiązanie to, jeśli nie masz transakcji. Ta ogólna metoda w zasadzie dostanie twoje colletions i użyje metody ustawiającego, aby ustawić je w twoim obiekcie nadrzędnym. Możesz poprawić ten proces, nie przekazując identyfikatora i uzyskując go w sposób ogólny, a jeśli nie chcesz zmieniać ustawień zabezpieczeń w java, możesz ustawić kolekcje bezpośrednio w swoim obiekcie nadrzędnym (nawet jeśli jest to prywatny obiekt), w którym to przypadku wiele z nich ten kod można znacznie zmniejszyć.

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

Nie sądzę, że rozwiąże to mój problem. Kiedy ładuję listę obiektów, nie chcę, aby dzieci tego obiektu były ładowane. Dzięki. – codea

Powiązane problemy