かとじゅんの技術日誌

技術の話をするところ

S2JDBCをS2非依存にしてみた

みなさま、こんばんわ。

DIコンテナの世界で使いやすいORM

OSGi上で使い勝手のよさそうなORMを探しているんですけど、いいものがないですね。
ORMといえばS2JDBCなんだけど。SQLがメソッドチェーンでかけてIDEでリファクタリングできるのはあまりにもメリットが大きいと。個人的に思っている。
OSGi上で動くフレームワークを実装しようとしていて、そこではまずDIコンテナ非依存な世界なんで、S2は使えないということで非常にクマった。
ならば、S2JDBCのソースをS2非依存にしてみようかしらと少し逝った発想でソースコードをみながら改造改造w

S2JDBCからS2分離手術

Seasar2本体とS2-tigerからJdbcMangaer関連のソースを抜き出して、以下のような作業を実施。1日ぐらいでできた。

  • S2Containerインターフェイスを適当な汎用インターフェイスに置き換える
  • DataSourceFactory関連と、JTA周りは面倒なんで一緒にソースを持ってくる
  • utilパッケージも依存度が高いので持ってくる
  • logger周りはslf4jに超適当に切り替える
  • jdbc.diconなどに記述されている初期化処理とオブジェクトグラフの構築を手動で行う
  • トランザクション管理のインターセプタ用のAOPエンジンにはJiemamyプロジェクトのFactory Enhancerを利用した。

実際のソースコード

DIの恩恵に授かれないので初期化処理がかなり大変でしたw 
まぁ、でもこれでDIコンテナに依存しなくなったので、GuiceやSpring用のアダプタもかけるようになるね。時間があったら作ってみよう。
で、断っておきますが、普通にS2JDBCを使いたい人はオリジナルを使った方がよいです。
とりあえず、S2から分離しただけなので、もう少しコードをきれいにしていく必要があるな。ちなみに、ソースコードはオリジナルから派生物としてApacheライセンスで公開します。

