Как использовать универсальный редактор для доступа к базе данных в гобелене 5?

У меня есть проект гобелена 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 in services с помощью 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;
    }
    
  • Связанный файл .tml для класса Java GenericEditor

    <!--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 Ошибка очереди рендеринга в 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 [в пути к классам: com/mycompany/myproject/components/ GenericEditor.tml, строка 5]

Затем я попытался полностью удалить элемент сетки и запустить GenericEditor только с BeanEditForm. Это привело к фактической загрузке страницы, но вместо отображения ожидаемой формы на странице с полями объекта «Комната» и кнопкой «Создать/обновить» в конце формы появилась только кнопка «Создать/обновить», без какого-либо поля, как если бы 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 похожие проблемы, но мне не удалось найти ничего похожего ни здесь, ни где-либо еще. Я надеюсь, что кто-то здесь сможет помочь мне определить, в чем проблема, и помочь мне обойти ее, поскольку я действительно понятия не имею, как это сделать самостоятельно. Заранее спасибо.

Обновление: я провел дальнейшую отладку, пытаясь проверить, какой тип класса перенаправляется в myclass GenericEditor. Я изменил следующий бит 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());
    }

и это привело к следующему результату:

conduit1.toString(): PropertyConduit[компонент com.mycompany.myproject.components.GenericEditor]

conduit1.getPropertyType().toString(): класс com.mycompany.myproject.entities.AbstractEntity

conduit1.getPropertyType().getName(): com.mycompany.myproject.entities.AbstractEntity

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

Что, я считаю, в значительной степени означает, что тип T, пересылаемый в GenericEditor, является AbstractEntity, а не Room, как предполагалось. Если мое предположение верно, я неправильно использую GenericEditor, поскольку я не получаю правильный класс, пересылаемый ему через дженерики, так как я должен перенаправить ему правильный класс? Или мое предположение неверно и здесь что-то еще не так?


person FoxEM    schedule 22.04.2015    source источник


Ответы (2)


Мне удалось найти ответ на этот вопрос, поэтому я размещаю его здесь, если кому-то это когда-нибудь понадобится:

Было 2 причины, по которым приложение не работало должным образом: 1) В классе GenericDAOImpl я забыл добавить аннотацию @Inject над строкой «private Session session», что в первую очередь привело к ошибке, так что кусок кода должно было выглядеть так:

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

2) в первую очередь я не был уверен в том, как использовать компонент GenericEditor, и я пытался сделать это неправильно, пытаясь добавить компонент в файл класса и связанный файл tml. Вместо этого предполагалось просто расширить GenericEditor и удалить связанный файл tml, поэтому вместо него используется tml GenericEditor, например:

public class RoomPage extends GenericEditor<Room>{
}

После внесения этих 2 изменений приложение работает как задумано

person FoxEM    schedule 12.06.2015

Я никогда не использовал его сам, но вас может заинтересовать tapestry-model от Tynamo, который, как я понимаю, помогает с общим CRUD.

person lance-java    schedule 23.04.2015