如何在基于Wicket、Spring和Hibernate的web应用程序中正确使用LoadableDetachableModel?

如何在基于Wicket、Spring和Hibernate的web应用程序中正确使用LoadableDetachableModel?,hibernate,spring,wicket,Hibernate,Spring,Wicket,我正在开发一个基于Hibernate、Spring和Wicket的web应用程序 到目前为止,我实现了业务对象和持久性层。事务由Spring框架的事务拦截器管理。所以DAO类的每个方法都封装在一个事务中。与单元测试一起实现这一点很简单 现在,我来到web应用程序部分,在这里我还使用Spring进行依赖项注入。我将DAO与Wicket框架的@SpringBean注释一起注入Wicket组件。但是,由于我对Wicket还很陌生,所以在将业务对象传递给Wicket组件时,在为业务对象使用正确的模型时,

我正在开发一个基于Hibernate、Spring和Wicket的web应用程序

到目前为止,我实现了业务对象和持久性层。事务由Spring框架的事务拦截器管理。所以DAO类的每个方法都封装在一个事务中。与单元测试一起实现这一点很简单

现在,我来到web应用程序部分,在这里我还使用Spring进行依赖项注入。我将DAO与Wicket框架的@SpringBean注释一起注入Wicket组件。但是,由于我对Wicket还很陌生,所以在将业务对象传递给Wicket组件时,在为业务对象使用正确的模型时,我有点卡住了

我尝试了LoadableDetachableModel,但遇到了一些问题。我得到了一个页面,可以根据页面的输入参数创建新的或编辑现有的业务对象。如果参数中有id,则应从数据库加载相应的业务对象。如果没有参数,则应创建新的业务对象。该部分是一个对象,应该被编辑运行得很好,但当一个新对象应该被创建时,我填写web表单并按save,我得到一个NullPointerException。经过一些调试后,我发现LoadableDetachableModel无法返回业务对象的实例,因为重写的load()方法无法从数据库加载该对象,因为该对象尚未保存在数据库中,因此没有id


所以现在我想知道如何解决这个问题。LoadableDetachableModel是正确的选择吗?建议将表单分为两个相互依赖的表单,并且每个表单使用不同的模型。因此,只有编辑页面/表单使用LoadableDetachableModel?

Igor Vaynberg在上对这一点和一些相关问题进行了很好的解释

该页面的最后一部分讨论了这个问题,基本上不使用
LoadableDetaccableModel
,而是实现了
AbstractEntityModel
,它允许更完整的控制。

描述如下:

现在,作为可能使用
LoadableDetachableModel
的示例,我们将 构建一个设计用于与通过JPA管理的实体协作的模型。到 了解以下代码需要基本的JPA知识 即使我们不深入讨论这个标准的细节

以下模型仅用于示例目的,不适用于 用于生产环境。重要方面如 因为没有考虑事务管理,所以您应该 在考虑使用之前,请重新编写代码

public class JpaLoadableModel<T> extends LoadableDetachableModel<T> {

    private EntityManagerFactory entityManagerFactory;
    private Class<T> entityClass;
    private Serializable identifier;
    private List<Object> constructorParams;

    public JpaLoadableModel(EntityManagerFactory entityManagerFactory, T entity) {
        super();

        PersistenceUnitUtil util = entityManagerFactory.getPersistenceUnitUtil();

        this.entityManagerFactory = entityManagerFactory;
        this.entityClass = (Class<T>) entity.getClass();
        this.identifier = (Serializable) util.getIdentifier(entity);

        setObject(entity);
    }

    @Override protected T load() {
        T entity = null;

        if(identifier != null) {
            EntityManager entityManager = entityManagerFactory.createEntityManager();
            entity = entityManager.find(entityClass, identifier);
        }
        return entity;
    }

    @Override protected void onDetach() {
        super.onDetach();

        T entity = getObject();
        PersistenceUnitUtil persistenceUtil = entityManagerFactory.getPersistenceUnitUtil();

        if(entity == null) return;

        identifier = (Serializable) persistenceUtil.getIdentifier(entity);
    }
}
公共类JpaLoadableModel扩展了LoadableDetaccessableModel{
私人实体管理工厂实体管理工厂;
私有类实体类;
私有可序列化标识符;
私有列表构造函数参数;
公共JpaLoadableModel(EntityManagerFactory EntityManagerFactory,T实体){
超级();
PersistenceUnitUtil=entityManagerFactory.getPersistenceUnitUtil();
this.entityManagerFactory=entityManagerFactory;
this.entityClass=(类)entity.getClass();
this.identifier=(可序列化)util.getIdentifier(实体);
setObject(实体);
}
@覆盖受保护的T负载(){
T实体=null;
if(标识符!=null){
EntityManager EntityManager=EntityManager工厂。createEntityManager();
entity=entityManager.find(entityClass,标识符);
}
返回实体;
}
@重写受保护的void onDetach(){
super.onDetach();
T entity=getObject();
PersistenceUnitUtil persistenceUtil=entityManagerFactory.getPersistenceUnitUtil();
if(entity==null)返回;
标识符=(可序列化)persistenceUtil.getIdentifier(实体);
}
}
模型的构造函数将两个参数作为输入:一个 JPA接口的实现
javax.persistence.EntityManagerFactory
管理JPA实体和 必须由此模型处理的实体。在其构造函数中 模型保存实体的类及其id(如果 该实体尚未被持久化)。这两个信息是 需要在以后检索实体,并由 加载方法

onDetach
负责在分离前更新实体id 发生。id可以在第一次持久化实体(JPA)时更改 生成新id并将其分配给实体)。请注意 此模型不负责保存对发生的任何更改 实体对象在分离之前。如果我们不想失去这些 在分离之前,我们必须显式地保留实体 相位发生

由于此示例的模型包含对
EntityManagerFactory
的引用,因此使用的实现必须是
可序列化的


另请参见Wicket 6.x的更新版本,该版本仍然使用
LoadableDetaccableModel
,该链接已经不存在,请改用此链接