Doma で DataSource を JNDI 経由で取得する方法
Seasar2 とかを使わずに JavaEE コンテナから JNDI 経由で DataSource を取得する方法のメモ。
ソースをまったく書き換えることなく、UT、ローカル開発サーバ、本番環境に対応できるので便利。Glassfish v3.0.1 上の JAX-RS + CDI + Doma なアプリ環境で稼動確認してます。
Doma 設定クラス
package sample; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.seasar.doma.jdbc.DomaAbstractConfig; import org.seasar.doma.jdbc.JdbcLogger; import org.seasar.doma.jdbc.dialect.Dialect; import org.seasar.doma.jdbc.dialect.OracleDialect; public class DaoConfig extends DomaAbstractConfig { public final static String JNDI_DATASOURCE = "jdbc/sample"; protected static DataSource dataSource = createDataSource(); private static JdbcLogger jdbcLogger = new CommonsJdbcLogger(); // 使用するデータベースに合わせて変える private static Dialect dialect = new OracleDialect(); @Override public DataSource getDataSource() { return dataSource; } @Override public Dialect getDialect() { return dialect; } @Override public JdbcLogger getJdbcLogger() { return jdbcLogger; } protected static DataSource createDataSource() { try { return (DataSource) InitialContext.doLookup(JNDI_DATASOURCE); } catch (NamingException e) { throw new RuntimeException(e); } } }
JUnit4.7 用の TestRunnner
ついでに、JUnit4.7 用の TestRunnner の雛形。テスト開始前にトランザクションを開始して、終了時にロールバックします。
package sample; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import org.junit.internal.runners.statements.InvokeMethod; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; import org.seasar.doma.jdbc.JdbcLogger; import org.seasar.doma.jdbc.SimpleDataSource; import org.seasar.doma.jdbc.tx.LocalTransaction; import org.seasar.doma.jdbc.tx.LocalTransactionalDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.CommonsJdbcLogger; import sample.DaoConfig; public class DaoTestRunner extends BlockJUnit4ClassRunner { protected static LocalTransactionalDataSource dataSource; protected static JdbcLogger defaultJdbcLogger = new CommonsJdbcLogger(); private static final Logger logger = LoggerFactory.getLogger(DaoTestRunner.class); private static boolean initialized = false; public DaoTestRunner(Class<?> klass) throws InitializationError { super(klass); if (!initialized) { init(); } } protected void init() { try { dataSource = createDataSource(); // Create initial context System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory"); System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming"); InitialContext ic = new InitialContext(); ic.createSubcontext("jdbc"); /* JNDI 名に合わせて Subcontext 作成。Tomcat だと以下のような感じ? ic.createSubcontext("java:"); ic.createSubcontext("java:/comp"); ic.createSubcontext("java:/comp/env"); ic.createSubcontext("java:/comp/env/jdbc"); */ ic.bind(DaoConfig.JNDI_DATASOURCE, dataSource); } catch (NamingException e) { logger.error(e.getMessage(), e); throw new RuntimeException(e); } } // データベース接続設定 protected LocalTransactionalDataSource createDataSource() { SimpleDataSource dataSource = new SimpleDataSource(); dataSource.setUrl("xxx"); dataSource.setUser("xxx"); dataSource.setPassword("xxx"); return new LocalTransactionalDataSource(dataSource); } public LocalTransaction getLocalTransaction() { return dataSource.getLocalTransaction(defaultJdbcLogger); } @Override protected Statement methodInvoker(FrameworkMethod method, Object test) { return new DatabaseTestInvokeMethod(method, test); } private class DatabaseTestInvokeMethod extends InvokeMethod { private FrameworkMethod testMethod; private Object target; public DatabaseTestInvokeMethod(FrameworkMethod testMethod, Object target) { super(testMethod, target); this.testMethod = testMethod; this.target = target; } @Override public void evaluate() throws Throwable { LocalTransaction tx = getLocalTransaction(); try { logger.info(String.format("%s#%s の初期化処理を実行します", target.getClass(), testMethod.getName())); tx.begin(); logger.info("トランザクションを開始しました"); super.evaluate(); } catch (Exception e) { e.printStackTrace(); } finally { logger.info(String.format("%s#%s の終了処理を実行します", target.getClass(), testMethod.getName())); tx.rollback(); logger.info("ロールバックしました"); } } } }
ずっと Seasar2 使ってたので、InitialContext の使い方を度忘れして、2時間くらい浪費してしまいました。