【问题标题】:Using case class to parse data containing enum in ScalikeJdbc and ScalaJson在 ScalikeJdbc 和 ScalaJson 中使用案例类解析包含枚举的数据
【发布时间】:2018-09-08 03:10:01
【问题描述】:

我需要从MySQL 数据库中的Scala 中读取某些枚举值(除了其他常规内容,包括一些可选的空 值)。我使用ScalikeJdbc 查询MySQL 并解析结果集。我在it's docs 中找不到解析枚举类型的任何提及(也许我需要更深入地挖掘)。

但我的目标应用程序需要有两种加载数据的方式:数据库和JSON 文件。因此,我还使用 ScalaJson (Play Framework) 来解析 Json 文件,以读取与 MySQL 数据库完全相同的数据。

我知道ScalikeJdbcScalaJson 都支持通过使用Scalacase 类es 来解析数据以实现自动转换


我的问题是:

  • ScalikeJdbc 是否支持解析枚举值 like ScalaJson?
  • 可以使用相同的case class 解析ScalikeJdbcScalaJson 中的数据,还是它们需要不同?我的case class 将包含自定义类型的参数,而这些参数本身就是case classes。这些case classes 接受除了枚举值之外的以下类型的参数(基本上,它们不包含完全相同类型的参数,但复杂程度将是相同的):
    • Int, String
    • Option[Int], Option[String]
    • Option[Seq[ (Int, String, Option[Int], Option[String]) ]]
  • 枚举可以通过添加额外的手动验证来完全取消,尽管最终结果可能不会像整洁那样。也就是说,总的来说,使用 Enums 是个好主意(特别是从数据库或JSON / XML 文件中读取数据时)或添加枚举的开销 em> 太多,不足以证明他们的好处?

【问题讨论】:

  • 虽然我在case classes 中取消了Enums,但我仍然需要为ScalikeJdbcScalaJson 使用相同的case classes;那些case classes 中的companion object 必须扩展SQLSyntaxSupport[T](根据ScalikeJdbc);当我在其中添加 Json reads[T]writes[T] 等方法时,会产生 不明确的引用错误
  • 虽然我已经描述了我是如何为ScalikeJdbcScalaJson 使用相同的case classes 实现的,但使用ScalikeJdbc 解析Enums(并将它们映射到定义的EnumScala 代码中)仍然是一个悬而未决的问题。

标签: scala playframework enums scalikejdbc


【解决方案1】:

正如对该问题的评论所述,我不再直接从MySQL 数据库或JSON 读取Enums。显然,这些字段现在在数据库中是简单的VARCHARs,我将验证逻辑移到了我的Scala 代码中。由于我单独执行验证,我用来保存从MySQLJSON 读取的数据的case classes 不再有任何Enum.Value 类型字段。

我会说将Enums 直接放在数据库中会更有意义;但由于缺乏简单的解决方案,我选择了这种解决方法。这仍然是一个悬而未决的问题,一旦找到解决方案,我将更新答案。


来到我问题的另一部分

ScalikeJdbc 中是否可以使用同一个 case 类来解析数据 和 ScalaJson 还是需要不同?

是的

我用companion objects 创建了case classes,我可以在ScalikeJdbcScalaJson 中将它们用于自动转换。我发布了一个完整的 case class 以及它的 companion object 服务于这个双重目的

注意:此代码示例来自一个框架,旨在将MySQL 表从一个位置移动到另一个位置。 [实际生产代码]

案例类:

case class Table(dbId: Option[Int] = None,
                 id: Option[Int] = None,
                 name: String,
                 shouldCopy: Option[Boolean] = None,
                 lastUpdated: Option[DateTime] = None
                )

伴侣对象:

object Table {

  private def mapResultToTable(rs: WrappedResultSet): Table = Table(
    dbId = rs.intOpt("db_id"),
    id = rs.intOpt("id"),
    name = rs.string("name"),
    shouldCopy = rs.booleanOpt("should_copy"),
    lastUpdated = rs.dateTimeOpt("last_updated").flatMap { zonedDateTime: ZonedDateTime =>
      Some(DateTime.parse(zonedDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)).plusMinutes(330))
    }
  )

  def getTables(db: Db)(implicit session: DBSession): Seq[Table] = {
    sql"""
       SELECT
         *
       FROM
         db_name.tables
       WHERE
         db_id = ${db.id} AND
         should_copy = b'1'
       ORDER BY
         name ASC
    """.
      map(mapResultToTable).
      list().
      apply()
  }

  // for Play-Json [Scala-Json]
  implicit val dateTimeJsReader = JodaReads.jodaDateReads("yyyyMMddHHmmss")
  implicit val dateTimeWriter = JodaWrites.jodaDateWrites("dd/MM/yyyy HH:mm:ss")

  implicit val reads = Json.reads[Table]
  implicit val writes = Json.writes[Table]
}

以下是对代码特定部分的解释:

  • def mapResultToTable(rs: WrappedResultSet)

    此方法读取数据库查询的result-set,并构建case classobject

  • def getTables(db: Db)

    此方法查询MySQL数据库正在使用ScalikeJdbc

  • implicit val dateTimeJsReader = JodaReads.jodaDateReads("yyyyMMddHHmmss")

  • implicit val dateTimeJsReader = JodaReads.jodaDateReads("yyyyMMddHHmmss")

    这些是Joda DateTime 读写转换器(由ScalaJson 使用)用于Joda DateTime 参数case class

  • implicit val reads = Json.reads[Table]

  • implicit val writes = Json.writes[Table]

    这些是给定case class读写转换器


最后,这是从Json文件中读取这个案例类数据的code-sn-p

val tableOpt: Option[Table] = try {
  val rawJsonString: String = Source.fromFile("path/to/file/fileName.json").mkString
  Some(Json.parse(rawJsonString).as[Table])
} catch {
  case ex: Throwable =>
    println(s"Exception while reading Json file: '$ex'")
    None
}

对像我这样的 noobs 的最后一条建议:除非您对 ScalaJson(和一般的 JSON 解析)感到满意,否则请避免使用 Json.parse(jsonString).asOpt[T] 方法(上面的 sn -p 使用.as[T] 方法)来克服像this 这样的细微错误。


EDIT-1

ScalikeJdbc 是否支持像 ScalaJson 那样解析 Enum 值?

获取Enum.Value 真的与ScalikeJdbc 无关:一旦拥有String,转换为Enum.Value 有多难?

这是一个小例子:

// Enum
object CloudProvider extends Enumeration {

  val AWS: Value = Value("aws")
  val MicrosoftAzure: Value = Value("microsoft-azure")
  val GoogleCloud: Value = Value("google-cloud")
}

// Case Class
case class SiteInfo(website: String, provider: CloudProvider.Value)

// Mapper method
def mapResult(rs: WrappedResultSet): SiteInfo = {
  val website: String = rs.string("website")
  val provider: CloudProvider.Value = CloudProvider.values.
    find(_.toString == rs.string("provider")).
    getOrElse(CloudProvider.AWS)

  SiteInfo(website, provider)
}

很难理解为什么当它就在我面前时我看不到答案:(mapResult方法)


..总的来说,使用枚举是个好主意吗..

是的,绝对的

拥有Enum 类型的全部意义在于限制变量可以取值的范围。除了Enum,我找不到更简单的方法来执行此类规则。

此外,一旦您掌握了您所使用的语言/库的足够专业知识(当时我对 ScalaScalikeJdbc 了解不多),绝对没有开销(从编码角度来看)使用Enum;事实上,它们使代码更加清晰。不用说,即使从性能角度来看,使用 Enums 也比使用 String 好得多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多