2008年1月17日木曜日

jPersist続き(問題発生)

  • あるTableの名称含んだTable名称があるとマズイ模様。例えば、先日の例でいうと「Member」というTableを使った。で、SingleTableInheritanceを試そうとして「SpecialMember」という特殊なMemberで試そうとしたらダメ。もぅめちゃはまった。Table名のマッピングでMemberの解決で「Member」「SpecialMember」の両方を拾ってしまう。基本的に「Table名=jcommontk.utils.StringUtils#camelCaseToUpperCaseUnderline(Model名+"s")」の法則で考えるから、Memberの解決を行っている箇所を修正してしまえばいいんだけど、そもそも俺の使い方がマズイのか?そーなんか?
  • 関連するテーブルで、直接save()とかDB操作しないClassはPOJOでも良さげ。ただ、そんな事は滅多に無いからやっぱ「jpersist.PersistentObject」を継承することになる。
  • ただし、直接操作が無いTableは「jpersist.PersistentObject」のSubClassである「jpersist.Entity」を継承するようにしといた方がいいのかも?
先日の例のModelを少し変えてSingleTableInheritanceを試みた。
hsqldb/sample.script
CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE MEMORY TABLE DEPARTMENTS(ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,NAME VARCHAR(100))
CREATE MEMORY TABLE MEMBERS(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(ID) ON DELETE CASCADE ON UPDATE CASCADE)
CREATE MEMORY TABLE SUBTABLES(ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,DEPARTMENT_ID INTEGER NOT NULL,NAME VARCHAR(100),SPECIAL_PROPERTY VARCHAR(100),CONSTRAINT SUBTABLES_FKEY_01 FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENTS(ID) ON DELETE CASCADE ON UPDATE CASCADE)
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 10
Model「Department」。PrimaryKeyの名称を「ID」にした。
package shin1o;

import java.util.Vector;

import jpersist.PersistentObject;

@SuppressWarnings("serial")
public class Department extends PersistentObject {
  Long id;
  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(id).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 getId() { return id; }
  public void setId(Long departmentId) { this.id = departmentId; }
  public String getName() { return name; }
  public void setName(String name) { this.name = name; }
}
Model「Member」。PrimaryKeyの名称を「ID」にして、SuperClassをjpersist.Entityに変更した。
package shin1o;

import jpersist.Entity;

@SuppressWarnings("serial")
public class Member extends Entity {
  Long id;
  String name;
  Long departmentId;

  public Member() { }
  public Member(String _name) { name = _name; }

  public String toString() {
    return new StringBuilder().append("{id:").append(id).append(
        ", departmentId:").append(departmentId).append(", name:")
        .append(name).append("}").toString();
  }

  public Long getId() { return id; }
  public void setId(Long memberId) { this.id = 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; }
}
今回追加したModel「SubTable」。最初は「SpecialMember」だったが、エントリ頭の問題で適当な名前にした。Memberを継承している。 @SingleTableInheritanceアノテーションをClassに適用している。PDFのマニュアルを見ると「jpersist.interfaces.SingleTableInheritance」を実装またはSingleTableInheritanceアノテーション云々、と書いてある。今回はXMLもアノテーションも極力使用せずにActiveRecordを、と思ったのでinterfaceの方でやろうとしたらそんなinterfaceは存在しない。ドキュメントの更新がソースに追随していないよぅだ。
package shin1o;

import jpersist.annotations.SingleTableInheritance;

@SuppressWarnings("serial")
@SingleTableInheritance
public class SubTable extends Member {
  String specialProperty;

  public SubTable() { }
  public SubTable(String _name, String _specialProperty) {
    name = _name;
    specialProperty = _specialProperty;
  }

  public String toString() {
    String s = super.toString();
    s = s.substring(0, s.length() - 1);
    return new StringBuilder(s).append(", specialProperty:").append(
        specialProperty).append("}").toString();
  }

  public String getSpecialProperty() { return specialProperty; }
  public void setSpecialProperty(String specialProperty) { this.specialProperty = specialProperty; }
}
TestCase。先日のTestCase以下のメソッドを追加。実際には、フィールド名称を簡単にしたりしているからそのあたりも先日のモノに対して修正が必要。 やけにSystem.out.println()していたり、直接JDBCを操作したりしているのは調査のため。
  @Test
  public void 継承を使う() throws Exception {
    System.out.println("継承を使う() start.");
    Department department = dbm.loadObject(Department.class,
        "where :name like '%2'");
    System.out.println("継承を使う() load end. and add start.");
    department.getMembers().add(new SubTable("member_2_3", "subMember"));
    System.out.println("継承を使う() add SubTable end.");
    department.getMembers().add(new Member("member_2_4"));
    System.out.println("継承を使う() add Member end. and save start.");
    System.out.println(department);
    department.save(dbm);
    System.out.println("継承を使う() save end. and reload start.");
    System.out.println(department);

    try {
      jdbcQuery("select * from subtables");
      department = dbm.loadObject(Department.class,
          "where :name like '%2'");
      Collection<Member> members = loadObjects(Member.class);
      Collection<SubTable> subTables = loadObjects(SubTable.class);
      System.out.println("継承を使う() reload end.");
      System.out.println(department);
      assertEquals(6, members.size());
      assertEquals(1, subTables.size());
      assertEquals(4, department.getMembers().size());
    } finally {
      System.out.println("継承を使う() end.");
    }
  }

  void jdbcQuery(String sql) throws JPersistException, SQLException {
    ResultSet resultSet = dbm.getDatabase().executeQuery(sql)
        .getResultSet();
    int columnSize = resultSet.getMetaData().getColumnCount();
    System.out.println("jdbcQuery - " + sql);
    while (resultSet.next()) {
      ArrayList<Object> values = new ArrayList<Object>(columnSize);
      for (int i = 1; i <= columnSize; i++) {
        values.add(resultSet.getObject(i));
      }
      System.out.println(StringUtils.toString(values.toArray()));
    }
  }
これで実行してみると、DBへの保存はDEPARTMENT,MEMBER,SUBTABLEすべてにおいて成功する(JDBC操作はDBへの保存を確認するため)。が、最後の方のDEPARTMENTの取得で「MEMBER」しか自動取得してくれない。jPersist内で発行されたSQLを表示させてみると、DEPARTMENTS,MEMBERSのselect文が発行されているのにSUBTABLESへはselectが発行されていない。という問題がまだ未解決。jPersistの中をちょっと追っかけてみる必要がありそぅだ。 ちなみに、SQLの表示はjpersist.DatabaseManager.setLogLevel()でLevel.FINEを指定すればおk。誰か、タスケテ!MLも見当たらないし、連絡の取り方がわからんのだ。

コメントを投稿