过于复杂的oracle jdbc BLOB处理

asa*_*n74 31 java oracle blob jdbc clob

当我使用jdbc瘦驱动程序搜索Web以将BLOB插入Oracle数据库时,大多数网页都建议采用三步法:

  1. 插入empty_blob()值.
  2. 选择行for update.
  3. 插入实际价值.

这对我来说很好,这是一个例子:

Connection oracleConnection = ...

byte[] testArray = ...

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test (id, blobfield) values(?, empty_blob())");
ps.setInt(1, 100);
ps.executeUpdate();
ps.close();
ps = oracleConnection.prepareStatement(
    "select blobfield from test where id = ? for update");
ps.setInt(1, 100);
OracleResultSet rs = (OracleResultSet) ps.executeQuery();
if (rs.next()) {
    BLOB blob = (BLOB) rs.getBLOB(1);
    OutputStream outputStream = blob.setBinaryStream(0L);
    InputStream inputStream = new ByteArrayInputStream(testArray);
    byte[] buffer = new byte[blob.getBufferSize()];
    int byteread = 0;
    while ((byteread = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, byteread);
    }
    outputStream.close();
    inputStream.close();
}
Run Code Online (Sandbox Code Playgroud)

有一些网页作者建议使用更简单的一步解决方案.此解决方案的先前示例:

Connection oracleConnection = ...

byte[] testArray = ...

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test(id, blobfield) values(?, ?)");
BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION);
OutputStream outputStream = blob.setBinaryStream(0L);
InputStream inputStream = new ByteArrayInputStream(testArray);
byte[] buffer = new byte[blob.getBufferSize()];
int byteread = 0;
while ((byteread = inputStream.read(buffer)) != -1) {
    outputStream.write(buffer, 0, byteread);
}
outputStream.close();
inputStream.close();

ps.setInt(1, 100);
ps.setBlob(2, blob);
ps.executeUpdate();
ps.close();
Run Code Online (Sandbox Code Playgroud)

第二个代码更容易,所以我的问题是:第一个(流行)解决方案的重点是什么?是否存在某种约束(Oracle服务器版本号,jdbc驱动程序版本,blob的大小......)?第一种解决方案更好(速度,内存消耗......)?没有使用更简单的第二种方法的任何理由?

完全相同的问题适用于CLOB字段.

Mr.*_* 安宇 11

您在第一种情况下提到的更新方法可以使用纯JDBC代码重写,从而减少您对特定于Oracle的类的依赖性.如果您的应用需要与数据库无关,这可能会有所帮助.

public static void updateBlobColumn(Connection con, String table, String blobColumn, byte[] inputBytes, String idColumn, Long id) throws SQLException {
  PreparedStatement pStmt = null;
  ResultSet rs = null;
  try {
    String sql = 
      " SELECT " + blobColumn + 
      " FROM " + table + 
      " WHERE " + idColumn + " = ? " +
      " FOR UPDATE";
    pStmt = con.prepareStatement(sql, 
      ResultSet.TYPE_FORWARD_ONLY, 
      ResultSet.CONCUR_UPDATABLE);
    pStmt.setLong(1, id);
    rs = pStmt.executeQuery();
    if (rs.next()) {
      Blob blob = rs.getBlob(blobColumn);
      blob.truncate(0);
      blob.setBytes(1, inputBytes);
      rs.updateBlob(blobColumn, blob);
      rs.updateRow();
    }
  }
  finally {
    if(rs != null) rs.close();
    if(pStmt != null) pStmt.close();
  }
}
Run Code Online (Sandbox Code Playgroud)

对于MSSQL,我理解锁定语法是不同的:

String sql = 
  " SELECT " + blobColumn + 
  " FROM " + table + " WITH (rowlock, updlock) " + 
  " WHERE " + idColumn + " = ? "
Run Code Online (Sandbox Code Playgroud)

  • 你没有在这里插入BLOB,你只是在更新它.OP特别关于插入. (5认同)

小智 7

Oracle DBA的另一种观点.当Sun设计JDBC标准(1.0,2.0,3.0,4.0)时,他们的工作非常糟糕.BLOB代表大型物体,因此它可能非常大.它是无法存储在JVM堆中的东西.Oracle认为BLOB与文件句柄类似(事实上它们称之为"lob定位器").LOBS不能通过构造函数创建,也不是Java对象.另外LOB定位器(oracle.sql.BLOB)不能通过构造函数创建 - 它们必须在DB端创建.在Oracle中,有两种方法可以创建LOB.

  1. DBMS_LOB.CREATETEMPORATY - 在这种情况下返回的定位符指向临时表空间.针对此定位器的所有写入/读取将通过网络发送到DB服务器.JVM堆中没有存储任何内容.

  2. 调用EMPTY_BLOB函数.插入T1(名称,文件)值("a.avi",EMPTY_BLOB())返回文件?在这种情况下,返回的lob定位器指向数据表空间.针对此定位器的所有写入/读取将通过网络发送到DB服务器.通过写入重做日志来"保护"所有写入.JVM堆中没有存储任何内容.JDBC标准(1.0,2.0)不支持返回子句,因此您可以在互联网上找到许多人们建议采用两个步骤的示例:"INSERT ...; SELECT ... FOR UPDATE;"

Oracle lobs必须与某些数据库连接相关联,当数据库连接丢失/关闭/(或"提交")时,不能使用它们.它们不能从一个连接传递到另一个连接.

您的第二个示例可以工作,但如果数据从临时表空间进入数据表空间,则需要进行过多的复制.

  • OMG - 我只想在String中将几行文本插入到我继承的模式中的"BLOB"中.字符串不是MB,更不用说GB了,但是,它有时超过2000个字符的惊人限制.真是个马戏团. (3认同)

ska*_*man 5

Oracle服务器的LOB处理非常差,并且可能会遇到严重的性能问题(例如,大量过度使用重做日志),因此第一种解决方案可能是解决这些问题的方法.

我建议尝试这两种方法.如果您有一个称职的DBA,他们可能会建议哪种方法对服务器的影响最小.


Bri*_*ian 5

JDBC的一个有趣之处在于,您可以相当积极地升级到最新的驱动程序并使用JDBC 4.0功能.oracle JDBC驱动程序将与较旧的数据库版本一起使用,因此您可以对10g数据库使用11g品牌JDBC驱动程序.Oracle数据库11g JDBC有两种形式:Java 5的ojdbc5.jar(即JDK 1.5)和Java 6的ojdbc6.jar(即JDK 1.6).ojdbc6.jar支持新的JDBC 4.0规范.

使用较新的drivers/jdbc 4.0,您可以从连接对象创建Blob和Clobs:

Blob aBlob = con.createBlob();
int numWritten = aBlob.setBytes(1, val);
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,Oracle在实现BLOB处理的标准JDBC方式方面表现不佳(可能出于商业原因).您被迫使用具体的oracle类来正确完成工作. (3认同)