以下を含むタペストリー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); }
サービス パッケージの AppModule で、binder.bind を使用してバインドされている汎用 DAO インターフェイスの実装
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 を使用して作成されたため、次のようになりました (そのうちの 1 つの例)。
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 [エラー] pages.RoomPage SetupRender の Render キュー エラー [RoomPage:ge.grid]: コンポーネント RoomPage:ge.grid のパラメーター 'source' の読み取りに失敗しました: org.apache.tapestry5.ioc.internal.util.TapestryException org.apache .tapestry5.ioc.internal.util.TapestryException: コンポーネント RoomPage:ge.grid のパラメータ 'source' の読み取りに失敗しました: org.apache.tapestry5.ioc.internal.util.TapestryException [クラスパス:com/mycompany/myproject/components/ で] GenericEditor.tml、5 行目]
次に、グリッド要素を完全に削除し、GenericEditor を BeanEditForm のみで実行しようとしました。これにより、ページが実際に読み込まれましたが、ページに予想されるフォームが表示される代わりに、Room エンティティのフィールドとフォームの最後に [作成/更新] ボタンが表示され、[作成/更新] ボタンだけが表示されました。 BeanEditForm が属性のないオブジェクトで作成されたかのように、フィールドはありません。[作成/更新] ボタンを押すと、別のヌル ポインター例外が作成されます。
デバッグの目的で、GenericEditor.java を非ジェネリックな方法で動作するように変更しました。その中にジェネリック型 T の別の属性を作成し、(T) としてキャストされた Room 型の新しいオブジェクトとして初期化します。次に、以下に示すように、属性クラスが部屋属性と同じタイプであることを宣言します。
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[com.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 が意図した Room ではなく、AbstractEntity であることを意味していると私は考えています。私の仮定が正しければ、ジェネリックを介して適切なクラスを転送していないため、GenericEditor を誤用しています。適切なクラスをどのように転送すればよいのでしょうか? それとも私の仮定が間違っていて、ここで何か他のことが間違っていますか?