【问题标题】:How to use UUID in a VARCHAR column with Slick?如何在带有 Slick 的 VARCHAR 列中使用 UUID?
【发布时间】:2017-04-16 05:13:42
【问题描述】:

我有一个 MySQL 5.7 数据库,其中包含多个表,这些表将 UUID 存储在具有 VARCHAR 数据类型的列中。我正在尝试将我们的代码库转换为使用 Slick 3.1.1,但我遇到了 UUID 到字符串转换的问题。这是我的列定义:

def myCol: Rep[UUID] = column[UUID]("myCol", SqlType("VARCHAR"))

当我运行查询时,我得到如下异常:

Got the exception java.sql.SQLException: Incorrect string value: '\xDA\xFD\xDAuOL...' for column 'myCol' at row 1

据我了解,Slick 假定 UUID 应存储为二进制列类型,因此我尝试使用以下方法隐式转换为字符串:

implicit val uuidToString = MappedColumnType.base[UUID, String](_.toString, UUID.fromString)
def myCol: Rep[UUID] = column[UUID]("myCol", SqlType("VARCHAR"))(uuidToString)

这适用于插入,但是当我选择带有myCol 的值时,结果为空。任何人都知道如何强制 Slick 使用 VARCHAR 而不是 BINARY(16) 进行数据转换?

编辑:

使用隐式 this 查询不返回任何结果:

db.run { table.filter(_.myCol === val)).result }

但是这个可以:

db.run { table.result }.map(_.filter(_.myCol == val))

EDIT2:

我在这里有一个带有演示的小项目:https://github.com/nmatpt/slick-uuid-test

【问题讨论】:

  • 只是一个注释,但您也可以存储高/低位,因为 UUID 只是两个 long 值(并将两列配对为单个唯一键)。如果您仍想使用字符串,则不需要 VARCHAR(因为 UUID 字符串始终为 36 个字符,32 个字符已删除)。

标签: mysql scala slick


【解决方案1】:

您确定来自 db 的空值?我刚刚检查了我的实现(在其中一个项目中,完全相同的解决方案),它似乎工作得很好。确保您的物理列(在 DB 中)实际上是 VARCHAR 类型(而不是一些二进制)。

编辑: 好的,我们遇到的问题是 UUID 定义为 BINARY 已经包含在 MySQL 配置文件中。快速(虽然可能有点脏)在全球范围内重新定义它会是这样的:

package profile

import java.sql.{PreparedStatement, ResultSet}

import slick.ast._
import slick.jdbc.MySQLProfile

object CustomMySqlProfile  extends MySQLProfile {
  import java.util.UUID

  override val columnTypes = new JdbcTypes

  class JdbcTypes extends super.JdbcTypes {
    override val uuidJdbcType = new UUIDJdbcType {
      override def sqlTypeName(sym: Option[FieldSymbol]) = "UUID"
      override def valueToSQLLiteral(value: UUID) = "'" + value + "'"
      override def hasLiteralForm = true

      override def setValue(v: UUID, p: PreparedStatement, idx: Int) = p.setString(idx, toString(v))
      override def getValue(r: ResultSet, idx: Int) = fromString(r.getString(idx))
      override def updateValue(v: UUID, r: ResultSet, idx: Int) = r.updateString(idx, toString(v))

      private def toString(uuid: UUID) = if(uuid != null) uuid.toString else null
      private def fromString(uuidString: String) = if(uuidString != null) UUID.fromString(uuidString) else null
    }
  }
}

然后导入它而不是常规的api:

import profile.CustomMySqlProfile.api._

这绝对有效 - 但请记住,现在 UUID 将映射到整个应用程序中的字符串(而不是默认二进制文件)。 基本上我只需要覆盖配置文件,因为 UUID 转换是内置。通常你会使用类型映射。

类似的事情在这里以类似的方式得到解决:https://github.com/slick/slick/issues/1446(也是 UUID,但用于 Oracle 驱动程序)。

【讨论】:

  • 还有什么意思:results come empty?如果此字段为空,则可能会引发异常(因为您会将 UUID.fromString 转换为无效值)。可能值得将映射拆分为单独的函数(一个单独的函数用于映射 UUID -> String 和一个 String -> UUID。在第二个函数中,您可以添加一些日志记录/断点以查看会发生什么。
  • 第一个查询似乎很好。生成的 SQL 是否正确? (您可以通过在 logback.xml 中添加以下行来查看它 -> <logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />
  • 好的,我有个主意。您在执行查询的地方是否有范围内的隐式映射 - 这个:db.run { table.result }.map(_.filter(_.myCol == val))。如果没有 - 确保你在范围内得到它
  • 是的,我写完评论后才意识到。我有一个想法 - 尝试将您的版本更改为:mvnrepository.com/artifact/com.typesafe.slick/slick_2.11/… 它非常稳定,但更重要的是它具有新的日志记录级别。添加以下内容:<logger name="slick.jdbc.JdbcBackend.parameter" level="DEBUG" /> slick.driver.MySQLDriver.api._ 更改为 slick.jdbc.MySQLProfile.api._
  • 你能把你的查询代码和这个隐式粘贴在一起吗?
【解决方案2】:
implicit val uuidToString = MappedColumnType.base[UUID, String](_.toString, UUID.fromString)

@QP,这也适用于选择,但重要的是,在您为包含“UUID”的 VARCHAR 列构建查询的任何地方,这种隐式转换都在范围内。否则 select with WHERE 条件将永远不会匹配并返回空结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-13
    • 2014-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    相关资源
    最近更新 更多