2011-01-26 4 views
11

Posso pigro caricare le associazioni uno a molti e molti a uno, ma non posso con le associazioni molti-a-molti.Come caricare pigro una raccolta many-to-many in modalità ibernazione?

Noi abbiamo una città in wich abbiamo mercanti wich avere indirizzi. I commercianti possono avere più indirizzi e più commercianti possono avere gli stessi indirizzi.

Quando carichiamo un commerciante con un get,

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

è ok, gli indirizzi non vengono caricati fino a quando abbiamo scorrere su di loro.

Ma quando carichiamo un elenco di commercianti,

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

anche se non otteniamo gli indirizzi, ibernazione automaticamente li carica.

Here's an example of my problem.

La mappatura:

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

Tutte le idee?

+1

sembra strano. Potresti confermare il comportamento? Come sono mappate le tue collezioni? – Bozho

+0

@Bozho Posso confermare il comportamento registrando le query e vedo che Hibernate carica gli indirizzi. Ho aggiunto i mapping nella domanda. – codea

+1

Questo non è l'argomento, ma non dovrebbe essere una delle relazioni molti-a-molti contrassegnate con l'inverso? – Ralph

risposta

-1

È possibile utilizzare l'oggetto Criteria per interrogare e utilizzare FetchMode.EAGER.

+0

l'OP ha dichiarato, che fa ** NON ** desidera caricare le altre entità associate. – luksch

1

Ci sono due correzioni che ho trovato per questo. Il facile è avere una transazione. Se avvii una transazione nel tuo metodo bussiness, sarai in grado di inizializzarla pigramente in qualsiasi momento nel ciclo di vita di quel metodo. Se le tue transazioni sono gestite in container, un semplice metodo @TransactionAttribute(TransactionAttributeType.REQUIRED) su tale metodo sarà sufficiente per ottenere questo risultato. Un altro modo è utilizzare Hibernate.initialize(object.getDesiredColletion()) anche per recuperare i tuoi oggetti, ma è necessaria anche una transazione.

La mia ultima soluzione è se non si dispone di una transazione. Questo metodo generico genererà fondamentalmente i tuoi colletions e userà il metodo setter per impostarli nell'oggetto padre. Puoi migliorare questo processo non passando un ID e ottenerlo genericamente e se non ti interessa modificare le impostazioni di sicurezza su java, puoi impostare le raccolte sull'oggetto padre direttamente (anche se è privato), nel qual caso gran parte di questo codice può essere ampiamente ridotto.

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

Non penso che questo risolva il mio problema. Quando carico un elenco di oggetti, non voglio che i figli di questo oggetto siano caricati. Grazie. – codea