在我的JSF应用程序中,我有一段非常简单的代码。我希望用户从<h:selectOneMenu>
:中的集合中选择一个值
<h:selectOneMenu value="#{bean.value}">
<f:selectItems value="#{dao.valuesFromDb}" />
</h:selectOneMenu>
#{bean.value}
的类型为Region
,#{dao.valuesFromDb}
返回Regions
的列表。问题是,bean中的Region
是从hibernate惰性加载的,hibernate将其封装在某种包装器中,类看起来像my.package.Region_$$_javassist_15@25183
。
如果#{bean.value}
设置了一些值,则如果它在值列表(#{dao.valuesFromDb}
)中,则应在页面上预先选择它。问题是它是由equals
方法检查的,该方法返回false
,因为类型不同。
如何解决这个问题?是否可以强制JSF不使用equals
,而是自己处理这种比较?重写equals
从而忽略类型是IMHO非常糟糕的主意,因为它会破坏equals
的对称性。
在这种情况下,我会通过在bean
中急切地获取Region
来完全避免这个问题。
与简单地获取JSF想要进行比较的信息相比,使用equals()会导致代码不那么清晰。正如你所指出的,这很可能会产生意想不到的后果。
我们决定在Override"equals"方法中解决此问题。重要的技巧是永远不要在equals方法中直接引用外部对象的属性,而是始终使用getter。然后Hibernate似乎从javassist对象中挖掘出了正确的值。
在您的情况下,Region equals方法应该如下所示:
@Override
public boolean equals(Object obj) {
if (this == obj) {return true;}
if (obj == null) {return false;}
if (!(obj instanceof Region)) {return false;}
Region region = (Region) obj;
if ((name == null) && (region.getName() != null)) {return false;} // Do NOT use 'region.name' here
else if (!name.equals(region.getName())) {return false;} // Do NOT use 'region.name' here
return true;
}
您可以通过在转发到视图的(控制器的)操作中调用Region
getter来预加载bean,而不是将fetch类型更改为EAGER。I.e:只需调用bean.getRegion()
方法。
这样,您就不会得到LazyInitializationException(如果这就是您得到的)。