我正在尝试传递ARRAY
BLOB
,但我收到了错误。
uploadFiles = new SimpleJdbcCall(dataSource).withCatalogName("FILES_PKG")
.withFunctionName("insertFiles").withReturnValue()
.declareParameters(new SqlParameter("p_userId", Types.NUMERIC),
new SqlParameter("p_data", Types.ARRAY, "BLOB_ARRAY"),
new SqlOutParameter("v_groupId", Types.NUMERIC));
uploadFiles.compile();
List<Blob> fileBlobs = new ArrayList<>();
for(int x = 0; x < byteFiles.size(); x++){
fileBlobs.add(new javax.sql.rowset.serial.SerialBlob(byteFiles.get(x)));
}
final Blob[] data = fileBlobs.toArray(new Blob[fileBlobs.size()]);
SqlParameterSource in = new MapSqlParameterSource()
.addValue("p_files", new SqlArrayValue<Blob>(data, "BLOB_ARRAY"))
.addValue("p_userId", userId);
Map<String, Object> results = uploadFiles.execute(in);
我在数据库中创建了一个SQL类型
create or replace TYPE BLOB_ARRAY is table of BLOB;
功能规格
FUNCTION insertFiles(p_userId IN NUMBER,
p_files IN BLOB_ARRAY)
RETURN NUMBER;
功能体
FUNCTION insertFiles (p_userId IN NUMBER,
p_files IN BLOB_ARRAY)
RETURN NUMBER
AS
v_groupId NUMBER := FILE_GROUP_ID_SEQ.NEXTVAL;
v_fileId NUMBER;
BEGIN
FOR i IN 1..p_files.COUNT
LOOP
v_fileId := FILE_ID_SEQ.NEXTVAL;
BEGIN
INSERT INTO FILES
(FILE_ID,
FILE_GROUP_ID,
FILE_DATA,
UPDT_USER_ID)
SELECT
v_fileId,
v_groupId,
p_files(i),
USER_ID
FROM USERS
WHERE USER_ID = p_userId;
EXCEPTION WHEN OTHERS THEN
v_groupId := -1;
END;
END LOOP;
RETURN v_groupId;
END insertFiles;
我不确定如何正确地将Blob数组传递给SQL函数。
错误:
java.sql.SQLException:无法转换为内部表示: javax.sql.rowset.serial.SerialBlob@87829c90 at oracle.jdbc.oracore.OracleTypeBLOB.toDatum(OracleTypeBLOB.java:69) 〜[ojdbc7.jar:12.1.0.1.0] at oracle.jdbc.oracore.OracleType.toDatumArray(OracleType.java:176) 〜[ojdbc7.jar:12.1.0.1.0] at oracle.sql.ArrayDescriptor.toOracleArray(ArrayDescriptor.java:1321) 〜[ojdbc7.jar:12.1.0.1.0]在oracle.sql.ARRAY。(ARRAY.java:140)
更新
在尝试了Luke的建议后,我收到以下错误:
未分类SQL的SQLException [{? =调用FILES_PKG.INSERTFILES(?, ?)}]; SQL状态[99999];错误代码[22922]; ORA-22922:不存在 LOB值;嵌套异常是java.sql.SQLException:ORA-22922: 具有根本原因的不存在的LOB值
java.sql.SQLException:ORA-22922:不存在的LOB值
答案 0 :(得分:3)
出现错误消息,表明Oracle JDBC驱动程序不知道如何处理您传递给它的javax.sql.rowset.serial.SerialBlob对象。
尝试使用Connection.createBlob
创建Blob
个对象。换句话说,请尝试替换以下内容
环
for(int x = 0; x < byteFiles.size(); x++){
fileBlobs.add(new javax.sql.rowset.serial.SerialBlob(byteFiles.get(x)));
}
与
Connection conn = dataSource.getConnection();
for(int x = 0; x < byteFiles.size(); x++){
Blob blob = conn.createBlob();
blob.setBytes(1, byteFiles.get(x));
fileBlobs.add(blob);
}
另外,请确保您的SimpleJdbcCall
与存储的函数之间的参数名称一致。您的SimpleJdbcCall
使用名称BLOB
声明p_data
数组参数,但您存储的函数声明使用p_files
。如果参数名称不一致,则可能会出现Invalid column type
错误。
但是,如果我使用我自己的存储函数运行上述测试,实际上已经传入了BLOB
值,而不是仅仅对返回值进行硬编码,我可能会发现这种方法没用。我不知道为什么,我可能不得不花一些时间在Spring的胆量中挖掘才能找到答案。
我尝试用Spring Blob
替换SqlLobValue
值,但这也不起作用。我猜Spring的SqlArrayValue<T>
类型不处理各种JDBC类型的Spring包装器对象。
所以我放弃了Spring方法并回到了简单的JDBC:
import oracle.jdbc.OracleConnection;
// ...
OracleConnection conn = dataSource.getConnection().unwrap(OracleConnection.class);
List<Blob> fileBlobs = new ArrayList<>();
for(int x = 0; x < byteFiles.size(); x++){
Blob blob = conn.createBlob();
blob.setBytes(1, byteFiles.get(x));
fileBlobs.add(blob);
}
Array array = conn.createOracleArray("BLOB_ARRAY",
fileBlobs.toArray(new Blob[fileBlobs.size()]));
CallableStatement cstmt = conn.prepareCall("{? = call insertFiles(?, ?)}");
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.setInt(2, userId);
cstmt.setArray(3, array);
cstmt.execute();
int result = cstmt.getInt(1);
我已经使用您现在包含在问题中的存储函数对此进行了测试,并且可以调用此函数并将BLOB
插入到数据库中。
我会让你做你认为适合变量result
的事情,并添加任何必要的清理或交易控制。
然而,虽然这种方法有效,但感觉正确。它不适合Spring的做事方式。它至少证明了你所要求的是可能的,因为JDBC驱动程序中没有一些限制意味着你不能使用BLOB
数组。我觉得应该有一些方法可以使用Spring JDBC来调用你的函数。
我花了一些时间研究ORA-22922错误并得出结论,潜在的问题是Blob
对象是使用与用于执行语句的内容不同的Connection
创建的。那么问题就变成了如何掌握Connection
Spring的用法。
在对源代码进一步深入挖掘各种Spring类之后,我意识到更像Spring的方法是将SqlArrayValue<T>
类替换为专用于BLOB
的不同类。阵列。这就是我最终的结果:
import java.sql.Array;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import oracle.jdbc.OracleConnection;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.jdbc.core.support.AbstractSqlTypeValue;
public class SqlBlobArrayValue extends AbstractSqlTypeValue {
private List<byte[]> values;
private String defaultTypeName;
public SqlBlobArrayValue(List<byte[]> values) {
this.values = values;
}
public SqlBlobArrayValue(List<byte[]> values, String defaultTypeName) {
this.values = values;
this.defaultTypeName = defaultTypeName;
}
protected Object createTypeValue(Connection conn, int sqlType, String typeName)
throws SQLException {
if (typeName == null && defaultTypeName == null) {
throw new InvalidDataAccessApiUsageException(
"The typeName is null in this context. Consider setting the defaultTypeName.");
}
Blob[] blobs = new Blob[values.size()];
for (int i = 0; i < blobs.length; ++i) {
Blob blob = conn.createBlob();
blob.setBytes(1, values.get(i));
blobs[i] = blob;
}
Array array = conn.unwrap(OracleConnection.class).createOracleArray(typeName != null ? typeName : defaultTypeName, blobs);
return array;
}
}
此课程主要基于SqlArrayValue<T>
,该许可在Version 2 of the Apache License下获得许可。为简洁起见,我省略了注释和package
指令。
在这个类的帮助下,使用Spring JDBC调用函数变得更加容易。实际上,您可以在调用uploadFiles.compile()
之后用以下内容替换所有内容:
SqlParameterSource in = new MapSqlParameterSource()
.addValue("p_files", new SqlBlobArrayValue(byteFiles, "BLOB_ARRAY"))
.addValue("p_userId", userId);
Map<String, Object> results = uploadFiles.execute(in);