2011-01-26 40 views
11

Tôi có thể tải các liên kết một-nhiều và nhiều người một cách lười biếng nhưng tôi không thể kết hợp với nhiều liên kết.Làm thế nào để lười biếng tải một bộ sưu tập nhiều-nhiều trong hibernate?

Chúng tôi có một thành phố mà chúng tôi có các thương gia có địa chỉ. Người bán có thể có nhiều địa chỉ và nhiều người bán có thể có cùng địa chỉ.

Khi chúng ta nạp một thương gia với một get,

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

đó là ok, địa chỉ không được nạp cho đến khi chúng ta lặp qua chúng.

Nhưng khi chúng ta tải một danh sách các thương gia,

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

thậm chí nếu chúng ta không có được địa chỉ, hibernate sẽ tự động tải chúng.

Here's an example of my problem.

Các bản đồ:

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

Bất kỳ ý tưởng?

+1

nghe có vẻ lạ. Bạn có thể xác nhận hành vi không? Bộ sưu tập của bạn được ánh xạ như thế nào? – Bozho

+0

@Bozho Tôi có thể xác nhận hành vi bằng cách ghi lại các truy vấn và tôi thấy rằng Hibernate tải các địa chỉ. Tôi đã thêm ánh xạ vào câu hỏi. – codea

+1

Đó không phải là chủ đề, nhưng không phải là một trong những mối quan hệ nhiều-nhiều được đánh dấu nghịch đảo? – Ralph

Trả lời

-1

Bạn có thể sử dụng đối tượng tiêu chí để truy vấn và sử dụng FetchMode.EAGER.

+0

OP đã làm trạng thái, rằng anh ta không ** KHÔNG ** muốn tải các thực thể liên quan khác. – luksch

1

Có hai bản sửa lỗi mà tôi đã tìm thấy cho điều này. Một cách dễ dàng là có một giao dịch. Nếu bạn bắt đầu một giao dịch trong phương thức kinh doanh của bạn, bạn sẽ có thể lười biếng khởi tạo chúng bất cứ lúc nào trong vòng đời của phương pháp đó. Nếu các giao dịch của bạn là container được quản lý một đơn giản @TransactionAttribute(TransactionAttributeType.REQUIRED) trên phương pháp đó sẽ là đủ để thực hiện điều này. Một cách khác là sử dụng Hibernate.initialize(object.getDesiredColletion()) điều này cũng sẽ tìm nạp đối tượng của bạn nhưng giao dịch cũng được yêu cầu.

Giải pháp cuối cùng của tôi là nếu bạn không có giao dịch. Phương pháp chung này về cơ bản sẽ nhận được colletions của bạn và sử dụng phương thức setter để đặt chúng trong đối tượng cha của bạn. Bạn có thể cải thiện quá trình này bằng cách không chuyển id và lấy nó một cách tổng quát và nếu bạn không quan tâm đến việc thay đổi các thiết lập bảo mật trên java, bạn có thể thiết lập trực tiếp các bộ sưu tập cho đối tượng cha của bạn (ngay cả khi nó là private). mã này có thể được giảm một cách khủng khiếp.

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

Tôi không nghĩ rằng điều này sẽ giải quyết được vấn đề của tôi. Khi tôi tải một danh sách các đối tượng, tôi không muốn các con của đối tượng này được nạp. Cảm ơn. – codea

Các vấn đề liên quan