2011-01-26 201 views
11

我可以懶加載一對多和多對一的關聯,但我不能與多對多的關聯。如何在休眠中延遲加載多對多的集合?

我們有一個城市,我們有商人有很多地址。 商家可以有多個地址,多個商家可以擁有相同的地址。

當我們加載一個商人用GET,

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

它的確定,地址不會加載,直到我們遍歷他們。

但是,當我們加載的商家列表,

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

即使我們沒有得到的地址,自動休眠加載它們。

Here's an example of my problem.

映射:

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

任何想法?

+1

聽起來很奇怪。你能證實這種行爲嗎?你的藏品如何映射? – Bozho 2011-01-26 14:50:43

+0

@Bozho我可以通過記錄查詢來確認行爲,並且我看到Hibernate加載了地址。我在問題中添加了映射。 – codea 2011-01-26 15:07:44

+1

這不是主題,但不應該成爲標記爲逆的多對多關係之一? – Ralph 2011-01-26 16:08:39

回答

-1

您可以使用條件對象來查詢和使用FetchMode.EAGER。

1

我找到了兩個修復程序。簡單的是有一個交易。如果您以商業方式開始交易,您將能夠在該方法的生命週期內隨時對這些交易進行懶惰初始化。如果您的交易是容器管理,那麼使用該方法的簡單@TransactionAttribute(TransactionAttributeType.REQUIRED)就足以實現此目的。另一種方法是使用Hibernate.initialize(object.getDesiredColletion())這也將獲取您的對象,但交易也是必需的。

我最後的解決方案是如果你沒有交易。這個通用的方法將基本上得到你的收集,並使用setter方法將它們設置在父對象中。你可以通過不傳遞一個id並一般地獲得它來改善這個過程,如果你不關心改變java的安全設置,你可以直接將集合設置爲父對象(即使它是私有的),在這種情況下,大部分這個代碼可以大大減少。

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(); 
    } 

}