如何使用 Slick 3 和 Oracle 12 将 Clob 插入 Oracle table?

How to insert a Clob into a Oracle table with Slick 3 and Oracle 12?

但最终 slick 的异常如下:

java.lang.ClassCastException: javax.sql.rowset.serial.SerialClob cannot be cast to oracle.sql.CLOB

这可能吗?

我们终于找到了解决方法如下:

根据slickcolumn中的定义

def column[C](n: String, options: ColumnOption[C]*)(implicit tt: TypedType[C]): Rep[C]

您可以指定如何在驱动程序和您的代码之间转换该列。如果您想使用开箱即用的翻译,但对于 Oracle,CLOB 类型的翻译似乎无法正常工作。

我们所做的是将列定义为 String,但让 Slick 使用我们的自定义代码处理翻译。列定义如下:

def myClobColumn = column[String]( "CLOBCOLUMN" )( new StringJdbcType )

asd

作为我们的自定义代码 StringJdbcType 来解决我们要插入的 String(最多 65535 字节)和 Oracle CLOB.

之间的转换

StringJdbcType的代码如下:

class StringJdbcType extends driver.DriverJdbcType[String] {
  def sqlType = java.sql.Types.VARCHAR
  // Here's the solution
  def setValue( v: String, p: PreparedStatement, idx: Int ) = {
    val conn = p.getConnection
    val clob = conn.createClob()
    clob.setString( 1, v )
    p.setClob( idx, clob )
  }
  def getValue( r: ResultSet, idx: Int ) = scala.io.Source.fromInputStream( r.getAsciiStream( "DSPOLIZARIESGO" ) )( Codec.ISO8859 ).getLines().mkString
  def updateValue( v: String, r: ResultSet, idx: Int ) = r.updateString( idx, v )
  override def hasLiteralForm = false
}

setValue 函数是我们的救星,因为我们可以使用已经实例化的 PreparedStatement 和来自我们域的字符串构建一个 Oracle CLOB。在我们的实施中,我们只需要为 Oracle CLOB 做管道和肮脏的工作。

总而言之,driver.DriverJdbcType[A] 中 Slick 提供的扩展点是我们实际使用的扩展点。

这些是与解决方案相关的一些改进:关闭资源和流检查

  class BigStringJdbcType
    extends profile.DriverJdbcType[String] {

    def sqlType: Int = java.sql.Types.VARCHAR

    def setValue(v: String, p: PreparedStatement, idx: Int): Unit = {
      val connection = p.getConnection
      val clob = connection.createClob()
      try {
        clob.setString(1, v)
        p.setClob(idx, clob)
      } finally {
        clob.free()
      }
    }

    def getValue(r: ResultSet, idx: Int): String = {
      val asciiStream = r.getAsciiStream(idx)
      try {
        val (bufferEmpty, encoding) = getInputStreamStatus(asciiStream)

        if (bufferEmpty) {
          convertInputStreamToString(asciiStream, encoding)
        } else ""

      } finally {
        asciiStream.close()
      }
    }

    def updateValue(v: String, r: ResultSet, idx: Int): Unit =
      r.updateString(idx, v)

    override def hasLiteralForm: Boolean = false
  } 

补充解决方案的一些实用程序

  def getInputStreamStatus(stream: InputStream): (Boolean, String) = {
    val reader = new InputStreamReader(stream)
    try {
      val bufferEmpty = reader.ready()
      val encoding = reader.getEncoding
      bufferEmpty -> encoding

    } finally {
      reader.close()
    }
  }

  def convertInputStreamToString(
    stream: InputStream,
    encoding: String
  ): String = {
    scala.io.Source.fromInputStream(stream)(encoding).getLines().mkString
  }