此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
参数和数据值处理的常见问题
不同方法中存在参数和数据值的常见问题 由 Spring Framework 的 JDBC 支持提供。本节介绍如何解决这些问题。
为参数提供 SQL 类型信息
通常,Spring 会根据参数的类型确定参数的 SQL 类型
通过了。可以显式提供在设置时要使用的 SQL 类型
参数值。有时需要正确设置NULL
值。
您可以通过多种方式提供 SQL 类型信息:
-
许多更新和查询方法
JdbcTemplate
在 的int
数组。此数组用于指示 相应的参数,使用来自java.sql.Types
类。提供 每个参数一个条目。 -
您可以使用
SqlParameterValue
类来包装需要此参数值 附加信息。为此,请为每个值创建一个新实例并传入 SQL 类型 以及构造函数中的参数值。您还可以提供可选的比例 参数。 -
对于使用命名参数的方法,您可以使用
SqlParameterSource
类BeanPropertySqlParameterSource
或MapSqlParameterSource
.他们都有方法 用于注册任何命名参数值的 SQL 类型。
处理 BLOB 和 CLOB 对象
您可以在数据库中存储图像、其他二进制数据和大块文本。这些
大对象称为二进制数据的 BLOB(Binary Large OBject),而 CLOB(字符
Large OBject) 来获取字符数据。在 Spring 中,您可以使用以下命令来处理这些大型对象
这JdbcTemplate
直接使用RDBMS提供的高级抽象时也是如此
Objects 和SimpleJdbc
类。所有这些方法都使用
这LobHandler
用于实际管理 LOB(大型 OBject)数据的接口。LobHandler
提供对LobCreator
类,通过getLobCreator
方法
用于创建要插入的新 LOB 对象。
LobCreator
和LobHandler
为 LOB 输入和输出提供以下支持:
-
斑点
-
byte[]
:getBlobAsBytes
和setBlobAsBytes
-
InputStream
:getBlobAsBinaryStream
和setBlobAsBinaryStream
-
-
CLOB
-
String
:getClobAsString
和setClobAsString
-
InputStream
:getClobAsAsciiStream
和setClobAsAsciiStream
-
Reader
:getClobAsCharacterStream
和setClobAsCharacterStream
-
下一个示例演示如何创建和插入 BLOB。稍后我们将展示如何阅读 它从数据库中返回。
此示例使用JdbcTemplate
以及AbstractLobCreatingPreparedStatementCallback
.它实现了一种方法setValues
.此方法提供了一个LobCreator
我们用来设置
LOB 列。
对于这个例子,我们假设有一个变量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 | 使用该方法setClobAsCharacterStream 传入 CLOB 的内容。 |
3 | 使用该方法setBlobAsBinaryStream 传入 BLOB 的内容。 |
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 | 使用该方法setClobAsCharacterStream 传入 CLOB 的内容。 |
3 | 使用该方法setBlobAsBinaryStream 传入 BLOB 的内容。 |
如果调用 请参阅用于验证其是否支持流式处理的 JDBC 驱动程序的文档 一个 LOB,但不提供内容长度。 |
现在是时候从数据库中读取 LOB 数据了。同样,您使用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 | 使用该方法getClobAsString 检索 CLOB 的内容。 |
2 | 使用该方法getBlobAsBytes 检索 BLOB 的内容。 |
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 | 使用该方法getClobAsString 检索 CLOB 的内容。 |
2 | 使用该方法getBlobAsBytes 检索 BLOB 的内容。 |
传入 IN 子句的值列表
SQL 标准允许根据包含
变量值列表。一个典型的例子是select * from T_ACTOR where id in
(1, 2, 3)
.预处理语句不直接支持此变量列表
JDBC 标准。不能声明可变数量的占位符。您需要一个号码
的变体,并准备了所需数量的占位符,或者您需要生成
一旦您知道需要多少个占位符,就会动态地显示 SQL 字符串。命名的
参数支持NamedParameterJdbcTemplate
采取后一种方法。
您可以将值作为java.util.List
(或任何Iterable
) 的简单值。
此列表用于将所需的占位符插入到实际的 SQL 语句中
并在语句执行期间传入值。
传入许多值时要小心。JDBC 标准不保证
您可以对IN 表达式列表。各种数据库超过
这个数字,但它们通常对允许的值数有硬性限制。
例如,Oracle 的限制是 1000。 |
除了值列表中的基元值外,还可以创建java.util.List
对象数组。此列表可以支持为in
子句,例如select * from T_ACTOR where (id, last_name) in ((1, 'Johnson'), (2,
'Harrop'))
. 当然,这需要您的数据库支持此语法。
处理存储过程调用的复杂类型
调用存储过程时,有时可以使用特定于
数据库。为了适应这些类型,Spring 提供了一个SqlReturnType
用于处理它们从存储过程调用返回时,以及SqlTypeValue
当他们
作为参数传递给存储过程。
这SqlReturnType
接口有一个方法(名为getTypeValue
) 必须是
实现。此接口用作声明的一部分SqlOutParameter
.
以下示例显示返回java.sql.Struct
用户的对象
声明的类型ITEM_TYPE
:
-
Java
-
Kotlin
import java.sql.CallableStatement;
import java.sql.Struct;
import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.object.StoredProcedure;
public class TestItemStoredProcedure extends StoredProcedure {
public TestItemStoredProcedure(DataSource dataSource) {
super(dataSource, "get_item");
declareParameter(new SqlOutParameter("item", Types.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;
}));
// ...
}
}
import org.springframework.jdbc.core.SqlOutParameter
import org.springframework.jdbc.`object`.StoredProcedure
import java.sql.CallableStatement
import java.sql.Struct
import java.sql.Types
import java.util.Date
import javax.sql.DataSource
class TestItemStoredProcedure(dataSource: DataSource) : StoredProcedure(dataSource, "get_item") {
init {
declareParameter(SqlOutParameter("item",Types.STRUCT,"ITEM_TYPE") {
cs: CallableStatement, colIndx: Int, _: Int, _: String? ->
val struct = cs.getObject(colIndx) as Struct
val attr = struct.attributes
TestItem(
(attr[0] as Number).toLong(),
attr[1] as String,
attr[2] as Date
)
})
// ...
}
}
您可以使用SqlTypeValue
传递 Java 对象的值(例如TestItem
) 设置为
存储过程。这SqlTypeValue
接口有一个方法(名为createTypeValue
) 您必须实施。活动连接传入,并且您
可以使用它来创建特定于数据库的对象,例如java.sql.Struct
实例
或java.sql.Array
实例。以下示例创建了一个java.sql.Struct
实例:
-
Java
-
Kotlin
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 connection, int sqlType, String typeName) throws SQLException {
Object[] item = new Object[] { testItem.getId(), testItem.getDescription(),
new java.sql.Date(testItem.getExpirationDate().getTime()) };
return connection.createStruct(typeName, item);
}
};
val testItem = TestItem(123L, "A test item",
SimpleDateFormat("yyyy-M-d").parse("2010-12-31"))
val value = object : AbstractSqlTypeValue() {
override fun createTypeValue(connection: Connection, sqlType: Int, typeName: String?): Any {
val item = arrayOf<Any>(testItem.id, testItem.description,
Date(testItem.expirationDate.time))
return connection.createStruct(typeName, item)
}
}
您现在可以添加此SqlTypeValue
到Map
包含execute
调用存储过程。
的另一个用途SqlTypeValue
将值数组传递给存储的 Oracle
程序。Oracle 有一个createOracleArray
方法OracleConnection
你可以
通过打开它来访问它。您可以使用SqlTypeValue
创建数组并填充
it 与 Java 中的值java.sql.Array
,如以下示例所示:
-
Java
-
Kotlin
Long[] ids = new Long[] {1L, 2L};
SqlTypeValue value = new AbstractSqlTypeValue() {
protected Object createTypeValue(Connection conn, int sqlType, String typeName) throws SQLException {
return conn.unwrap(OracleConnection.class).createOracleArray(typeName, ids);
}
};
val ids = arrayOf(1L, 2L)
val value: SqlTypeValue = object : AbstractSqlTypeValue() {
override fun createTypeValue(conn: Connection, sqlType: Int, typeName: String?): Any {
return conn.unwrap(OracleConnection::class.java).createOracleArray(typeName, ids)
}
}