对于最新稳定版本,请使用Spring Framework 7.0.1spring-doc.cadn.net.cn

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

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

为参数提供SQL类型信息

通常,Spring 会根据参数类型确定参数的 SQL 类型 通过了。在设置时可以明确提供使用的SQL类型 参数值。这有时是正确设置所必需的值。spring-doc.cadn.net.cn

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

  • 许多更新和查询方法的Jdbc模板取一个额外的参数 一个形式智力数组。该数组用于表示 对应参数,使用来自java.sql.类型类。提供 每个参数都有一个条目。spring-doc.cadn.net.cn

  • 你可以使用SqlParameterValue类来包裹需要此处理的参数值 补充信息。为此,为每个值创建一个新实例,并传递SQL类型 以及构造子中的参数值。你也可以提供可选的体重秤 参数用于数值。spring-doc.cadn.net.cn

  • 对于使用命名参数的方法,你可以使用SqlParameterSourceBeanPropertySqlParameterSourceMapSql参数源.它们都有自己的方法 用于注册任意指定参数值的SQL类型。spring-doc.cadn.net.cn

处理 BLOB 和 CLOB 对象

你可以在数据库中存储图片、其他二进制数据和大块文本。这些 大型对象称为BLOB(二进制大OBject)表示二进制数据,CLOBs(字符) 大OBject)用于字符数据。在春季,你可以通过以下方式处理这些大型物体 这Jdbc模板直接使用RDBMS提供的更高抽象时也是如此 对象和SimpleJdbc类。所有这些方法都采用以下实现 这LobHandler用于实际管理 LOB(大型 OBject)数据的接口。LobHandler提供访问LobCreator类,通过getLobCreator方法 用于创建新的LOB对象以插入。spring-doc.cadn.net.cn

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

下一个示例展示了如何创建和插入一个BLOB。之后我们会展示如何阅读 从数据库里回来了。spring-doc.cadn.net.cn

这个例子使用了一个Jdbc模板以及摘要Lob创建准备语句回拨.它实现了一种方法,集合值.该方法提供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,setClobAsciiStreamsetClobAsCharacterStream方法LobCreator返回DefaultLobHandler.getLobCreator(),你可以选择性地指定一个负值对于内容长度论点。 如果指定的内容长度为负,则DefaultLobHandler使用 JDBC 4.0 版本的集合流方法,但没有长度参数。否则,它会将指定的长度传递给驱动程序。spring-doc.cadn.net.cn

请参阅你使用的JDBC驱动文档,以验证它是否支持流式传输一个LOB,但不提供内容长度。spring-doc.cadn.net.cn

现在是时候从数据库读取LOB数据了。同样,你使用了一个Jdbc模板使用相同的实例变量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标准允许基于包含变量列表的值列表的表达式选择行。一个典型的例子是从T_ACTOR中选择* 其中 ID in (1, 2, 3). 该变量列表不直接支持预备语句JDBC 标准。你不能声明可变数量的占位符。你需要一个若干带有所需占位符数量的变体,或者你需要生成一旦知道需要多少占位符,就需要动态生成SQL字符串。命名参数支持在NamedParameterJdbcTemplate采用后一种方法。你可以将这些值传递为java.util.List(或者任何可迭代)的简单值。该列表用于在实际SQL语句中插入所需的占位符并在语句执行时传递这些值。spring-doc.cadn.net.cn

传递多个值时要小心。JDBC标准并不保证你可以使用超过100个值来表示表达式列表。各种数据库都超过这个数字,但通常对允许的值数有硬性限制。例如,Oracle 的限制是 1000。

除了值列表中的原始值外,你还可以创建java.util.List对象数组的表达式。该列表可以支持为从句,例如:从T_ACTOR中选择*,其中(id, last_name) in (((1, 'Johnson'), (2,'Harrop')). 当然,这需要你的数据库支持这种语法。spring-doc.cadn.net.cn

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

当你调用存储过程时,有时可以使用针对 数据库。 为了适应这些类型,Spring 提供了SqlReturnType用于处理当它们从存储过程调用返回时SqlTypeValue当它们作为参数传递到存储过程时。spring-doc.cadn.net.cn

SqlReturnType接口只有一个方法(称为getTypeValue) 必须是 实现。 该接口作为声明的一部分SqlOutParameter. 以下示例展示了返回神谕值的方法结构用户对象声明类型ITEM_TYPE:spring-doc.cadn.net.cn

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

你可以使用SqlTypeValue传递 Java 对象的值(例如测试项目)到一个存储过程。 这SqlTypeValue接口只有一个方法(称为createTypeValue你必须实现。激活连接会被传递,你可以用它来创建数据库特定的对象,例如结构描述符实例 或ArrayDescriptor实例。 以下示例可生成结构描述符实例:spring-doc.cadn.net.cn

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前往地图包含执行存储过程调用。spring-doc.cadn.net.cn

另一种用途SqlTypeValue是将数组值传递给存储的神谕者 程序。 Oracle 有自己的内部数组在这种情况下必须使用该类,且你可以使用SqlTypeValue创建神谕实例数组并填充Java 的值数组,如下示例所示:spring-doc.cadn.net.cn

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