如何在Java中通过PreparedStatement向DB2 Z/OS插入十六进制值?
我需要在Java中使用PreparedStatement向DB2 Z/OS数据库插入十六进制值,目前查询环节可以通过HEX函数正常获取结果:
String id = "02658947530232010038892587183C"; String request = "SELECT HEX(COLUMN) FROM TABLE WHERE HEX(COLUMN)=?"; Connection con = getConnectionDb2z(); ResultSet rs = null; try(PreparedStatement select = con.prepareStatement(request)) { select.setString(1, id); rs = select.executeQuery(); rs.next(); System.out.println(rs.getString("COLUMN")); } catch(SQLException e) { throw e; } finally { con.close(); }
查询结果为02658947530232010038892587183C(示例值)。
但插入值时遇到了问题:
尝试1:直接在X'?'中使用参数
String id = "02658947530232010038892587183C"; String request = "INSERT INTO TABLE (COLUMN) VALUES (X'?')"; Connection con = getConnectionDb2z(); try(PreparedStatement insert = con.prepareStatement(request)) { insert.setString(1, id); insert.executeUpdate(); }
报错:
Exception in thread "main" com.ibm.db2.jcc.am.SqlSyntaxErrorException: invalid parameter.
原因:DB2无法识别X'?'内部的参数占位符,语法不合法。
尝试2:将X'...'作为字符串传入
String id = "X'02658947530232010038892587183C'"; String request = "INSERT INTO TABLE (COLUMN) VALUES (?)"; Connection con = getConnectionDb2z(); try(PreparedStatement insert = con.prepareStatement(request)) { insert.setString(1, id); insert.executeUpdate(); }
报错:
Exception in thread "main" com.ibm.db2.jcc.am.SqlDataException: DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001
原因:数据库将X'02658947530232010038892587183C'当作普通字符串处理,而目标字段是7字节的二进制类型,长度不匹配导致错误。
当前临时方案(存在安全风险)
只能通过字符串替换生成SQL,但这种方式会引发SQL注入风险,不符合安全规范:
String id = "02658947530232010038892587183C"; String request = "INSERT INTO TABLE (COLUMN) VALUES (X'?')"; Connection con = getConnectionDb2z(); try(PreparedStatement insert = con.prepareStatement(request.replace("?", id))) { insert.executeUpdate(); }
补充说明:数据库为DB2 Z/OS,无UNHEX函数;X''是DB2用于将十六进制字符串转为二进制的语法,目标字段COLUMN长度为7字节,对应14位十六进制字符串;查询时通过HEX(COLUMN)可以还原出原始十六进制字符串,直接查询该字段则显示不可读字符。
正确的安全实现方法
核心思路是将十六进制字符串转换为byte[]数组,再通过PreparedStatement.setBytes()方法传入参数,避免字符串拼接带来的注入风险:
步骤1:实现十六进制字符串转byte数组的工具方法
public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16)); } return data; }
步骤2:使用setBytes插入二进制数据
String id = "02658947530232010038892587183C"; String request = "INSERT INTO TABLE (COLUMN) VALUES (?)"; Connection con = getConnectionDb2z(); try(PreparedStatement insert = con.prepareStatement(request)) { byte[] binaryId = hexStringToByteArray(id); insert.setBytes(1, binaryId); insert.executeUpdate(); } catch(SQLException e) { throw e; } finally { con.close(); }
这种方式完全遵循PreparedStatement的安全规范,既避免了SQL注入,又能正确将十六进制字符串转为二进制数据插入到目标字段中。查询时依然可以通过HEX(COLUMN)获取原始的十六进制字符串。
内容的提问来源于stack exchange,提问作者Maelstrom

