如何在休眠中延迟加载多对多的集合? [英] How to lazy load a many-to-many collection in hibernate?

查看:75
本文介绍了如何在休眠中延迟加载多对多的集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以懒加载一对多和多对一的关联,但是我不能使用多对多的关联。



我们有一个城市在我们有商人极有地方。
商家可以有多个地址,多个商家可以拥有相同的地址。



当我们使用get加载商家时,

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

没关系,地址在我们迭代它们之前不会加载。



但是当我们加载商家列表时,

 城市城市=(城市)hib_session.get (City.class,city_name); (商家merchant:city.getMerchants()){
System.out.println(merchant.getName());

}

即使我们没有得到地址,hibernate也会自动加载它们。 / p>

这是我的问题的一个例子。



映射:

 < class name =Merchanttable =Merchantslazy =true> 
< id name =id
type =long
column =id>
< generator class =native>< / generator>
< / id>
< key column =merchant_id>< / key>
< / set>
< / class>

< class name =Addresstable =Adresseslazy =true>
< id name =id
type =long
column =id>
< generator class =native>< / generator>
< / id>
< set name =merchantstable =AdressesMerchantlazy =true>
< key column =adress_id/>
< / set>
< / class>

有什么想法?

解决方案

我找到了两个修复程序。简单的是有一个交易。如果您以商业方式开始交易,您将能够在该方法的生命周期内随时对这些交易进行懒惰初始化。如果你的事务是容器管理的,那么该方法上的一个简单的 @TransactionAttribute(TransactionAttributeType.REQUIRED)就足以实现这一点。另一种方法是使用 Hibernate.initialize(object.getDesiredColletion()),它也会获取你的对象,但是一个事务也是必需的。



我最后的解决方案是如果你没有交易。这个通用的方法将基本上得到你的收集,并使用setter方法将它们设置在父对象中。你可以通过不传入一个id并一般地获得它来改善这个过程,如果你不关心改变java的安全设置,你可以直接将集合设置为父对象(即使它是私有的),在这种情况下,大部分

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

logger.debug(需要获取+(childs.length)+collections);
String fieldName =;
String query =;
+(childs)的
(int i = 0; i< childs.length; i ++){
logger.debug(Fetching colletion+(i + 1)+ 。长度));
logger.debug(Collection type is+ childs [i] .getSimpleName());

fieldName = findFieldName(childs [i],parent.getClass());
if(fieldName == null){
logger.debug(试图用父类进行搜索);
logger.debug(parent.getClass()。getSuperclass());
fieldName = findFieldName(childs [i],parent.getClass()
.getSuperclass());

}
logger.debug(创建查询);
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]);

}

返回父项;



private String findFieldName(Class parentClass,Class childClass){
String fieldName = null;
boolean isCollection = false;
logger.debug(搜索字段类型
+ 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(这个字段是一个集合);
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();
休息;
$ b $ else {
logger.debug(Type =+ f.getType()。getName()+childType =
+ childClass.getName()) ;
if(f.getType()。getName()。equals(childClass.getName())){
logger.debug(***** MATCH FOUND);
fieldName = f.getName();
休息;
}

}

}

return fieldName;



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

String methodName =设置+ fieldName.substring(0,1).toUpperCase()
+ fieldName.substring(1);
logger.debug(trivial setter is:+ methodName);
Class<?> [] args = new Class<?> [] {java.util.Set.class};
//尝试一般情况
boolean methodFound = false;
方法method = null;
尝试{
method = result.getClass()。getMethod(methodName,args);
methodFound = true;
} catch(SecurityException e){
e.printStackTrace();
} catch(NoSuchMethodException e){
logger.debug(Method not found by trivial method);如果(!methodFound){
FindMethod:for(方法m:result.getClass()。getMethods()){
/ b


$ b / 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参数类型

} // end for Methods

} // end if

invokeMethod(方法,结果,假,集合);





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

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

}


I can lazy load one-to-many and many-to-one associations but I can't with the many-to-many associations.

We have a city in wich we have merchants wich have adresses. Merchants can have multiple addresses and multiple merchants can have the same addresses.

When we load a merchant with a get,

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

it's ok, addresses aren't load until we iterate over them.

But when we load a list of merchants,

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

even if we don't get the addresses, hibernate automatically loads them.

Here's an example of my problem.

The mapping :

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

Any ideas ?

解决方案

There are two fixes I have found for this. The easy one is to have a transaction. If you start a transaction in your bussiness method you will be able to lazily initialize these at any time within the life cycle of that method. If your transactions are container managed a simple @TransactionAttribute(TransactionAttributeType.REQUIRED) on that method will be enough to accomplish this. Another way is to use Hibernate.initialize(object.getDesiredColletion()) this will also fetch your objects but a transaction is also required.

My last solution is if you do not have a transaction. This generic method will basically get your colletions and use the setter method to set them in your parent object. You can improve this process by not passing in an id and obtaining it generically and if you dont care about changing the security settings on java, you can set the collections to your parent object directly (even if it is private) in which case much of this code can be grately reduced.

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

}

这篇关于如何在休眠中延迟加载多对多的集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