初期化処理部分
public class Sample {
	public static void main(String[] args) {

		// 汎用的なコンテナ(今回のものはインターフェイスにインスタンスを関連づける単純なもの)
		ComponentContainerImpl componentContainer = new ComponentContainerImpl();

		// トランザクションマネージャの初期化
		TransactionManagerImpl transactionManager = new TransactionManagerImpl();
		UserTransactionImpl userTransaction = new UserTransactionImpl(
				transactionManager);
		JTATransactionManagerAdapter jtaTransactionManagerAdapter = new JTATransactionManagerAdapter(
				userTransaction, transactionManager);
		TransactionSynchronizationRegistryImpl syncRegistry = new TransactionSynchronizationRegistryImpl();
		syncRegistry.setTransactionManager(transactionManager);

		// データソースファクトリの初期化
		DataSourceFactoryImpl dataSourceFactory = new DataSourceFactoryImpl(
				componentContainer);
		XADataSourceImpl xaDataSource = new XADataSourceImpl();
		xaDataSource.setDriverClassName("org.postgresql.Driver");
		xaDataSource
				.setURL("jdbc:postgresql://localhost:5432/sisioh?schema=sisioh");
		xaDataSource.setUser("postgres");
		xaDataSource.setPassword(null);
		componentContainer.registerComponent(XADataSource.class, xaDataSource);

		// コネクションプールの初期化
		ConnectionPoolImpl connectionPool = new ConnectionPoolImpl();
		connectionPool.setXADataSource(xaDataSource);
		connectionPool.setTransactionManager(transactionManager);
		connectionPool.setTimeout(600);
		connectionPool.setMaxPoolSize(10);
		connectionPool.setAllowLocalTx(true);
		componentContainer.registerComponent(ConnectionPool.class,
				connectionPool);

		// データソースの初期化
		DataSourceImpl dataSource = new DataSourceImpl(connectionPool);
		componentContainer.registerComponent(DataSource.class, dataSource);

		// トランザクション用インターセプタの初期化
		RequiredInterceptor ri = new RequiredInterceptor();
		ri.setTransactionControl(jtaTransactionManagerAdapter);

		// エンティティメタファクトリの初期化
		EntityMetaFactoryImpl entityMetaFactory = new EntityMetaFactoryImpl();
		PersistenceConvention persistenceConvention = new PersistenceConventionImpl();
		entityMetaFactory.setPersistenceConvention(persistenceConvention);
		PropertyMetaFactoryImpl propertyMetaFactory = new PropertyMetaFactoryImpl(
				componentContainer);
		ColumnMetaFactoryImpl columnMetaFactory = new ColumnMetaFactoryImpl();
		columnMetaFactory.setPersistenceConvention(persistenceConvention);
		propertyMetaFactory.setColumnMetaFactory(columnMetaFactory);
		propertyMetaFactory.setPersistenceConvention(persistenceConvention);
		entityMetaFactory.setPropertyMetaFactory(propertyMetaFactory);
		TableMetaFactoryImpl tableMetaFactory = new TableMetaFactoryImpl();
		tableMetaFactory.setPersistenceConvention(persistenceConvention);
		entityMetaFactory.setTableMetaFactory(tableMetaFactory);

		// ダイアレクトの初期化
		Postgre81Dialect postgre81Dialect = new Postgre81Dialect();

		// JdbcManagerの初期化
		JdbcManagerImpl jdbcManager = new JdbcManagerImpl();
		jdbcManager.setSyncRegistry(syncRegistry);
		jdbcManager.setPersistenceConvention(persistenceConvention);
		jdbcManager.setEntityMetaFactory(entityMetaFactory);
		jdbcManager.setDataSource(dataSource);
		jdbcManager.setDataSourceFactory(dataSourceFactory);
		jdbcManager.setDialect(postgre81Dialect);
		componentContainer.registerComponent(JdbcManager.class, jdbcManager);

		// JdbcSampleにトランザクション管理用のAOPをかける
		FactoryEnhancer<JdbcSampleFactory> factoryEnhancer = new FactoryEnhancer<JdbcSampleFactory>(
				JdbcSampleFactory.class, // ファクトリのインターフェース
				JdbcSampleFactoryImpl.class, // 実装クラス
				new Enhance(new Pointcut(), ri));
		try {
			JdbcSampleFactory jdbcSampleFactory = factoryEnhancer.getEnhanced()
					.newInstance();
			JdbcSample jdbcSample = jdbcSampleFactory
					.newJdbcSample(componentContainer);
			// JdbcMangerを呼び出す
			jdbcSample.process();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (EnhanceException e) {
			e.printStackTrace();
		}

	}
}
JdbcManagerを使っているロジック部分

processメソッドは、Factory Enhancerでトランザクション管理のインターセプタがかかっています。

public class JdbcSample {

	private ComponentContainer componentContainer;

	public JdbcSample(ComponentContainer componentContainer) {
		this.componentContainer = componentContainer;
	}

	public void process() {
		JdbcManager jdbcManager = componentContainer
				.getComponent(JdbcManager.class);
		long count = jdbcManager.getCountBySql("SELECT * FROM USER_ACCOUNT");
		System.out.println("count = (" + count + ")");
	}
}
ログ

デバッグログが超適当なんで、まともにでてないけど。うごいてます。

22:35:18.298 [main] DEBUG o.s.extension.jta.TransactionImpl - DSSR0003
22:35:18.313 [main] DEBUG o.s.e.jdbc.query.SqlSelectImpl - select count(*) from ( SELECT * FROM USER_ACCOUNT ) COUNT_
22:35:18.503 [main] DEBUG o.s.e.dbcp.impl.ConnectionPoolImpl - DSSR0006
22:35:18.506 [main] DEBUG o.s.e.dbcp.impl.ConnectionPoolImpl - DSSR0007
count = (0)
22:35:18.561 [main] DEBUG o.s.extension.jta.TransactionImpl - DSSR0004
22:35:18.561 [main] DEBUG o.s.e.d.impl.ConnectionWrapperImpl - DSSR0002