0

我有一个 Tapestry 5 项目,其中包含以下内容:

  • 实体包中的抽象实体,由所有其他具体实体继承

    import java.io.Serializable;
    import javax.persistence.Basic;
    import javax.persistence.Column;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.MappedSuperclass;
    
    @MappedSuperclass
    public class AbstractEntity implements Serializable, Comparable<AbstractEntity> {
    
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "ID")
    protected Integer id;
    
    @Override
    public int compareTo(AbstractEntity o) {
         return this.toString().compareTo(o.toString());
    }
    
    }
    
  • 继承 AbstractEntity 的几个具体实体(我将省略它们的大部分实体,因为我认为它与问题完全无关,它们是简单的实体数据类)。一个这样的实体类的示例:

    //Imports go here
    
    @Entity
    @Table(name = "room")
    @NamedQueries({
        @NamedQuery(name = "Room.findAll", query = "SELECT r FROM Room r")})
    public class Room extends AbstractEntity {
        private static final long serialVersionUID = 1L;
        @Basic(optional = false)
        @Column(name = "ROOM_TYPE")
        @Validate("required")
        @Enumerated(EnumType.STRING)
        private RoomType roomType;
    //rest of the attributes and their annotations go here, as well as setter/getter methods
    
  • 一个通用的 DAO 接口

    import com.mycompany.myproject.entities.AbstractEntity;
    import java.util.List;
    
    public interface GenericDAO <T extends AbstractEntity>{
    
    public abstract List<T> getListOfObjects(Class myclass);
    public abstract T getObjectById(Integer id, Class myclass);
    public abstract T addOrUpdate(T obj);
    public abstract T delete(Integer id, Class myclass);
    
    }
    
  • 通用 DAO 接口的实现,在服务包中的 AppModule 中使用 binder.bind 绑定到它

    import com.mycompany.myproject.entities.AbstractEntity;
    import java.util.Collections;
    import java.util.List;
    import org.hibernate.Criteria;
    import org.hibernate.Session;
    import org.hibernate.criterion.Restrictions;
    
    public class GenericDAOImpl<T extends AbstractEntity> implements GenericDAO<T> {
    
    private Session session;
    
    @Override
    public List getListOfObjects(Class myclass) {
        List<T> list = session.createCriteria(myclass).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
        Collections.sort(list);
        return list;
    }
    
    @Override
    public T getObjectById(Integer id, Class myclass) {
        AbstractEntity ae = (AbstractEntity) session.createCriteria(myclass)
            .add(Restrictions.eq("id", id)).list().get(0);
        return (T) ae; 
    }   
    
    @Override
    public AbstractEntity addOrUpdate(AbstractEntity obj) {
        return (T) session.merge(obj);
    }
    
    @Override
    public T delete(Integer id, Class myclass) {
        AbstractEntity ae = (AbstractEntity) session.createCriteria(myclass)
            .add(Restrictions.eq("id", id)).list().get(0);
        session.delete((T) ae);
        session.flush();
        return (T) ae;
    }
    
    }
    
  • 组件包中的通用编辑器 java 类

    import com.mycompany.myproject.entities.AbstractEntity;
    import com.mycompany.myproject.services.GenericDAO;
    import java.util.List;
    import org.apache.tapestry5.ComponentResources;
    import org.apache.tapestry5.PropertyConduit;
    import org.apache.tapestry5.annotations.Persist;
    import org.apache.tapestry5.annotations.Property;
    import org.apache.tapestry5.beaneditor.BeanModel;
    import org.apache.tapestry5.hibernate.annotations.CommitAfter;
    import org.apache.tapestry5.ioc.annotations.Inject;
    import org.apache.tapestry5.services.BeanModelSource;
    import org.apache.tapestry5.services.PropertyConduitSource;
    
    public class GenericEditor<T extends AbstractEntity> {
    
    @Inject
    private PropertyConduitSource conduit;
    @Inject
    private GenericDAO genericDAO;
    @Property
    @Persist
    private T bean;
    @Property
    private T row;
    @Inject
    private BeanModelSource bms;
    @Inject
    private ComponentResources cr;
    
    private Class myclass;
    
    {
        PropertyConduit conduit1 = conduit.create(getClass(), "bean");
        myclass = conduit1.getPropertyType();
    }
    
    public List<T> getGrid(){
        List<T> temp = genericDAO.getListOfObjects(myclass);
        return temp;
    }
    
    public BeanModel<T> getFormModel(){
        return bms.createEditModel(myclass, cr.getMessages()).exclude("id");
    }
    
    public BeanModel<T> getGridModel(){
        return bms.createDisplayModel(myclass, cr.getMessages()).exclude("id");
    }
    
    @CommitAfter
    Object onActionFromDelete(int id){
        genericDAO.delete(id, myclass);
        return this;
    }
    
    @CommitAfter
    Object onActionFromEdit(int row){
        bean = (T)genericDAO.getObjectById(row, myclass);
        return this;
    }
    
    @CommitAfter
    Object onSuccess(){
        genericDAO.addOrUpdate(bean);
        try {
            bean = (T) myclass.newInstance();
        } catch(Exception ex){
        }
        return this;
    }
    
  • GenericEditor java 类的关联 .tml 文件

    <!--GenericEditor.tml-->
    <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">    
        <t:beaneditform object="bean" t:model="formModel" >
        </t:beaneditform>
        <t:grid t:source="grid" t:model="gridModel" add="edit,delete" row="row">
            <p:editCell>
                <t:actionlink t:id="edit" context="row">Edit</t:actionlink>
            </p:editCell>
            <p:deleteCell>
                <t:actionlink t:id="delete" context="row">Delete</t:actionlink>
            </p:deleteCell>
        </t:grid>
    </html> 
    
  • 此外,pages 包中有几个 java 类,以及它们相关的 .tml 文件,这些文件最初是在没有使用 genericDAO 的情况下制作的,但是使用了具体的 DAO,所以它们看起来像这样(其中一个示例):

    import com.mycompany.myproject.entities.Room;
    import com.mycompany.myproject.services.RoomDAO;
    import java.util.ArrayList;
    import java.util.List;
    import org.apache.tapestry5.annotations.Persist;
    import org.apache.tapestry5.annotations.Property;
    import org.apache.tapestry5.hibernate.annotations.CommitAfter;
    import org.apache.tapestry5.ioc.annotations.Inject;
    
    public class RoomPage {
    
        @Property
        private Room room;
        @Property
        private Room roomrow;
        @Inject
        private RoomDAO roomDAO;
    
        @Property
        private List<Room> rooms;
    
    
        void onActivate(){
            if(rooms==null){
                rooms = new ArrayList<Room>();
            }
            rooms = roomDAO.getListOfRooms();
        }
    
        @CommitAfter
        Object onSuccess(){
            roomDAO.addOrUpdateRoom(room);
            room = new Room();
            return this;
        }
    
        @CommitAfter
        Object onActionFromEdit(Room room2){
            room = room2;
            return this;
        }
    
        @CommitAfter
        Object onActionFromDelete(int id){
            roomDAO.deleteRoom(id);
            return this;
        }
    
    }
    
  • 以及相关的 .tml 文件:

    <!--RoomPage.tml-->
    <html t:type="layout" title="RoomPage"
          xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
          xmlns:p="tapestry:parameter">
        <div class="row">
    
            <div class="col-sm-4 col-md-4 col-lg-3">        
                <t:beaneditform object="room" exclude="id" reorder="roomtype, floor,
                        tv, internet"
                        submitlabel="message:submit-label"/>
            </div>
            <div class="col-sm-8 col-md-8 col-lg-9"> 
                <t:grid t:source="rooms" exclude="id" 
                add="edit,delete" row="roomrow"
                include="roomtype, floor, tv, internet">
                    <p:editCell>
                        <t:actionlink t:id="edit" context="roomrow">Edit</t:actionlink>
                    </p:editCell>
                    <p:deleteCell>
                        <t:actionlink t:id="delete" context="roomrow.id">Delete</t:actionlink>
                    </p:deleteCell>
                </t:grid>
            </div>
        </div>
    </html>
    

上面使用具体 DAO 的代码正常工作,用于在数据库中输入新行的表单按预期显示在页面上,以及包含数据库表中的行的网格。

因此,基本思想是将 GenericEditor 与 genericDAO 一起使用,以减少必要的代码量并操作任何数据库表,使用 BeanEditForm 在表中输入新行,并使用 Grid 显示表中的所有行和删除或编辑它们。理论上,这应该适用于任何继承 AbstractEntity 类的实体,因此不需要为每个实体制作单独的 DAO 接口/实现配对。

问题是,我似乎无法让它按预期工作,因为我不确定如何实际使用上面显示的 GenericEditor。我尝试了以下方法:

  • RoomPage.java 修改后:

    import com.mycompany.myproject.components.GenericEditor;
    import com.mycompany.myproject.entities.Room;
    
    public class RoomPage{
    
        @Component
        private GenericEditor<Room> ge;
    
    }
    
  • RoomPage.tml 修改后:

    <!--RoomPage.tml-->
    <html t:type="layout" title="RoomPage"
          xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
          xmlns:p="tapestry:parameter">   
    <t:GenericEditor t:id="ge" />
    </html>
    

但这显然不起作用,因为它产生的只是一个空指针异常以及这个错误:

Blockquote [ERROR] pages.RoomPage Render queue error in SetupRender[RoomPage:ge.grid]: 读取组件 RoomPage:ge.grid: org.apache.tapestry5.ioc.internal.util.TapestryException org.apache 的参数“源”失败.tapestry5.ioc.internal.util.TapestryException:读取组件 RoomPage 的参数“源”失败:ge.grid:org.apache.tapestry5.ioc.internal.util.TapestryException [at classpath:com/mycompany/myproject/components/ GenericEditor.tml,第 5 行]

然后我尝试完全删除网格元素,并仅使用 BeanEditForm 运行 GenericEditor。这导致页面实际加载,但不是在页面上显示预期的表单,而是在表单末尾显示房间实体的字段和创建/更新按钮,出现的只是创建/更新按钮,没有任何字段,就好像 BeanEditForm 是在没有任何属性的对象上创建的。按创建/更新按钮会创建另一个空指针异常。

出于调试目的,我已将 GenericEditor.java 更改为以非泛型方式工作,方法是在其中创建泛型类型 T 的另一个属性,然后将其初始化为 Room 类型的新对象,强制转换为 (T),然后然后将属性类声明为与房间属性相同的类型,如下所示

private T room; 
{
    //PropertyConduit conduit1 = conduit.create(getClass(), "bean");
    //class = conduit1.getPropertyType();
    room = (T) new Room();
    class = room.getClass();
}

运行具有这些更改的应用程序(仍然禁用网格并且仅启用 beaneditform),页面现在可以正确呈现所有输入字段。这使我得出结论,问题在于 GenericEditor 没有通过泛型接收正确的类型,但我不知道我的逻辑是否正确,即使正确,如何解决这个问题。问题的另一个可能来源可能是 PropertyConduit,我不确定它是如何工作的,以及我是否正确使用它,所以我不排除问题也源于那里。无论哪种方式,我的主要猜测是我以某种方式滥用了 GenericEditor,所以正如这个问题的标题所说,我应该如何使用 GenericEditor 才能正确访问数据库?

我已经在 stackoverflow 中搜索了与我自己类似的问题,但我找不到任何类似的东西,无论是在这里还是其他地方。我希望这里的某个人能够帮助我确定问题所在并帮助我解决它,因为我真的不知道如何自己解决。提前致谢。

更新: 我已经做了一些进一步的调试,尝试检查什么类型的类被转发到 GenericEditor 的 myclass。我修改了 GenericEditor.java 的以下位:

    {
        PropertyConduit conduit1 = conduit.create(getClass(), "bean");
        myclass = conduit1.getPropertyType();
    }

遵循:

    {
        PropertyConduit conduit1 = conduit.create(getClass(), "bean");
        System.out.println("conduit1.toString(): "+conduit1.toString());
        System.out.println("conduit1.getPropertyType().toString(): "+conduit1.getPropertyType().toString());
        System.out.println("conduit1.getPropertyType().getName(): "+conduit1.getPropertyType().getName());
        myclass = conduit1.getPropertyType();
        System.out.println("myclass.getName(): "+myclass.getName());
    }

这导致了以下输出:

管道1.toString():PropertyConduit[c​​om.mycompany.myproject.components.GenericEditor bean]

管道1.getPropertyType().toString():com.mycompany.myproject.entities.AbstractEntity 类

管道1.getPropertyType().getName():com.mycompany.myproject.entities.AbstractEntity

myclass.getName(): com.mycompany.myproject.entities.AbstractEntity

我相信这几乎意味着转发给 GenericEditor 的类型 T 是 AbstractEntity,而不是预期的 Room。如果我的假设是正确的,那么我正在滥用 GenericEditor,因为我没有通过泛型将正确的类转发给它,那么我应该如何将正确的类转发给它呢?还是我的假设是错误的,这里还有其他问题?

4

2 回答 2

1

我已经设法找到了这个问题的答案,所以我把它贴在这里以防万一有人需要它:

应用程序无法按预期工作的原因有两个:1)在 GenericDAOImpl 类中,我忘记在“私人会话会话”行上方添加 @Inject 注释,这首先产生了错误,因此该部分的代码应该是这样的:

//imports
public class GenericDAOImpl<T extends AbstractEntity> implements GenericDAO<T> {
@Inject
private Session session;
//rest of code unchanged

2)我首先不确定的是如何使用 GenericEditor 组件,我试图以错误的方式这样做,试图将组件添加到类文件和关联的 tml 文件中。应该做的是简单地扩展 GenericEditor,并删除关联的 tml 文件,因此使用 GenericEditor tml,如下所示:

public class RoomPage extends GenericEditor<Room>{
}

进行这 2 项更改后,应用程序按预期工作

于 2015-06-12T23:16:24.870 回答
0

我自己从未使用过它,但您可能对 tynamo 的挂毯模型感兴趣,我理解它有助于通用 CRUD。

于 2015-04-23T19:09:53.120 回答