对于最新的稳定版本,请使用 Spring Framework 6.2.0! |
参数和数据值处理的常见问题
参数和数据值的常见问题存在于不同的方法中 由 Spring Framework 的 JDBC 支持提供。本节介绍如何解决这些问题。
为参数提供 SQL 类型信息
通常,Spring 会根据参数的类型来确定参数的 SQL 类型
传入。可以显式提供在设置
参数值。这有时对于正确设置值是必要的。NULL
您可以通过多种方式提供 SQL 类型信息:
-
的许多 update 和 query 方法在 数组的形式。此数组用于指示 相应的参数。提供 每个参数一个条目。
JdbcTemplate
int
java.sql.Types
-
你可以使用 class 来包装需要 this 的 parameter value 其他信息。为此,请为每个值创建一个新实例并传入 SQL 类型 和构造函数中的参数值。您还可以提供可选的比例 参数表示数值。
SqlParameterValue
-
对于使用命名参数的方法,可以使用类或 .他们都有方法 用于注册任何命名参数值的 SQL 类型。
SqlParameterSource
BeanPropertySqlParameterSource
MapSqlParameterSource
处理 BLOB 和 CLOB 对象
您可以在数据库中存储图像、其他二进制数据和大块文本。这些
对于二进制数据,大型对象称为 BLOB(二进制大型对象),对于 CLOB(字符
Large OBject) 获取字符数据。在 Spring 中,您可以使用
直接使用,以及使用 RDBMS 提供的更高抽象时
Objects 和 Class.所有这些方法都使用
用于实际管理 LOB (Large OBject) 数据的接口。 通过方法
用于创建要插入的新 LOB 对象。JdbcTemplate
SimpleJdbc
LobHandler
LobHandler
LobCreator
getLobCreator
LobCreator
并为 LOB 输入和输出提供以下支持:LobHandler
-
斑点
-
byte[]
:和getBlobAsBytes
setBlobAsBytes
-
InputStream
:和getBlobAsBinaryStream
setBlobAsBinaryStream
-
-
CLOB
-
String
:和getClobAsString
setClobAsString
-
InputStream
:和getClobAsAsciiStream
setClobAsAsciiStream
-
Reader
:和getClobAsCharacterStream
setClobAsCharacterStream
-
下一个示例演示如何创建和插入 BLOB。稍后我们将介绍如何阅读 it 从数据库返回。
此示例使用 a 和 .它实现一种方法 .此方法提供了一个,我们使用它来设置
SQL 插入语句中的 LOB 列。JdbcTemplate
AbstractLobCreatingPreparedStatementCallback
setValues
LobCreator
对于此示例,我们假设有一个变量 ,它已经是
设置为 .通常通过
依赖注入。lobHandler
DefaultLobHandler
以下示例演示如何创建和插入 BLOB:
-
Java
-
Kotlin
final File blobIn = new File("spring2004.jpg");
final InputStream blobIs = new FileInputStream(blobIn);
final File clobIn = new File("large.txt");
final InputStream clobIs = new FileInputStream(clobIn);
final InputStreamReader clobReader = new InputStreamReader(clobIs);
jdbcTemplate.execute(
"INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)",
new AbstractLobCreatingPreparedStatementCallback(lobHandler) { (1)
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
ps.setLong(1, 1L);
lobCreator.setClobAsCharacterStream(ps, 2, clobReader, (int)clobIn.length()); (2)
lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, (int)blobIn.length()); (3)
}
}
);
blobIs.close();
clobReader.close();
1 | 传入 (在本例中) 是普通的 .lobHandler DefaultLobHandler |
2 | 使用该方法传入 CLOB 的内容。setClobAsCharacterStream |
3 | 使用该方法传入 BLOB 的内容。setBlobAsBinaryStream |
val blobIn = File("spring2004.jpg")
val blobIs = FileInputStream(blobIn)
val clobIn = File("large.txt")
val clobIs = FileInputStream(clobIn)
val clobReader = InputStreamReader(clobIs)
jdbcTemplate.execute(
"INSERT INTO lob_table (id, a_clob, a_blob) VALUES (?, ?, ?)",
object: AbstractLobCreatingPreparedStatementCallback(lobHandler) { (1)
override fun setValues(ps: PreparedStatement, lobCreator: LobCreator) {
ps.setLong(1, 1L)
lobCreator.setClobAsCharacterStream(ps, 2, clobReader, clobIn.length().toInt()) (2)
lobCreator.setBlobAsBinaryStream(ps, 3, blobIs, blobIn.length().toInt()) (3)
}
}
)
blobIs.close()
clobReader.close()
1 | 传入 (在本例中) 是普通的 .lobHandler DefaultLobHandler |
2 | 使用该方法传入 CLOB 的内容。setClobAsCharacterStream |
3 | 使用该方法传入 BLOB 的内容。setBlobAsBinaryStream |
如果在返回的 from 上调用 、 、 或 方法,则可以选择指定负值
对于参数。如果指定的内容长度为负数,则使用 set-stream 方法的 JDBC 4.0 变体,而不使用
length 参数。否则,它将指定的长度传递给驱动程序。 请参阅您所使用的 JDBC 驱动程序的文档,以验证它是否支持流式处理 一个 LOB,但未提供内容长度。 |
现在,可以从数据库中读取 LOB 数据。同样,您将 a 与相同的实例变量和对 .
以下示例显示了如何执行此操作:JdbcTemplate
lobHandler
DefaultLobHandler
-
Java
-
Kotlin
List<Map<String, Object>> l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table",
new RowMapper<Map<String, Object>>() {
public Map<String, Object> mapRow(ResultSet rs, int i) throws SQLException {
Map<String, Object> results = new HashMap<String, Object>();
String clobText = lobHandler.getClobAsString(rs, "a_clob"); (1)
results.put("CLOB", clobText);
byte[] blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob"); (2)
results.put("BLOB", blobBytes);
return results;
}
});
1 | 使用该方法检索 CLOB 的内容。getClobAsString |
2 | 使用该方法检索 BLOB 的内容。getBlobAsBytes |
val l = jdbcTemplate.query("select id, a_clob, a_blob from lob_table") { rs, _ ->
val clobText = lobHandler.getClobAsString(rs, "a_clob") (1)
val blobBytes = lobHandler.getBlobAsBytes(rs, "a_blob") (2)
mapOf("CLOB" to clobText, "BLOB" to blobBytes)
}
1 | 使用该方法检索 CLOB 的内容。getClobAsString |
2 | 使用该方法检索 BLOB 的内容。getBlobAsBytes |
传入 IN 子句的值列表
SQL 标准允许根据包含
variable 的值列表。一个典型的例子是 。准备好的语句不直接支持此变量列表
JDBC 标准。不能声明可变数量的占位符。您需要一个号码
的变体,并准备好所需数量的占位符,或者您需要生成
SQL 字符串,一旦您知道需要多少个占位符。命名的
Parameter support 采用后一种方法。
您可以将值作为简单值的 (或任何) 传入。
此列表用于将所需的占位符插入到实际的 SQL 语句中
并在语句执行期间传入值。select * from T_ACTOR where id in
(1, 2, 3)
NamedParameterJdbcTemplate
java.util.List
Iterable
传入许多值时要小心。JDBC 标准不保证
表达式列表可以有 100 个以上的值。各种数据库超过
这个数字,但它们通常对允许的值数量有硬性限制。
例如,Oracle 的限制为 1000。IN |
除了值列表中的基元值之外,您还可以创建 of 对象数组。此列表可以支持为子句定义的多个表达式,例如 .当然,这需要您的数据库支持此语法。java.util.List
in
select * from T_ACTOR where (id, last_name) in ((1, 'Johnson'), (2,
'Harrop'))
处理存储过程调用的复杂类型
调用存储过程时,有时可以使用特定于
数据库。为了适应这些类型,Spring 提供了一个用于处理
当它们从存储过程调用返回时,以及当它们
作为参数传递给存储过程。SqlReturnType
SqlTypeValue
接口有一个方法(名为 ),该方法必须是
实现。此接口用作 .
以下示例显示如何返回用户的 Oracle 对象的值
声明类型 :SqlReturnType
getTypeValue
SqlOutParameter
STRUCT
ITEM_TYPE
-
Java
-
Kotlin
public class TestItemStoredProcedure extends StoredProcedure {
public TestItemStoredProcedure(DataSource dataSource) {
// ...
declareParameter(new SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE",
(CallableStatement cs, int colIndx, int sqlType, String typeName) -> {
STRUCT struct = (STRUCT) cs.getObject(colIndx);
Object[] attr = struct.getAttributes();
TestItem item = new TestItem();
item.setId(((Number) attr[0]).longValue());
item.setDescription((String) attr[1]);
item.setExpirationDate((java.util.Date) attr[2]);
return item;
}));
// ...
}
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
// ...
declareParameter(SqlOutParameter("item", OracleTypes.STRUCT, "ITEM_TYPE") { cs, colIndx, sqlType, typeName ->
val struct = cs.getObject(colIndx) as STRUCT
val attr = struct.getAttributes()
TestItem((attr[0] as Long, attr[1] as String, attr[2] as Date)
})
// ...
}
}
您可以使用 将 Java 对象(例如 )的值传递给
stored 过程。该接口具有您必须实现的单个方法 (名为 )。活动连接传入,您
可以使用它来创建特定于数据库的对象,例如实例
或 instances 的 S 。以下示例创建一个实例:SqlTypeValue
TestItem
SqlTypeValue
createTypeValue
StructDescriptor
ArrayDescriptor
StructDescriptor
-
Java
-
Kotlin
final TestItem testItem = new TestItem(123L, "A test item",
new SimpleDateFormat("yyyy-M-d").parse("2010-12-31"));
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
StructDescriptor itemDescriptor = new StructDescriptor(typeName, conn);
Struct item = new STRUCT(itemDescriptor, conn,
new Object[] {
testItem.getId(),
testItem.getDescription(),
new java.sql.Date(testItem.getExpirationDate().getTime())
});
return item;
}
};
val (id, description, expirationDate) = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val itemDescriptor = StructDescriptor(typeName, conn)
return STRUCT(itemDescriptor, conn,
arrayOf(id, description, java.sql.Date(expirationDate.time)))
}
}
现在可以将其添加到包含存储过程调用的输入参数的 中。SqlTypeValue
Map
execute
的另一个用途是将值数组传递给存储的 Oracle
程序。在这种情况下,必须使用 Oracle 自己的内部类,并且
您可以使用 创建 Oracle 的实例并填充
它与来自 Java 的值,如下例所示:SqlTypeValue
ARRAY
SqlTypeValue
ARRAY
ARRAY
-
Java
-
Kotlin
final Long[] ids = new Long[] {1L, 2L};
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
ArrayDescriptor arrayDescriptor = new ArrayDescriptor(typeName, conn);
ARRAY idArray = new ARRAY(arrayDescriptor, conn, ids);
return idArray;
}
};
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure() {
init {
val ids = arrayOf(1L, 2L)
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
val arrayDescriptor = ArrayDescriptor(typeName, conn)
return ARRAY(arrayDescriptor, conn, ids)
}
}
}
}