AnormでTypeDoesNotMatchを回避
最近、Playframework2/scalaで自宅用の家計簿なんかを作ってます。
DBアクセスにはPlay標準のAnormを使用していますが、その時困ったことです。
やりたかったのは実績の金額をある期間で分類ごとに合計するということで、分析関数のsum()を使用した列で値を取得していました。
SELECT distinct sum(amount) over (partiton by category_id) as total_amount FROM actual WHERE actual_date >= {startDate} AND actual_date <= {endDate}
パーサ
val simple = { get[Long]("total_amount") map { case totalAmount => (totalAmount) } }
コンパイルは通ったのですが、実行時に
TypeDoesNotMatch(Cannot convert 67300:class java.math.BigDecimal to Long
という例外が発生しました。
パーサにはget[Long]
を使用していたのですが、ならばということでget[java.math.BigDecimal]
に変えてみたのですが、今度は
TypeDoesNotMatch(Cannot convert 0:class java.lang.Integer to BigDecimal
となってしまいました。
どうやら、行ごとの値(この場合0や67300)によって 列の型が異なるようです。
うーんこれは仕様?JDBCの固有の特性?よくわかりません。
ちなみにDBMSはPostgreSQLです。
回避方法は、AnormのソースからLong型の列のパーサをコピペしてきてそこへBigDecimalのcaseを追加してやりました。
そしてこれを上記のパーサの前に書いておきます。これでIntでもLongでもBigDecimalでも無事get[Long]
で取得できるようになりました。
implicit def rowToLong: Column[Long] = Column.nonNull { (value, meta) => val MetaDataItem(qualified, nullable, clazz) = meta value match { case int: Int => Right(int: Long) case long: Long => Right(long) case bigDecimal: java.math.BigDecimal => Right(bigDecimal.longValue) // 追加 case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass + " to Long for column " + qualified)) } }
もっと良い方法ご存知のかたは教えて下さい。