我可以延迟加载一对多和多对一关联,但我不能延迟加载多对多关联。
我们有一个城市,我们的商人有地址。 商家可以有多个地址,多个商家可以有相同的地址。
当我们用 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());
}
即使我们没有得到地址,hibernate 也会自动加载它们。
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>
有什么想法吗?
最佳答案
我找到了两个修复方法。最简单的是进行交易。如果你在你的业务方法中开始一个事务,你将能够在该方法的生命周期内的任何时间延迟初始化这些。如果您的事务是容器管理的,那么该方法上的一个简单的 @TransactionAttribute(TransactionAttributeType.REQUIRED)
就足以完成此操作。另一种方法是使用 Hibernate.initialize(object.getDesiredColletion())
这也将获取您的对象,但还需要一个事务。
我的最后一个解决方案是,如果您没有交易。这个通用方法基本上会获取您的集合并使用 setter 方法将它们设置在您的父对象中。您可以通过不传递 id 并以一般方式获取它来改进此过程,如果您不关心更改 java 上的安全设置,您可以将集合直接设置为您的父对象(即使它是私有(private)的)在这种情况下这段代码可以大大减少。
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();
}
}
关于java - 如何在 hibernate 中延迟加载多对多集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4805859/