Java: Re: ダイナミックプロキシ
誰か書くかと思っていたのだけれど、私の巡回範囲で誰もやらなかったので自分で書いてみた。
ダイナミックプロキシを使って、簡易なO/R Mapperを作る。
interfaceを使って、インターフェース名がテーブル名、メソッド名がカラム名というルールにして、動的にSQLを作って実行して、ダイナミックプロキシでそのインターフェースを実装したオブジェクトを作成するというもの。
まずは、前半の実行部分。EMPLOYEEテーブルにNAMEカラムとSALARYカラムがあるという設定。書かなければいけないのは、interface Employeeだけ。
import java.lang.reflect.*; import java.sql.*; import java.util.*; interface Employee { String name(); int salary(); } public class ORMapping { public static void main (Connection conn) throws SQLException { Mapper<Employee> mapper = null; try{ mapper = Mapper.create(conn, Employee.class); Employee emp; while (null != (emp = mapper.fetch())){ System.out.println("name=" + emp.name() + ", salary=" + emp.salary()); } }finally{ if (mapper != null) mapper.close(); } } }
以下、ダイナミックプロキシを使った実装。ただし、動かして検証していないので、バグっている可能性あります。
class ResultProxy implements InvocationHandler { private final HashMap<String, Object> data; ResultProxy (HashMap<String, Object> data) { this.data = data;} public Object invoke (Object proxy, Method m, Object[] args) throws Throwable { return data.get(m.getName()); } } class Mapper<A> { static<A> Mapper<A> create (Connection conn, Class<A> intf) throws SQLException { return new Mapper<A>(conn, intf); } private Mapper (Connection conn, Class<A> intf) throws SQLException { this.intf = intf; tbl = intf.getName(); Method[] ms = intf.getDeclaredMethods(); cols = new String[ms.length]; for (int i=0; i<ms.length; ++i){ cols[i] = ms[i].getName(); } StringBuffer sql = new StringBuffer("SELECT ").append(cols[0]); for (int i=1; i<cols.length; ++i){ sql.append(",").append(cols[i]); } sql.append(" FROM ").append(tbl); ps = conn.prepareStatement(sql.toString()); rs = ps.executeQuery(); } private Class<A> intf; private String tbl; private String[] cols; private PreparedStatement ps; private ResultSet rs; @SuppressWarnings("unchecked") A fetch () throws SQLException { if (! rs.next()) return null; HashMap<String, Object> data = new HashMap<String, Object>(cols.length); for (int i=0; i<cols.length; ++i){ data.put(cols[i], rs.getObject(i+1)); } return (A) java.lang.reflect.Proxy.newProxyInstance (intf.getClassLoader(), new Class[] {intf}, new ResultProxy(data)); } void close () throws SQLException { if (ps != null){ ps.close(); ps = null; } } }