HibernateのResultTransformerを使ってSQLQueryを使いやすくする
HibernateのSQLQueryとResultTransformerを組み合わせれば、SQLの結果をDTO(Not Entity)に詰められるのは以前から知っていました。しかし、よくよく調べてみると、このResultTransformerはEntityの結果を返すときにも有効利用できるみたいです。
たとえば・・・
session.createSQLQuery( "SELECT" + " {mtoo.*}" + ",{otmi.*}" + " FROM" + " ManyToOneOwner mtoo" + " INNER JOIN" + " OneToManyInverse otmi" + " ON" + " otmi.id = mtoo.oneToManyInverse_id" + " WHERE" + " mtoo.id <= :id") .addEntity("mtoo", ManyToOneOwner.class) .addJoin("otmi", "mtoo.oneToManyInverse") .setParameter("id", 3) .list();
このような処理を実行すると、ManyToOneOwnerとOneToManyInverseという2つのEntityのオブジェクト配列のListが返されます。addJoinを定義することによって、ルートとなるEntity(この場合はManyToOneOwner)にはINNER JOINで同時取得したEntityが既に入っている状態なので、この場合本当はManyToOneOwnerのListで返してもらいたいところなんですが・・・今まで、こういう場合Hibernateはどうしてもオブジェクト配列を返してしまうものだと思ってました。
しかし、以下のようにすれば・・・
private EntityManager em; @SuppressWarnings("unchecked") public void testTest() { Session session = Session.class.cast(em.getDelegate()); List<ManyToOneOwner> list = session.createSQLQuery( "SELECT" + " {mtoo.*}" + ",{otmi.*}" + " FROM" + " ManyToOneOwner mtoo" + " INNER JOIN" + " OneToManyInverse otmi" + " ON" + " otmi.id = mtoo.oneToManyInverse_id" + " WHERE" + " mtoo.id <= :id") .addEntity("mtoo", ManyToOneOwner.class) .addJoin("otmi", "mtoo.oneToManyInverse") .setParameter("id", 3) .setResultTransformer(new TestTransformer("mtoo")) .list(); for (ManyToOneOwner mtoo : list) { System.out.println("mtoo.name:" + mtoo.getName()); System.out.println("otmi.name:" + mtoo.getOneToManyInverse().getName()); System.out.println("sotmi.name:" + mtoo.getSubOneToManyInverse().getName()); System.out.println(); } } private class TestTransformer implements ResultTransformer { private static final long serialVersionUID = -5126022678602811037L; private String alias; TestTransformer(String alias) { this.alias = alias; } /** * @see org.hibernate.transform.ResultTransformer#transformList(java.util.List) */ public List transformList(List collection) { return collection; } /** * @see org.hibernate.transform.ResultTransformer#transformTuple(java.lang.Object[], java.lang.String[]) */ public Object transformTuple(Object[] tuple, String[] aliases) { for (int i = 0; i < aliases.length; i++) { if (alias.equals(aliases[i])) { return tuple[i]; } } return null; } }
これで成功しました。SQLQuery.setResultTransformerで、独自作成したTransformerを渡してやれば、オブジェクト配列からほしいEntityだけを選択して返すことができるんですね・・・もっと早く気付いておけば・・・
SELECT句に書く内容がどうしても純粋なSQLとは違ってしまうのが欠点ですが、しかしこの機能を使えば、HQLを使わなくともFETCH JOINを駆使したHQLと同等のことがSQLで記述出来るようになります。SQLQueryは名称パラメータもサポートしているので、機能的にはHQLに劣る部分はほとんど無くなりますね。
Hibernateの導入を吟味する際、開発者が最も嫌がるのがHQLだと思うのですが、この機能を知っておけば、Hibernate+SQLの組み合わせでも十分Entityを活用できるのではないかと思います。
ただ、Entityでない結果(DTOに詰める場合etc)を使うことが多い場合は、やはりHibernate以外のツールを使ったほうが無難だとも思います。あくまで、HibernateのメリットはEntityをフル活用するときだけに受け取れるものだと思いますので。