config:WildFly 8.2服务器,JPA 2.1,Hibernate 4.3.7,JSF 2.2,Mojarra 2.2.8,PrimeFaces 5.1
我想调整我的JavaEE Web应用程序但是当我将@ManyToOne(fetch = FetchType.EAGER)转换为@ManyToOne(fetch = FetchType.LAZY)时会出现一些问题
我想在xhtml facelet中编辑员工类别
这是模型:
@Entity
public class Employee implements Serializable {
@Id
private Integer id;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "id_category", referencedColumnName = "id")
private EmployeeCategory category;
//...
}
@Entity
@Cacheable(true)
@Slf4j
public class EmployeeCategory implements Serializable {
@Id
private Integer id;
private String label;
@Override
public boolean equals(Object o) {
if (o==null) {
return false;
}
if (!testClasses(this,o)) {
return false;
}
final EmployeeCategory that = (EmployeeCategory) o;
return this==that || this.id==that.id;
}
private boolean testClasses(Object o1, Object o2) {
return getClass(o1).equals(getClass(o2));
}
private Class getClass(Object o) {
if(o instanceof HibernateProxy) {
return HibernateProxyHelper.getClassWithoutInitializingProxy(o);
} else {
return o.getClass();
}
}
//...
}
这是Employee的控制器,一个拥有员工的JSF托管bean,并将一些方法(save(),remove()...)暴露给facelet视图:
@Named
@ViewScoped
public class EmployeeView implements Serializable {
private Employee value;
public Employee getValue() {
return value;
}
public void save() {
//...
}
//init(), save(), remove()....
}
EmployeeCategory的EJB服务/ DAO:
@Stateless
public class EmployeeCategoryService {
@PersistenceContext(unitName="myAppPU")
private EntityManager entityManager;
public List<EmployeeCategory> findAll() {
CriteriaQuery cq = entityManager.getCriteriaBuilder().createQuery();
cq.select(cq.from(EmployeeCategory.class));
Query q = entityManager.createQuery(cq) ;
return q.getResultList();
}
//...
}
JSF管理员提供了所有EmployeeCategory列表到视图,并具有转换器的作用:
@Named
@ViewScoped
@Slf4j
public class EmployeeCategoryItems implements Converter, Serializable{
@Inject
private EmployeeCategoryService service;
private final String defaultSelection = "select one item";
private List<EmployeeCategory> values;
private Map<Integer,EmployeeCategory> index;
@PostConstruct
protected void init() {
values = service.findAll();
unproxyfied();
index = values.stream().collect(Collectors.toMap(c->c.getId(), c->c));
}
private void unproxyfied() {
values.stream().map(item -> {
if (item instanceof HibernateProxy) {
log.debug("unproxyfied() {} was HibernateProxy",item);
return (EmployeeCategory) ((HibernateProxy)item).getHibernateLazyInitializer().getImplementation();
} else {
return item;
}
}).collect(Collectors.toList()) ;
}
public String getDefaultSelection() {
return defaultSelection;
}
public List<EmployeeCategory> getValues() {
return values;
}
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value==null || value.equals(defaultSelection)) {
return null;
}
Integer id = Integer.parseInt(value);
EmployeeCategory category = index.get(id);
log.debug("getAsObject({}) return {}", value, category);
return entity;
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return null;
}
if(value instanceof HibernateProxy) {
Integer id = (Integer) ((HibernateProxy)value).getHibernateLazyInitializer().getIdentifier();
log.debug("getAsString({}) return {} HibernateProxy", value, id);
return id.toString();
} else {
Integer id = ((EmployeeCategory)value).getId();
log.debug("getAsString({}) return {}", value, id);
return id.toString();
}
}
}
和xhtml页面,其中pf是primefaces组件的前缀:
<h:form>
<pf:messages/>
<!-- ... -->
<pf:selectOneMenu value="#{employeeView.value.category}"
converter="#{employeeCategoryItems}">
<f:selectItem itemValue="#{null}"
itemLabel="#{employeeCategoryItems.defaultSelection}"
noSelectionOption="true"/>
<f:selectItems value="#{employeeCategoryItems.values}"
var="item" itemValue="#{item}" itemLabel="#{item.label}"/>
</pf:selectOneMenu>
<!-- ... -->
<pf:commandButton value="Save" action="#{employeeView.save()}" update="@form"/>
</h:form>
所以当我用HibernateProxy替换真正的EmployeeCategory对象时使用延迟加载而不是急切
时会出现问题首先一切顺利,在页面中selectOneMenu被设置为“选择一个项目”,显示类别列表,我成功保存(id_category在数据库中设置)。
但是当我重新加载页面时,selectOneMenu再次显示“选择一个项目” 如果我选择相同的类别并再次保存,我会收到一条JSF消息:“类别:验证错误:值无效”,但如果我选择其他类别,我会成功保存。
我在这个问题上进行了很多调查,正如您在代码中看到的那样,我尝试在employeeCategoryItems中取消解析对象。我还更改了EmployeeCategory的equals()方法以了解HibernateProxy,我还尝试将所有注释移动到getter而不是field,并且还尝试在EmployeeService.findById()中的unproxyfied employee.category中调用EmployeeView (此处未显示)。所有这些解决方案都不起作用。
我找到的唯一解决方案是在@ApplicationScoped中设置employeeCategoryItems的范围,并在启动时加载此JSF托管bean,在这种情况下,在系统工作的员工之前首先在employeeCategoryItems中加载项目。但我无法将此解决方案应用于所有@ManyToOne ......
我不知道HibernateProxy和JSF(或PrimeFaces)之间出了什么问题,我不知道如何很好地解决它。
我唯一的选择就是让@ManyToOne处于EAGER模式
如果有人可以帮助我,那就太棒了
答案 0 :(得分:0)
解决方案几乎就在那里,实际上我忘记了EmployeeCategoryItems.unproxyfied()方法中的一段代码
private void unproxyfied() {
values = values.stream().map(item -> {
if (item instanceof HibernateProxy) {
return (EmployeeCategory) ((HibernateProxy)item).getHibernateLazyInitializer().getImplementation();
} else {
return item;
}
}).collect(Collectors.toList()) ;
}
有了这个,解决方案正在运作