S2JDBCをEntityの親クラスのプロパティまで見るように拡張
=== 1/31 追記 ===
@MappedSuperclassで出来るようです。
http://s2container.seasar.org/2.4/ja/s2jdbc_entity.html#%E7%B6%99%E6%89%BF
higayasuoさんからコメントで教えていただきました。ありがとうございました。
バイト先でのO/R Mapperの教育や個人的な開発にS2JDBCを使っています。シンプルだし、Hibernateでダメダメだった複数の1対多関連の紐付けがうまくいっていたりと気に入ってるんですが、シンプルがゆえに物足りなさを感じることも。
今のところ物足りないなぁと感じているのは、
- S2JDBCはEntityの親クラスのプロパティを評価してくれないのでジェネレーション・ギャップパターンが出来ない。
- ページング検索をするときに、ある検索条件+limit, offsetで検索、そのあとある検索条件をselect count(*) form (…)でかこってトータルを取得というのができない。つまり、S2Pagerみたいなこと。
2.については俺が知らないだけかも。。
1.は結構困っていたので今回自力で拡張してみました。
同じような要望を持ってる人もいるみたい。
http://ml.seasar.org/archives/seasar-user/2007-December/012363.html
アプローチは、上記のリンクでひがさんが言ってるように、S2JDBCとその周辺のクラスは拡張しやすいように作ってあるのでフレームワークのクラスを継承して拡張します。
JdbcManagerImplから辿ってクラスを眺めていたら、JdbcManagerがEntityMedaDataFactoryを持っていて、そこからEntityの情報を取得している様子。こいつの実装クラスのEntityMedaDataFactoryImplがメタデータを作成&キャッシュしているので、こいつを継承することにした。
public class CustomizedEntityMetaFactoryImpl extends EntityMetaFactoryImpl { @Override protected void doCustomize(EntityMeta entityMeta, Class<?> entityClass) { super.doCustomize(entityMeta, entityClass); // エンティティの継承をサポートする doSuperClassPropertyMeta(entityMeta, entityClass); } private void doSuperClassPropertyMeta(EntityMeta entityMeta, Class<?> entityClass) { Class<?> clazz = entityClass.getSuperclass(); while (!clazz.equals(Object.class)) { super.doPropertyMeta(entityMeta, clazz); clazz = clazz.getSuperclass(); } } }
doPropertyMetaというメソッドがあって、そこでEntityのプロパティをなめてるからそいつをoverrideしようかとも迷ったんだけども、doCustomizeという空のメソッドがあったのでカスタマイズしろ!というこだと空気を読んでこっちにした。スーパークラスのなめかたはもっといい方法があったら教えてください< > <
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd"> <components> <include path="jdbc.dicon"/> <include path="convention.dicon"/> <include path="s2jdbc-internal.dicon"/> <component class="my.s2jdbc.CustomizedEntityMetaFactoryImpl" name="entityMetaFactory" > </component> <component name="jdbcManager" class="org.seasar.extension.jdbc.manager.JdbcManagerImpl"> <property name="maxRows">0</property> <property name="fetchSize">0</property> <property name="queryTimeout">0</property> <property name="dialect">hsqlDialect</property> </component> </components>
s2jdbc.diconをこんな感じに。s2jdbc-internal.diconでEntityMetaFactoryImplが既に登録されてるから、ひとつのinterfaceに対して2つの実装クラスが登録されているあまりよくない状態になってる。でも、s2jdbc-internal.diconとかあんまいじりたくないし! > < 一応name属性による解決が先にされるはずなので、うまくいってるんだと思う。
これで、Entityクラスの継承が可能に!!doltengで作成したプロジェクトについてるEmpテーブルで試してみました。
public abstract class EmpBase implements Serializable { @Id public Integer id; public Integer empNo; public String empName; public Integer mgrId; @Temporal(TemporalType.DATE) public Date hiredate; public BigDecimal sal; public Integer deptId; @Version public Integer versionNo; } @Entity public class Emp extends EmpBase { private static final long serialVersionUID = -1L; } public class Test extends S2TestCase { JdbcManager jdbcManager_; @Override protected void setUp() throws Exception { include("app.dicon"); super.setUp(); } public void testGap() { List<Emp> emps = jdbcManager_.from(Emp.class) .getResultList(); System.out.println(emps); } }
こんな感じでやったらうまくいってました。やったね!