読者です 読者をやめる 読者になる 読者になる

Wicket + Guice + iBATIS でどうでしょう。

2009/06/19修正: Providerの使用方法を修正しました。id:nak2kさんご指摘ありがとうございます。

ちまたではWicket+Guice+Activeobjectsとかが流行ってますが、Activeobjectsがまだまだ安心して使えなさそうなのと、Hibernateは重量級だし...なんだ感だでiBATISで良いよね。完全なxmlレスを目指したいところはあるけど、DB周りはSQLぐらい外出しでも気にしない!むしろ細かいチューニング出来るから良いんじゃないかな。特にエンタープライズ向けには。

と自分を説得し、Wicket+Guice+iBATISをやってみました。

このへんを参考にしました。
http://codezine.jp/article/detail/1289?p=1
http://cwiki.apache.org/WICKET/wicket-guice-and-ibatis-example.html

iBATIS: 2.3.4.726
DB: PostgreSQL

DBベースのテーブルはこんな感じです。

 deptno |  dname  |  loc  
                                                  • -
0 | yamkazu | tokyo

まずはSqlMapConfig.xmlを作ります。ここではtestという名前のDBを使用するとことにします。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
	PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
	"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
	<transactionManager type="JDBC" commitRequired="false">
		<dataSource type="SIMPLE">
			<property name="JDBC.Driver" value="org.postgresql.Driver" />
			<property name="JDBC.ConnectionURL" value="jdbc:postgresql:test" />
			<property name="JDBC.Username" value="postgres" />
			<property name="JDBC.Password" value="postgres" />
		</dataSource>
	</transactionManager>
	<sqlMap resource="org/yamkazu/wicket/guice/dao/maps/Dept.xml" />
</sqlMapConfig>

sqlMapは各自のパッケージ構成にあわせて適当に。

iBATISの初期化するクラスを作る。

public class SqlMapClientImpl implements Provider<SqlMapClient> {
	private static SqlMapClient sqlMapClient;
	@Inject
	@Named("ibatis.configXml")
	String configXml;
	public SqlMapClient get() {
		try {
			Reader reader = Resources.getResourceAsReader(configXml);
			sqlMapClient = SqlMapClientBuilder.buildSqlMapClient(reader);
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException(
					"SqlMapClientP unable to create instance." + e);
		}
		return sqlMapClient;
	}
}

WebApplicationでは以下のように設定してあげる。

public class WicketApplication extends WebApplication {

	…

	@Override
	protected void init() {
		addComponentInstantiationListener(new GuiceComponentInjector(this,
				getModule()));
	}

	protected Module getModule() {
		return new MyModule();
	}

	static class MyModule extends AbstractModule {
		@Override
		protected void configure() {
			// ibatisの設定
			bind(SqlMapClient.class).toProvider(SqlMapClientImpl.class).in(
					Scopes.SINGLETON);
			bindConstant().annotatedWith(named("ibatis.configXml")).to(
					"SqlMapConfig.xml");
		}
	}
}

SqlMapConfig.xmlはクラスパスから探すので、上記で作成したSqlMapConfig.xmlをクラスパスで設定してあげます。

でDAOとDTOとDept.xmlを作ってあげます。

@ImplementedBy(DeptDaoImpl.class)
public interface DeptDao {
	List select() throws SQLException;
	void insert(Dept dept) throws SQLException;
	void update(Dept dept) throws SQLException;
	void delete(int deptno) throws SQLException;
}
@Singleton
public class DeptDaoImpl implements DeptDao {
	@Inject
	private SqlMapClient client;
	public List select() throws SQLException {
		return (List) client.queryForList("getDept");
	}
	public void insert(Dept dept) throws SQLException {
		client.insert("insertDept", dept);
	}
	public void update(Dept dept) throws SQLException {
		client.update("updateDept", dept);
	}
	public void delete(int deptno) throws SQLException {
		client.delete("deleteDept", new Integer(deptno));
	}
}
public class Dept implements Serializable {
	private int deptno;
	private String dname;
	private String location;
	public Dept() {
	}
	public Dept(int deptno, String dname, String location) {
		super();
		this.deptno = deptno;
		this.dname = dname;
		this.location = location;
	}
	public int getDeptno() {
		return deptno;
	}
	public void setDeptno(int deptno) {
		this.deptno = deptno;
	}
	public String getDname() {
		return dname;
	}
	public void setDname(String dname) {
		this.dname = dname;
	}
	public String getLocation() {
		return location;
	}
	public void setLocation(String location) {
		this.location = location;
	}
	@Override
	public String toString() {
		return "[deptno=" + deptno + ", dname=" + dname + ", location="
				+ location + "]";
	}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
	<!-- SELECT -->
	<select id="getDept" resultClass="org.yamkazu.wicket.guice.dto.Dept">
		SELECT DEPTNO, DNAME, LOC as location FROM DEPT
	</select>
	<!-- INSERT -->
	<insert id="insertDept" parameterClass="org.yamkazu.wicket.guice.dto.Dept">
		INSERT INTO DEPT(DEPTNO, DNAME, LOC)
		VALUES(#deptno#, #dname#, #location#)
	</insert>
	<!-- UPDATE -->
	<update id="updateDept" parameterClass="org.yamkazu.wicket.guice.dto.Dept">
		UPDATE DEPT SET DNAME = #dname#, LOC = #location#
		WHERE DEPTNO = #deptno#
	</update>	
	<!-- DELETE -->
	<delete id="deleteDept">
		DELETE FROM DEPT WHERE DEPTNO = #value#
	</delete>    
</sqlMap>

これで準備完了。あとはサービスクラスからこんな感じで呼んであげればOK。

@Singleton
public class ServiceImpl implements Service {
	@Inject
	private DeptDao dao;
	public List select() {
		List list = null;
		try {
			list = dao.select();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return list;
	}
	public void insert() {
		try {
			dao.insert(new Dept(1, "hoge", "kanagawa"));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public void update() {
		try {
			dao.update(new Dept(1, "hoge", "saitama"));
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	public void delete() {
		try {
			dao.delete(1);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
public class HomePage extends WebPage {
	@Inject
	private Service service;
	public HomePage(final PageParameters parameters) {
		final ListView listView = new ListView("messages", service.select()) {

			@Override
			protected void populateItem(ListItem item) {
				Dept dept = (Dept) item.getModelObject();
				item.add(new Label("message", dept.toString()));
			}
		};
		add(listView);
		…
	}
}