如何使用Blob对象将大型原始XML文件写入Oracle数据库?

时间:2019-07-12 14:10:16

标签: java xml oracle blob

我有一个函数,可以使用FileInputStream将大型XML文件转换为字节数组。它可以在我的IDE中正常运行,但是在通过可执行文件jar独立运行时会抛出Exception in thread "main" java.lang.OutOfMemoryError: Java heap space。我正在读取字节数组中的大文件,以将其作为Blob存储在目标数据库中。我无法控制Blob的存储方式,我只能访问存储过程以插入Blob。有没有办法在不将整个文件加载到内存的情况下读写数据块?

将文件转换为字节数组的功能-

private byte[] getBytesFromFile(Path path) throws IOException {
    FileInputStream fis = new FileInputStream(path.toFile());
    byte[] bytes = new byte[(int) path.toFile().length()];
    int read = 0;
    int offset = 0;
    while(offset < bytes.length && (read = fis.read(bytes, offset, bytes.length - offset)) >= 0 ){
        offset += read;
    }
    fis.close();
    return bytes;
}

这是使用存储过程调用将字节数组存储到db的代码

private void storeFileToDb(Connection connection, int fileId, String fileName, String fileType, byte[] fileBytes) throws SQLException {
    //
    String storedProcedure = "{call SP(?,?,?,?,?) }";
    CallableStatement callableStatement = connection.prepareCall(storedProcedure);
    callableStatement.setInt(1, fileId);
    callableStatement.setString(2, fileName);
    callableStatement.setString(3, fileType);
    Blob fileBlob = connection.createBlob();
    fileBlob.setBytes(1, fileBytes);
    callableStatement.setBlob(4, fileBlob);
    callableStatement.registerOutParameter(5, OracleTypes.NUMBER);
    callableStatement.execute();
    fileBlob.free(); // not entirely sure how this helps
    //callableStatement.close();
}

2 个答案:

答案 0 :(得分:1)

使用CallableStatement.setBlob(int, InputStream)Blob.setBinaryStream(long)。两种方法都可以使用InputStreamOutputStream对象,并避免在内存中创建byte[]数组。示例在Adding Large Object Type Object to Database docs中显示。

只要JDBC驱动程序足够聪明,不要在内部某个地方为整个blob创建byte[],这应该可以工作。

答案 1 :(得分:1)

服务器配置可能太严格。现在是检查内存参数的好时机。

仅提供 InputStream 即可填充Blob。

压缩XML数据也是一个好主意。尝试一下:将test.xml压缩为test.xml.gz,以增加大小。

请注意,标准Java中存在:

private byte[] getBytesFromFile(Path path) throws IOException {
    return Files.readAllBytes(path);
}

所以:

private void storeFileToDb(Connection connection, int fileId, String fileName,
        String fileType) throws SQLException, IOException {
    Path path = Paths.get(fileName); // Or parameter
    try (CallableStatement callableStatement = connection.prepareCall(storedProcedure);
         GZipInputStream fileIn = new GZipInputStream(Files.newBufferedInputStream(path))) {
        ...
        callableStatement.setBlob(4, fileIn);
        ...
    }
}

try-with-resources确保在引发异常或返回等情况下关闭。对于声明也很有用。

您没有 close 语句,里面有一个Blob。这是不可取的,因为数据可能会停留一段时间。 CallableStatement也是PreparedStatement,其中一个用例正在使用可能的其他参数值重复执行SQL。是否。

并用于解压缩GZipOutputStream