如何使用 Slick 3 和 Oracle 12 将 Clob 插入 Oracle table?
How to insert a Clob into a Oracle table with Slick 3 and Oracle 12?
- 我们有一个带有 CLOB 列的 table(用于保存 JSON 数据)
- 据我们了解(来自文档)slick 支持 LOB 类型 (http://slick.lightbend.com/doc/3.1.1/schemas.html)
- 我们可以成功查询table。包括 CLOB 列。
我们不能用 Clob 插入寄存器。我们正在将字符串转换为 java.sql.Clob,其中:
private java.sql.Clob stringToClob(String source)
{
try
{
return new javax.sql.rowset.serial.SerialClob(source.toCharArray());
}
catch (Exception e)
{
log.error("Could not convert string to a CLOB",e);
return null;
}
}
但最终 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
}
- 我们有一个带有 CLOB 列的 table(用于保存 JSON 数据)
- 据我们了解(来自文档)slick 支持 LOB 类型 (http://slick.lightbend.com/doc/3.1.1/schemas.html)
- 我们可以成功查询table。包括 CLOB 列。
我们不能用 Clob 插入寄存器。我们正在将字符串转换为 java.sql.Clob,其中:
private java.sql.Clob stringToClob(String source) { try { return new javax.sql.rowset.serial.SerialClob(source.toCharArray()); } catch (Exception e) { log.error("Could not convert string to a CLOB",e); return null; } }
但最终 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
}