abhrshのSeasarにっき

 | 

2004-07-18

[]ちょっと不具合

S2Unitで使うExcelをOOoのCalcで作っているせいかもしれないけど、おかしなエラーが出ることがある。

org.seasar.extension.jdbc.ColumnNotFoundRuntimeException: [ESSR0068]テーブル(EMP)のカラム()が見つかりません

どうやら、なぜか空の列が読み込まれてしまっているらしい。

そこで間違えて読み込まないように、org.seasar.extension.dataset.impl.XlsReaderクラスのsetupColumnsメソッドを修正してみた。

修正前

private void setupColumns(DataTable table, HSSFRow row) {
  for (int i = 0;; ++i) {
    HSSFCell nameCell = row.getCell((short) i);
    if (nameCell == null) {
      break;
    }
    table.addColumn(nameCell.getStringCellValue().trim());
  }
}

修正後

private void setupColumns(DataTable table, HSSFRow row) {
  for (int i = 0;; ++i) {
    HSSFCell nameCell = row.getCell((short) i);
    if (nameCell == null) {
      break;
    }
    String nameCellValue = nameCell.getStringCellValue().trim();
    if (nameCellValue.length() == 0) {
      break;
    }
    table.addColumn(nameCellValue);
  }
}

あと、文字型で扱いたい列に数値を入れておくとBigDecimalで取ってきてしまうのでDataSetの比較で失敗してしまう。これもCalcだからなのかな?

これを直すために、org.seasar.extension.dataset.types.ObjectTypeクラスのdoEqualsメソッドを修正した。

修正前

protected boolean doEquals(Object arg1, Object arg2) {
  return arg1.equals(arg2);
}

修正後

protected boolean doEquals(Object arg1, Object arg2) {
  return convert(arg1, null).equals(arg2);
}

これで、とりあえず不具合は解消。

[][]Daoで取得したBeanを検証する

S2Daoを使ってDaoの実装がすごく楽になったけれど、検索メソッドのテストを書くのはちょっと大変だよね。N:1マッピングを使ったときなんか特に、すごいコード量になることもある。

そこで、S2Dao用にBean検証クラスを作ってテストも楽にしよう。まずは検証用クラス

public final class BeanAssert {
  private BeanAssert() {
  }

  public static void assertBeanEquals(String message, DataSet expected, Object actual) {
  Assert.assertNotNull(expected);
  Assert.assertNotNull(actual);

  Class beanClass;
  if (actual instanceof List) {
    List actualList = (List) actual;
    Assert.assertFalse(actualList.isEmpty());
    beanClass = actualList.get(0).getClass();
  } else {
    beanClass = actual.getClass();
  }

  BeanReader actualBeans = new BeanReader(beanClass, actual);
  Assert.assertEquals(message, expected, actualBeans.read());
  }

  public static void assertBeanEquals(DataSet expected, Object actual) {
  assertBeanEquals(null, expected, actual);
  }
}

使うときに気をつけるのは、expectedにはExcelから読み込んだDataSetを利用することと、actualにはDaoの検索メソッドの戻り値をそのまま渡すこと。

actualはBeanとListのどちらでも良いが、Listの場合は空であってはならない。これはBeanのクラスを特定するため。

BeanReaderはBeanからDataSetを読み込むクラスで、BeanがS2Daoの定義に従っていれば、N:1マッピングされたBeanまで参照したDataSetを作成する。(実装は後述)

例えばS2DaoのサンプルにあるEmployeeとDepartmentの場合、

EmployeeDao.java

interface EmployeeDao {
  class BEAN = Employee.class;
  
  List getEmployeeByName(String ename);
  String getEmployeeByName_ARGS = "ename";
}
EmployeeDao_getEmployeeByName.sql

SELECT EMP.empno, EMP.ename, EMP.deptno, department.dname AS dname_0
FROM EMP LEFT OUTER JOIN DEPT department ON EMP.deptno = department.deptno
WHERE EMP.ename = /*ename*/'aaa'

このDaoメソッドのテストは、

public void testGetEmployeeByNameTx() throws Exception {
  readXlsAllReplaceDb("getEmployeeByName_prepare.xls");

  List actual = employeeDao.getEmployeeByName("aaa");

  assertBeanEquals(readXls("getEmployeeByName_result.xls"), actual);
}

