无法将266:class java.math.BigDecimal转换为Long for column

时间:2013-06-14 02:00:56

标签: postgresql scala heroku playframework anorm

我正在使用Play 2.1.1,Postgress和Herkou,并且仅从我的生产数据库中获得奇怪的例外。

本地对H2,一切正常。然而,在生产中,相同的操作失败了:

2013-06-14T01:49:25.275717+00:00 app[web.1]: [←[37minfo←[0m] application - 2013-06-14T01:49:25.275Z - Updating cache with new peak 266
2013-06-14T01:49:48.310600+00:00 app[web.1]: [←[37minfo←[0m] application - Updating database with peak 266
2013-06-14T01:49:48.327490+00:00 app[web.1]: [←[31merror←[0m] application -
2013-06-14T01:49:48.327490+00:00 app[web.1]:
2013-06-14T01:49:48.327490+00:00 app[web.1]: ! @6eih9flgb - Internal server error, for (GET) [/updateDB] ->
2013-06-14T01:49:48.327490+00:00 app[web.1]:
2013-06-14T01:49:48.327490+00:00 app[web.1]: play.api.Application$$anon$1: Execution exception[[RuntimeException: TypeDoesNotMatch(Cannot convert 266:
class java.math.BigDecimal to Long for column ColumnName(peaks.price,Some(price)))]]
2013-06-14T01:49:48.327490+00:00 app[web.1]:    at play.api.Application$class.handleError(Application.scala:289) ~[play_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327490+00:00 app[web.1]:    at play.api.DefaultApplication.handleError(Application.scala:383) [play_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327490+00:00 app[web.1]:    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$12$$anonfun$apply$24.apply(PlayDefaultUp
streamHandler.scala:314) [play_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327490+00:00 app[web.1]:    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$12$$anonfun$apply$24.apply(PlayDefaultUp
streamHandler.scala:312) [play_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327490+00:00 app[web.1]:    at play.api.libs.concurrent.PlayPromise$$anonfun$extend1$1.apply(Promise.scala:113) [play_2.10-2.1.0.j
ar:2.1.0]
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at play.api.libs.concurrent.PlayPromise$$anonfun$extend1$1.apply(Promise.scala:113) [play_2.10-2.1.0.j
ar:2.1.0]
2013-06-14T01:49:48.327660+00:00 app[web.1]: java.lang.RuntimeException: TypeDoesNotMatch(Cannot convert 266:class java.math.BigDecimal to Long for co
lumn ColumnName(peaks.price,Some(price)))
2013-06-14T01:49:48.322664+00:00 heroku[router]: at=info method=GET path=/updateDB host=www.bitcoinpeak.org fwd="84.94.173.221" dyno=web.1 connect=2ms
 service=2019ms status=500 bytes=1941
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at scala.sys.package$.error(package.scala:27) ~[scala-library.jar:na]
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at anorm.Sql$.as(Anorm.scala:535) ~[anorm_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at anorm.Sql$class.executeInsert(Anorm.scala:474) ~[anorm_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at anorm.SimpleSql.executeInsert(Anorm.scala:370) ~[anorm_2.10-2.1.0.jar:2.1.0]
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at org.bitcoinpeak.Peak$$anonfun$addPeak$1.apply(Peak.scala:43) ~[bitcoin-peak_2.10-1.0-SNAPSHOT.jar:1
.0-SNAPSHOT]
2013-06-14T01:49:48.327660+00:00 app[web.1]:    at org.bitcoinpeak.Peak$$anonfun$addPeak$1.apply(Peak.scala:40) ~[bitcoin-peak_2.10-1.0-SNAPSHOT.jar:1
.0-SNAPSHOT]
2013-06-14T01:50:24.933750+00:00 app[web.1]: [←[37minfo←[0m] application - 2013-06-14T01:50:24.933Z - Updating cache with new peak 266
2013-06-14T01:51:24.796588+00:00 app[web.1]: [←[37minfo←[0m] application - 2013-06-14T01:51:24.796Z - Updating cache with new peak 266
2013-06-14T01:51:40.719105+00:00 heroku[router]: at=info method=HEAD path=/ host=www.bitcoinpeak.org fwd="74.86.158.106" dyno=web.1 connect=1ms servic
e=7ms status=404 bytes=1900
2013-06-14T01:52:25.047381+00:00 app[web.1]: [←[37minfo←[0m] application - 2013-06-14T01:52:25.047Z - Updating cache with new peak 266
2013-06-14T01:52:33.646553+00:00 heroku[router]: at=info method=GET path=/ host=www.bitcoinpeak.org fwd="74.86.158.107" dyno=web.1 connect=2ms service
=21ms status=200 bytes=1892
2013-06-14T01:53:25.420489+00:00 app[web.1]: [←[37minfo←[0m] application - 2013-06-14T01:53:25.420Z - Updating cache with new peak 266  

这就是生产表的样子:

> \d+ Peaks
                                         Table "public.peaks"
 Column |            Type             |       Modifiers        | Storage | Stats target | Description
--------+-----------------------------+------------------------+---------+--------------+-------------
 price  | numeric(20,0)               | not null               | main    |              |
 time   | timestamp without time zone | not null default now() | plain   |              |

为什么在本地工作的相同代码在生产中失败? 是SQL的味道吗?我该如何解决?

奇怪的是,我正在尝试执行的事务(插入一行)确实有效 - 添加了行。那么异常来自哪里?

整个项目is one github,如果有人需要更多上下文。

更新:案例类:

import java.math.BigDecimal
import org.joda.time.DateTime

case class Peak(
  time: DateTime,
  price: BigDecimal
)

3 个答案:

答案 0 :(得分:1)

使用不同的数据库系统时,我看到了类似的问题。因此,我建议您检查数据库版本,配置,表的DDL以及用于子差异的确切JDBC驱动程序,这使得列在一种情况下显示为BigDecimal而在另一种情况下显示为Long。

您也可以查看这张票:http://play.lighthouseapp.com/projects/82401/tickets/243-weird-typedoesnotmatch-exception-in-rc-3-and-final

列的排序和某些版本的游戏似乎存在问题。

答案 1 :(得分:0)

对于迟到的回答感到抱歉,但我会回复,以防其他人偶然发现此问题并搜索SO。

在您的情况下,JDBC驱动程序返回的数据类型在使用H2驱动程序时返回Long数据类型,而使用Postgres驱动程序时返回BigDecimal数据类型。此行为可能取决于驱动程序实现以及数据库如何决定使用JDBC将其数据库数据类型转换为Java类型。在H2情况下,它使用Long并且你很好。对于Postgres,它决定返回BigDecimal

虽然this link描述的根本原因不同,但存在类似的问题。有一个提供的解决方案可以使用Column[BigInt]隐式转换为Column[Long],但我们会将Column[BigDecimal]转换为Column[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 bd:BigDecimal => Right(bd.longValue())  //Handle BigDecimal correctly
      case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass + " to Long for column " + qualified))
    }
}

这应该转换为Long类型BigDecimalLongint。这几乎与Anorm Column类中的原始Scala代码完全相同,只是添加了BigDecimal处理!

this (refused) pull request中还有一些更进化的隐式转换。值得注意的是,这些更改被拒绝是因为这些额外的隐式转换的行为是不确定的,因为修复程序取决于使用的JDBC驱动程序以及可能的版本。最重要的是,这是修改数据的魔术行为。因此,不幸的是,用户应该是负责这些更改的人。小心应用此修复程序,并确保它与所有环境和数据良好地交互。

答案 2 :(得分:0)

在app / helpers文件夹中创建AnormExtension助手。称之为AnormExtension.scala。代码如下。此外,将BigDecimal转换为Double而不是Long也可能更好。

仅供参考 - 注意我没有尝试转换为Scala BigDecimal。 Scala BigDecimal是一个谜。我会不管它。

object AnormExtension {

 implicit def rowToDouble: Column[Double] = Column.nonNull { (value, meta) =>
   val MetaDataItem(qualified, nullable, clazz) = meta
     value match {
       case d: java.math.BigDecimal => Right(d.doubleValue())
       case _ => Left(TypeDoesNotMatch("Cannot convert " + value + ":" + value.asInstanceOf[AnyRef].getClass + " to Double for column " + qualified))
     }
 }
}