2

我尝试byte[]使用存储过程插入 blob 字段,并得到一个异常:

请求处理失败;嵌套异常是 org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; 错误的 SQL 语法 [SELECT ID FROM sp_NEWFILE(?,?,?)]; 嵌套异常是 org.firebirdsql.jdbc.field.TypeConversionException:转换为对象时出错。

模型:

public class fileBody { 
private int ID;
private byte[] BODY;
private String FILENAME; //getters an setters}

将其插入数据库

public class FileBodyDaoImpl implements FileBodyDao {

public int insertData(final FileBody fileBody) throws IOException {     
    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    LobHandler lobHandler = new DefaultLobHandler();        
    final InputStream in = new ByteArrayInputStream(fileBody.getBODY());
    final int fileSize = in.available();
    Map<String, Object> out = jdbcTemplate.queryForMap("SELECT ID FROM  sp_NEWFILE(?,?,?)",
            new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
                protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
                        DataAccessException {
                    ps.setString(1, fileBody.getFILENAME());
                    lobCreator.setBlobAsBinaryStream(ps, 2, in, fileSize);
                    ps.setNull(3, java.sql.Types.INTEGER);
                }
            });     
    int last_inserted = Integer.parseInt(String.valueOf(out.get("ID")));
    return last_inserted;
}

还有我的存储过程

create or alter procedure sp_NEWFILE (
FILENAME varchar(255),
BODY blob sub_type 0 segment size 80,
USEID integer)
returns (
ID integer)
as
begin
  if (useid is not null) then ID=USEID;
  else ID=GEN_ID(gen_filebody_id,1);
  if ((FILENAME is NULL) or (FILENAME=''))  then FILENAME='UNDEFINED';
  INSERT INTO  t_filebody(ID,BODY,FILENAME) VALUES(:ID,:BODY,:FILENAME);
  suspend;
end^

我得到一个例外:

Request processing failed; nested exception is org.springframework.jdbc.BadSqlGrammarException: 
PreparedStatementCallback; 
bad SQL grammar [SELECT ID FROM sp_NEWFILE(?,?,?)]; nested exception is org.firebirdsql.jdbc.field.TypeConversionException: Error converting to object.

版本:jaybird-jdk17-2.2.5;来源:firebird2.5 版本:2.5.1.26351.ds4-2ubuntu0.1;

4

2 回答 2

3

问题是queryForMap不支持 a PreparedStatementCallback(与 example 相反execute),相反,您的匿名对象被认为是执行查询的正常参数,而 Jaybird 不支持此对象类型。如果 Jaybird 支持它,您将收到缺少参数 2 和 3 的错误。

通过传递字节数组可以大大简化您的代码:

Map<String, Object> out = jdbcTemplate.queryForMap("SELECT ID FROM sp_NEWFILE(?,?,?)",
        fileBody.getFILENAME(), fileBody.getBODY(), null);

这是因为 Jaybird 将 aBLOB SUB_TYPE 0视为 ajava.sql.Types.LONGVARBINARY并且 JDBC 4.2 附录 B 声明这byte[]是它的默认类型(尽管您也可以将其用作 a java.sql.Types.BLOB)。

作为旁注,您的存储过程不需要是可选择的(删除SUSPEND使其可执行),并且该过程也可以通过使用 aTRIGGER来生成主键并通过使用INSERT .. RETURNING ..或通过 JDBC 生成的键工具检索值来替换(这又在 Jaybird 中通过 实现INSERT .. RETURNING ..)。

于 2014-09-01T19:06:57.560 回答
0

对于那些寻求非 Jaybird 解决方案来使用 spring jdbctemplate 插入 BLOB 的人,以下语法对我有用,以使用与通过查询插入相比不同的存储过程。

通过查询插入

ByteArrayInputStream inputStream = new ByteArrayInputStream(file.getBytes());
ps.setBlob(1, inputStream);

通过存储过程调用插入

    Map<String, Object> inParams = new HashMap<>();
    inParams.put("pi_some_id", id);
    inParams.put("pi_file_blob",  new SqlLobValue(file.getBytes()));        
    SqlParameterSource sqlParameterSource = new MapSqlParameterSource(inParams);        
    SqlParameter[] sqlParameters = {
                    new SqlParameter("pi_some_id", Types.VARCHAR),
                    new SqlParameter("pi_file_blob", Types.BLOB),
                    new SqlOutParameter("po_error_flag", Types.VARCHAR),
                    new SqlOutParameter("po_message", Types.VARCHAR)};
            
    SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(jdbcTemplate).withoutProcedureColumnMetaDataAccess().
withProcedureName(storedProcName).withCatalogName(packageName).
declareParameters(sqlParameters);
    Map<String, Object> storedProcResult = simpleJdbcCall.execute(sqlParameterSource);
于 2022-02-22T14:49:22.833 回答