S2DaoのTIPS

引き続きメモ。

Pure Interface戦略

S2Daoのための定数アノテーションなどをinterfaceに書くと、クライアントクラスから見て不要なはずの定数にアクセスできたり、ちょっと気持ち悪いところもあります。
S2Daoではなく別のO/Rマッパーを使ったり、JDBCで実装することになった場合もinterfaceクラスの不要な記述を削除する必要があります。

そうならないために、pureなinterfaceを守ってS2Daoを利用することもできます。

public interface HogeDao {
  Hoge findById(String hogeId);
  void insert(Hoge hoge);
  void update(Hoge hoge);
  void delete(Hoge hoge);
}

@Aspect("dao.interceptor")
public abstract class HogeDaoImpl implements HogeDao {
  public static final Class BEAN = Hoge.class;
  public static final String findById_ARGS = "hogeid";
  public static final String udpate_QUERY = ".....";
}

このようにすれば、interfaceはpureなまま、S2Dao関連の実装情報はすべて実装クラスにカプセル化することができます。
abstractにすることでinterfaceで宣言されたメソッドをいちいち書く必要もありません。

デメリットは実装クラスも用意しないといけないので、ファイルが多くなることくらいかと。

このような構成にしていると実装メソッドの追加や、後述の多段Dao構成への対応などがやりやすいというメリットもあり、結構お勧めかと思います。

SubDaoによる多段Dao構成

現状のS2DaoでもN:1マッピングのSELECT系については、複数Beanを自動で組み合わせてくれます。
しかし、N:1マッピングのINSERT/UPDATE/DELETEや、1:NのCRUDすべてについては、複合化されたBeanのそれぞれごとに個別にDaoを用意して、クライアントクラス側でそれぞれを呼び出してあげる必要があります。

クライアントクラス側で意識しなければならないことが多いということは、バグを作りこみやすいということ。できればDao層側で対応してあげたい。このメソッドを呼ぶだけでいいんですよ、と簡単に説明したい。

というわけで、多段Dao構成。一言で言えばFacadeパターンです、で終了。

具体的にいうと、
FooエンティティとBarエンティティがあって、1:Nの関係にあるとします。

  public class Foo {
    private String fooId;
    private List<Bar> barList;
    //getter/setter省略
  }
  public class Bar {
    private String fooId;
    private String barId;
    //getter/setter省略
  }

みたいになっているとき、

  public interface FooSubDao {
    public static final Class BEAN = Foo.class;
    Foo findByFooId(String fooId);
    void insert(Foo foo);
  }
  public interface BarSubDao {
    public static final Class BEAN = Bar.class;
    List<Bar> findAllByFooId(String fooId);
    void insert(Bar bar);
  }

というサブDaoを用意します。いつものようにS2Daoを使ってパパッと実装します。
ここでpure interface戦略をとっても構いません。というか個人的にはそっちがお勧め。

で、

  public interface FooDao {
    Foo findByFooId(String fooId);
    void insert(Foo foo);
  }
  public class FooDao {
    private FooSubDao fooSubDao;
    private BarSubDao barSubDao;
    public void setFooSubDao(FooSubDao fooSubDao) { .... } // injection
    public void setBarSubDao(BarSubDao barSubDao) { .... } // injection

    public Foo findByFooId(String fooId) {
      Foo foo = fooSubDao.findByFooId(fooId);
      List<Bar> barList = barSubDao.findByFooId(fooId);
      foo.setBarList(barList);
      return foo;
    }
    public void insert(Foo foo) {
      fooSubDao.insert(foo);
      List<Bar> barList = foo.getBarList();
      for (Bar bar : barList) {
        barSubDao.insert(bar);
      }
    }
  }

こんな感じでファサードDaoを実装すると。


命名規約的には悩んでいるところもあって、サブDao側をFooDao, BarDaoと普通に命名しておいて、ファサードDao側をFooFacadeDaoみたいにする手もあるかと思います。
ただ今回は、Fooに含まれるBarもひっくるめてFooオブジェクトの状態であり、それを永続化するDaoなんですよ!という意味を強めに採用して、FooDaoとその愉快なサブDao達構成にしてみました。


※なお、動作確認したコードそのままではないので細かい点で色々あるかもしれませんが、あくまで構成についての話ということで。定数アノテーションが不足しているところなどが多々ありますのでご了承ください。(あえて省略してるところもあり)