2008年1月15日火曜日

jPersistを試してみる

JavaでActiveRecord、てのが気になったので試してみることにした。サポートされているDBはPDFのリファレンスを見ると次の通りだそぅだ。

Supported Databases Right now our focus is on adding support for as many databases as we can, and jPersist currently has complete, and fully tested, support for:
  • MySqlDB2
  • Oracle
  • Derby
  • HSQL
  • PostgreSQL
  • H2
HSQLDBを使う前提での作業。
  • hsqldb/sample.properties
    #HSQL Database Engine 1.8.0.7
    #Wed Jan 16 03:33:41 GMT+09:00 2008
    hsqldb.script_format=0
    runtime.gc_interval=0
    sql.enforce_strict_size=false
    hsqldb.cache_size_scale=8
    readonly=false
    hsqldb.nio_data_file=true
    hsqldb.cache_scale=14
    version=1.8.0
    hsqldb.default_table_type=memory
    hsqldb.cache_file_scale=1
    hsqldb.log_size=200
    modified=no
    hsqldb.cache_version=1.7.0
    hsqldb.original_version=1.8.0
    hsqldb.compatible_version=1.8.0
    
  • hsqldb/sample.script
    CREATE SCHEMA PUBLIC AUTHORIZATION DBA
    CREATE MEMORY TABLE DEPARTMENTS(DEPARTMENT_ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,NAME VARCHAR(100))
    CREATE MEMORY TABLE MEMBERS(MEMBER_ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,DEPARTMENT_ID INTEGER NOT NULL,NAME VARCHAR(100),CONSTRAINT MEMERS_FKEY_01 FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENTS(DEPARTMENT_ID) ON DELETE CASCADE ON UPDATE CASCADE)
    CREATE USER SA PASSWORD ""
    GRANT DBA TO SA
    SET WRITE_DELAY 10
    SET SCHEMA PUBLIC
    
    非常に横に長いが、整形するとHSQLDBが解析に失敗するのでこのままの状態で。
  • src/main/resources/databases.xml
    <databases>
      <database name="studyJPersist_hsqldb" poolSize="10"
        driver="org.hsqldb.jdbcDriver" url="jdbc:hsqldb:file:hsqldb/sample" />
    </databases>
    class出力フォルダの直下にあれば良い。eclipseならsrc/databases.xmlになるかな。
  • 次にModelをふたつ作成。DEPARTMENTに複数のMEMBERがぶら下がるという関係。まずはDEPARTMENT
    package shin1o;
    
    import java.util.Vector;
    import jpersist.PersistentObject;
    
    @SuppressWarnings("serial")
    public class Department extends PersistentObject {
      Long departmentId;
    
      String name;
    
      Vector<Member> members = new Vector<Member>();
    
      public Department() {}
    
      public Department(String _name) { name = _name; }
    
      public String toString() {
        return new StringBuilder().append("{id:").append(departmentId).append(
            ", name:").append(name).append(", members:").append(
            members.toString()).append("}}").toString();
      }
    
      // Associations
    
      public Vector<Member> getDbAssociation(Member c) {
        return members;
      }
    
      public void setDbAssociation(Member c, Vector<Member> _members) {
        members = _members;
      }
    
      public Vector<Member> getMembers() { return members; }
    
      public void setMembers(Vector<Member> members) { this.members = members; }
    
      public Long getDepartmentId() { return departmentId; }
    
      public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; }
    
      public String getName() { return name; }
    
      public void setName(String name) { this.name = name; }
    }
    
  • 次にぶら下がる方のMember。
    package shin1o;
    
    import jpersist.PersistentObject;
    
    @SuppressWarnings("serial")
    public class Member extends PersistentObject {
      Long memberId;
    
      String name;
    
      Long departmentId;
    
      public Member() { }
    
      public Member(String _name) { name = _name; }
    
      public String toString() {
        return new StringBuilder().append("{id:").append(memberId).append(
            ", departmentId:").append(departmentId).append(", name:")
            .append(name).append("}").toString();
      }
    
      public Long getMemberId() { return memberId; }
    
      public void setMemberId(Long memberId) { this.memberId = memberId; }
    
      public String getName() { return name; }
    
      public void setName(String _name) { this.name = _name; }
    
      public Long getDepartmentId() { return departmentId; }
    
      public void setDepartmentId(Long departmentId) { this.departmentId = departmentId; }
    }
    
  • TestClassを作成して、動作確認をしてみる。
    package shin1o;
    
    import static org.junit.Assert.assertEquals;
    
    import java.util.*;
    import jpersist.*;
    import org.junit.*;
    
    public class JPersistTest {
      static DatabaseManager dbm;
    
      @BeforeClass
      public static void setUp() throws JPersistException {
        DatabaseManager.setLogLevel(java.util.logging.Level.INFO);
        dbm = DatabaseManager
            .getXmlDefinedDatabaseManager("studyJPersist_hsqldb");
      }
    
      @AfterClass
      public static void tearDown() throws JPersistException {
        if (dbm != null && !dbm.isClosed()) {
          dbm.close();
        }
      }
    
      /**
       * {@link #before()} で作成したDepartmentのidを毎回保存しておく。
       */
      Long d0Id, d1Id, d2Id;
    
      /**
       * Testするたびに初期データを作り直す。
       * @throws Exception
       */
      @Before
      public void before() throws Exception {
        Department d0 = new Department("department0");
        d0.save(dbm);
        d0Id = d0.getDepartmentId(); // save後にIDを保存。
    
        Department d1 = new Department("department1");
        d1.getMembers().add(new Member("member_1_1"));
        d1.getMembers().add(new Member("member_1_2"));
        d1.getMembers().add(new Member("member_1_3"));
        d1.save(dbm);
        d1Id = d1.getDepartmentId();
    
        Department d2 = new Department("department2");
        d2.getMembers().add(new Member("member_2_1"));
        d2.getMembers().add(new Member("member_2_2"));
        d2.save(dbm);
        d2Id = d2.getDepartmentId();
      }
    
      @After
      public void after() throws Exception {
        // 全部消す
        Result<Department> departments = dbm.getDatabase().queryObject(
            Department.class);
        while (departments.hasNext()) {
          Department department = departments.next();
          dbm.getDatabase().deleteObject(department);
        }
      }
    
      @Test
      public void 一件load_ClassとSql条件() throws Exception {
        Department department = dbm.loadObject(Department.class,
            "where :departmentId = '" + d1Id + "'");
        assertEquals(d1Id, department.getDepartmentId());
        assertEquals(3, department.getMembers().size());
      }
    
      @Test
      public void 一件load_ClassとSql条件とパラメタ() throws Exception {
        Department department = dbm.loadObject(Department.class,
            "where :name like ?", "%1");
        assertEquals(d1Id, department.getDepartmentId());
      }
    
      @Test
      public void 一件load_Object() throws Exception {
        Department department = dbm.loadObject(new Department("%1"));
        assertEquals(d1Id, department.getDepartmentId());
      }
    
      @Test
      public void 全件取得() throws Exception {
        Collection<Department> departments = dbm.loadObjects(
            new ArrayList<Department>(), Department.class);
        assertEquals(3, departments.size());
        Collection<Member> members = dbm.loadObjects(new ArrayList<Member>(),
            Member.class);
        assertEquals(5, members.size());
      }
    
      @Test
      public void 複数件load_ClassとSql条件とパラメタ() throws Exception {
        Collection<Department> departments = dbm.loadObjects(
            new ArrayList<Department>(), Department.class,
            "where :name like ?", "dep%");
        assertEquals(3, departments.size());
      }
    
      @Test
      public void transactionManagerを使う() throws Exception {
        TransactionManager transaction = new TransactionManager(dbm) {
          Collection<Department> departments;
          final String sql = "where :name like 'dep%'";
    
          public void run() throws JPersistException { // 最初は3件。
            departments =
    
            dbm.loadObjects(new ArrayList<Department>(), Department.class,
                sql);
            assertEquals(3, departments.size());
    
            new Department("department_a").save(this);
            commit(); // 1件追加したから4件のはず。
            departments = loadObjects(Department.class, sql);
            assertEquals(4, departments.size());
    
            new Department("department_b").save(this); // さらに1件追加したから5件のはず。
            departments = loadObjects(Department.class, sql);
            assertEquals(5, departments.size());
    
            rollback(); // rollbackしたから4件のはず。
            departments = loadObjects(Department.class, sql);
            assertEquals(4, departments.size());
          }
        };
        transaction.executeTransaction();
      }
    
      @Test
      public void 一件delete() throws Exception {
        Department department = dbm.loadObject(Department.class,
            "where :departmentId = '" + d2Id + "'");
        dbm.deleteObject(department);
    
        Collection<Department> departments = loadObjects(Department.class);
        assertEquals(2, departments.size()); // delete cascadeでmemberも減っているはず。
        Collection<Member> members = loadObjects(Member.class);
        assertEquals(3, members.size());
      }
      
      <T> Collection<T> loadObjects(Class<T> c) throws JPersistException {
        return dbm.loadObjects(new ArrayList<T>(), c);
      }
    
      <T> Collection<T> loadObjects(Class<T> c, String sql)
          throws JPersistException {
        return dbm.loadObjects(new ArrayList<T>(), c, sql);
      }
    }
    
サンプルを元に単純な例を試すとこんなカンジに使うことになる。ModelがPOJOでないのが引っかかるけど、かなり簡単。Modelに[「get|set]Associations()」を追加するのがポイント。 ちなみに、注意点として「Modelにプリミティブな型を使わない」といぅこと!プリミティブな型はnullを表現できないため、自動的に関連先を取得する時のSQLの条件にプリミティブなフィールドが含まれていまい、正しく関連先が取得できません!俺ハマったよ! この状態で、SingleTableInheritanceを試そうとしたけど、PDFのドキュメントにある「jpersist.interface.SingleInheritance」が存在しなかったりで苦戦中。また後日チャレンジする。

コメントを投稿