初期状態と想定結果はExcelで用意して

getEmployeeByName_prepare.xls
DEPTシート
11, bbb
12, ccc
13, ddd
EMPシート
empno, ename, deptno, dname
101, aaa, 11
102, aba, 12
103, aaa, 13
104, aab, 11

getEmployeeByName_result.xls
EMPシート
empno, ename, deptno, dname, dname_0
101, aaa, 11, bbb
103, aaa, 13, ddd

以上。テストが簡単になりました。

最後にBeanReaderクラスの実装。

public class BeanReader implements DataReader {
  private DataTable table_;
  private BeanMetaData beanMetaData_;

  public BeanReader(Class beanClass, Object source) {
    beanMetaData_ = BeanMetaDataFactory.getBeanMetaData(beanClass);
    table_ = new DataTableImpl(beanMetaData_.getTableName());
    setupColumns();
    setupRows(source);
  }

  private void setupColumns() {
    for (int i = 0; i < beanMetaData_.getPropertyTypeSize(); ++i) {
      PropertyType pt = beanMetaData_.getPropertyType(i);
      Class propertyType = pt.getPropertyDesc().getPropertyType();
      table_.addColumn(pt.getColumnName(), ColumnTypes.getColumnType(propertyType));
    }
    for (int i = 0; i < beanMetaData_.getRelationPropertyTypeSize(); ++i) {
      RelationPropertyType rpt = beanMetaData_.getRelationPropertyType(i);
      for (int j = 0; j < rpt.getBeanMetaData().getPropertyTypeSize(); j++) {
        PropertyType pt = rpt.getBeanMetaData().getPropertyType(j);
        if (!rpt.isYourKey(pt.getColumnName())) {
          String columnName = pt.getColumnName() + "_" + rpt.getRelationNo();
          Class propertyType = pt.getPropertyDesc().getPropertyType();
          table_.addColumn(columnName, ColumnTypes.getColumnType(propertyType));
        }
      }
    }
  }

  private void setupRows(Object source) {
    if (source instanceof List) {
      List list = (List) source;
      for (int i = 0; i < list.size(); i++) {
        setupRow(list.get(i));
      }
    } else {
      setupRow(source);
    }
  }

  private void setupRow(Object source) {
    DataRow row = table_.addRow();
    for (int i = 0; i < beanMetaData_.getPropertyTypeSize(); ++i) {
      PropertyType pt = beanMetaData_.getPropertyType(i);
      Object value = pt.getPropertyDesc().getValue(source);
      row.setValue(pt.getColumnName(), convertValue(value));
    }
    for (int i = 0; i < beanMetaData_.getRelationPropertyTypeSize(); ++i) {
      RelationPropertyType rpt = beanMetaData_.getRelationPropertyType(i);
      Object relationBean = rpt.getPropertyDesc().getValue(source);
      for (int j = 0; j < rpt.getBeanMetaData().getPropertyTypeSize(); j++) {
        PropertyType pt = rpt.getBeanMetaData().getPropertyType(j);
        if (!rpt.isYourKey(pt.getColumnName())) {
          String columnName = pt.getColumnName() + "_" + rpt.getRelationNo();
          Object value = pt.getPropertyDesc().getValue(relationBean);
          row.setValue(columnName, convertValue(value));
        }
      }
    }
  }

  private Object convertValue(Object value) {
    if (value == null) {
      return null;
    }
    ColumnType columnType = ColumnTypes.getColumnType(value.getClass());
    return columnType.convert(value, null);
  }

  public DataSet read() {
    DataSet dataSet = new DataSetImpl();
    dataSet.addTable(table_);
    return dataSet;
  }
}

これで完成。

higayasuohigayasuo2004/07/19 20:52pt.getColumnName() + "_" + rpt.getRelationNo()の部分は直しておきました。

abhrshabhrsh2004/07/19 22:08"_"を入れるとequals()で失敗してしまうんです。Excelで"_"を入れていても比較先のカラム名を"_"抜きで探しているからだと思います。

JasonJason2012/10/29 03:40Thanks for cnortibuting. It's helped me understand the issues.

トラックバック - http://seasarproject.g.hatena.ne.jp/abhrsh/20040718
 |