JOTMのセットアップ手順(JavaSE)

JOTMを使用してJTAをJavaSEで使用する手順です。DBはApache Derbyを使用して、JDBCでInsertを行うところまでを説明します。なお、JTAApache Derbyについての説明はしません。

Apache Derbyのダウンロード、セットアップ、データベースとテーブルの作成

Apache Derbyからファイルをダウンロードして、適当なフォルダに展開します。

%DERBY%/bin/startNetworkServer.batを実行して、Apache Derbyを起動します。

%DERBY%/bin/ij.batを実行します。connectコマンドを使用して、データベースを作成 & 接続します。

connect 'jdbc:derby://localhost:1527/foo;create=true';

create table文を実行して、テーブルを作成します。

create table foo (
    id integer primary key,
    name varchar(100) not null
)

JOTMのダウンロード、セットアップ

JOTMからファイルをダウンロードして、適当なフォルダに展開します。

jarファイルがたくさんありますが、JTAを使用するためには次のjarファイルをクラスパスに追加します。

  • %JOTM%/lib/derbyclient.jar
  • %JOTM%/lib/jotm-client.jar
  • %JOTM%/lib/log4j.jar
  • %JOTM%/lib/ow2-jta-1.1-spec.jar
  • %JOTM%/lib/xapool.jar

また、次のpropertiesファイルをクラスパスに追加します(ソースフォルダへの追加で良いかと)。

  • %JOTM%/conf/carol.properties
  • %JOTM%/conf/jotm.properties
  • %JOTM%/conf/log4j.properties

コーディング

package foo;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.XAConnection;
import javax.transaction.UserTransaction;

import org.enhydra.jdbc.standard.StandardXADataSource;
import org.objectweb.jotm.Jotm;
import org.objectweb.transaction.jta.TMService;

public class Foo {

    private static final String JDBC_URL = "jdbc:derby://localhost:1527/foo";

    private static final String JDBC_DRIVER = "org.apache.derby.jdbc.ClientDriver";

    private static final String JDBC_USER = "APP";

    private static final String JDBC_PASSWORD = "APP";

    private static final String USER_TRANSACTION_JNDI_NAME = "UserTransaction";

    private static TMService jotm;

    private static StandardXADataSource ds;

    public static void main(String[] args) throws Exception {
        System.setProperty("java.naming.factory.initial", "org.ow2.carol.jndi.spi.MultiOrbInitialContextFactory");
        System.setProperty("java.naming.provider.url", "rmi://localhost:1099");

        try {
            Foo.init();

            Context ctx = new InitialContext();
            UserTransaction utx = (UserTransaction) ctx.lookup(Foo.USER_TRANSACTION_JNDI_NAME);

            try {
                utx.begin();

                Foo.insert(1, "one");
                Foo.insert(2, "two");

                utx.rollback();
            } catch (Exception e) {
                utx.rollback();

                e.printStackTrace();
            }
        } finally {
            Foo.stop();

            System.exit(0);
        }
    }

    private static void init() throws NamingException, SQLException {
        Foo.jotm = new Jotm(true, false);

        InitialContext ctx = new InitialContext();
        ctx.rebind(Foo.USER_TRANSACTION_JNDI_NAME, Foo.jotm.getUserTransaction());

        Foo.ds = new StandardXADataSource();
        Foo.ds.setDriverName(Foo.JDBC_DRIVER);
        Foo.ds.setUrl(Foo.JDBC_URL);
        Foo.ds.setTransactionManager(Foo.jotm.getTransactionManager());
    }

    private static Connection getConnection() throws SQLException {
        XAConnection con = Foo.ds.getXAConnection(Foo.JDBC_USER, Foo.JDBC_PASSWORD);

        return con.getConnection();
    }

    private static void insert(int id, String name) throws SQLException {
        Connection con = Foo.getConnection();
        try {
            PreparedStatement ps = con.prepareStatement("insert into foo values (?, ?)");
            try {
                ps.setInt(1, id);
                ps.setString(2, name);

                ps.executeUpdate();
            } finally {
                ps.close();
            }
        } finally {
            con.close();
        }
    }

    private static void stop() throws NamingException {
        InitialContext ctx = new InitialContext();
        ctx.unbind(Foo.USER_TRANSACTION_JNDI_NAME);
    }

}

ポイントは次の通りです。

  • システム・プロパティに、"java.naming.factory.initial"、"java.naming.provider.url"が必要です。RMIレジストリを使用するため。
  • 最後はSystem.exit()で終了する必要があります。そうしないと、main()が終了してもプロセスが終了しません。
  • utx.begin()した後にinsert()を2回呼び、その後にutx.rollback()しています。insert()の中でConnectionの開閉を行っていますが、utx.rollback()によりちゃんとロールバックされます。