此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.0spring-doc.cadn.net.cn

参数和数据值处理的常见问题

参数和数据值的常见问题存在于不同的方法中 由 Spring Framework 的 JDBC 支持提供。本节介绍如何解决这些问题。spring-doc.cadn.net.cn

为参数提供 SQL 类型信息

通常,Spring 会根据参数的类型来确定参数的 SQL 类型 传入。可以显式提供在设置 参数值。这有时是正确设置NULL值。spring-doc.cadn.net.cn

您可以通过多种方式提供 SQL 类型信息:spring-doc.cadn.net.cn

  • 许多 update 和 query 方法JdbcTemplate在 其形式为int数组。此数组用于指示 相应的参数,方法是使用java.sql.Types类。提供 每个参数一个条目。spring-doc.cadn.net.cn

  • 您可以使用SqlParameterValueclass 来包装需要这个 其他信息。为此,请为每个值创建一个新实例并传入 SQL 类型 和构造函数中的参数值。您还可以提供可选的比例 参数表示数值。spring-doc.cadn.net.cn

  • 对于使用命名参数的方法,您可以使用SqlParameterSourceBeanPropertySqlParameterSourceMapSqlParameterSource.他们都有方法 用于注册任何命名参数值的 SQL 类型。spring-doc.cadn.net.cn

处理 BLOB 和 CLOB 对象

您可以在数据库中存储图像、其他二进制数据和大块文本。这些 对于二进制数据,大型对象称为 BLOB(二进制大型对象),对于 CLOB(字符 Large OBject) 获取字符数据。在 Spring 中,您可以使用 这JdbcTemplate直接使用,也可以在使用 RDBMS 提供的更高抽象时 Objects 和SimpleJdbc类。所有这些方法都使用 这LobHandler用于实际管理 LOB (Large OBject) 数据的接口。LobHandler提供对LobCreator类,通过getLobCreator方法 用于创建要插入的新 LOB 对象。spring-doc.cadn.net.cn

LobCreatorLobHandler为 LOB 输入和输出提供以下支持:spring-doc.cadn.net.cn

下一个示例演示如何创建和插入 BLOB。稍后我们将介绍如何阅读 it 从数据库返回。spring-doc.cadn.net.cn

此示例使用JdbcTemplate以及AbstractLobCreatingPreparedStatementCallback.它实现了一种方法setValues.此方法提供了一个LobCreator,我们用它来设置 SQL 插入语句中的 LOB 列。spring-doc.cadn.net.cn

在这个例子中,我们假设有一个变量lobHandler,这已经是 设置为DefaultLobHandler.通常通过 依赖注入。spring-doc.cadn.net.cn

以下示例演示如何创建和插入 BLOB:spring-doc.cadn.net.cn

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 的内容。

如果调用setBlobAsBinaryStream,setClobAsAsciiStreamsetClobAsCharacterStream方法上的LobCreator返回自DefaultLobHandler.getLobCreator()中,您可以选择指定负值 对于contentLength论点。如果指定的内容长度为负数,则DefaultLobHandler使用 set-stream 方法的 JDBC 4.0 变体,而不使用 length 参数。否则,它将指定的长度传递给驱动程序。spring-doc.cadn.net.cn

请参阅您所使用的 JDBC 驱动程序的文档,以验证它是否支持流式处理 一个 LOB,但未提供内容长度。spring-doc.cadn.net.cn

现在,可以从数据库中读取 LOB 数据。同样,您使用JdbcTemplate具有相同的实例变量lobHandler和对DefaultLobHandler. 以下示例显示了如何执行此作:spring-doc.cadn.net.cn

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 标准允许根据包含 variable 的值列表。一个典型的例子是select * from T_ACTOR where id in (1, 2, 3).准备好的语句不直接支持此变量列表 JDBC 标准。不能声明可变数量的占位符。您需要一个号码 的变体,并准备好所需数量的占位符,或者您需要生成 SQL 字符串,一旦您知道需要多少个占位符。命名的 参数支持在NamedParameterJdbcTemplate采用后一种方法。 您可以将值作为java.util.List(或任何Iterable) 的简单值。 此列表用于将所需的占位符插入到实际的 SQL 语句中 并在语句执行期间传入值。spring-doc.cadn.net.cn

传入许多值时要小心。JDBC 标准不保证 您可以使用 100 多个值来表示INexpression 列表。各种数据库超过 这个数字,但它们通常对允许的值数量有硬性限制。 例如,Oracle 的限制为 1000。

除了值列表中的基元值外,您还可以创建一个java.util.Listof 对象数组。此列表可以支持为in子句,例如select * from T_ACTOR where (id, last_name) in ((1, 'Johnson'), (2, 'Harrop')).当然,这需要您的数据库支持此语法。spring-doc.cadn.net.cn

处理存储过程调用的复杂类型

调用存储过程时,有时可以使用特定于 数据库。为了适应这些类型, Spring 提供了一个SqlReturnType用于搬运 当它们从存储过程调用返回时,以及SqlTypeValue当他们 作为参数传递给存储过程。spring-doc.cadn.net.cn

SqlReturnTypeinterface 有一个方法(名为getTypeValue),该 实现。此接口用作SqlOutParameter. 以下示例显示了返回java.sql.Struct用户的对象 declared type (声明的类型)ITEM_TYPE:spring-doc.cadn.net.cn

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
				val item = TestItem()
				item.id = (attr[0] as Number).toLong()
				item.description = attr[1] as String
				item.expirationDate = attr[2] as Date
				item
			})
		// ...
	}
}

您可以使用SqlTypeValue要将 Java 对象(例如TestItem) 转换为 stored 过程。这SqlTypeValueinterface 有一个方法(名为createTypeValue) 实现。活动连接传入,您 可以使用它来创建特定于数据库的对象,例如java.sql.Struct实例 或java.sql.Array实例。以下示例创建一个java.sql.Struct实例:spring-doc.cadn.net.cn

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)
	}
}

您现在可以添加此SqlTypeValueMap,其中包含execute调用存储过程。spring-doc.cadn.net.cn

另一个用途SqlTypeValue将值数组传递给 Oracle 存储的 程序。Oracle 有一个createOracleArraymethod 开启OracleConnection你可以 访问。您可以使用SqlTypeValue创建数组并填充 it 替换为 Java 中的java.sql.Array,如下例所示:spring-doc.cadn.net.cn

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)
	}
}