and 类提供了简化的配置 利用可通过 JDBC 驱动程序检索的数据库元数据。 这意味着,尽管您可以覆盖或关闭,但前期配置的次数更少 元数据处理(如果您希望在代码中提供所有详细信息)。SimpleJdbcInsertSimpleJdbcCall

使用SimpleJdbcInsert

我们首先查看具有最少数量的类 配置选项。您应该实例化数据访问 层的初始化方法。在此示例中,初始化方法是方法。您不需要对类进行子类化。相反 您可以使用该方法创建新实例并设置表名。 此类的配置方法遵循返回实例的样式 的 ,它允许您链接所有配置方法。以下 示例仅使用一种配置方法(稍后我们将展示多种方法的示例):SimpleJdbcInsertSimpleJdbcInsertsetDataSourceSimpleJdbcInsertwithTableNamefluidSimpleJdbcInsert

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcInsert insertActor;

	public void setDataSource(DataSource dataSource) {
		this.insertActor = new SimpleJdbcInsert(dataSource).withTableName("t_actor");
	}

	public void add(Actor actor) {
		Map<String, Object> parameters = new HashMap<>(3);
		parameters.put("id", actor.getId());
		parameters.put("first_name", actor.getFirstName());
		parameters.put("last_name", actor.getLastName());
		insertActor.execute(parameters);
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val insertActor = SimpleJdbcInsert(dataSource).withTableName("t_actor")

	fun add(actor: Actor) {
		val parameters = mutableMapOf<String, Any>()
		parameters["id"] = actor.id
		parameters["first_name"] = actor.firstName
		parameters["last_name"] = actor.lastName
		insertActor.execute(parameters)
	}

	// ... additional methods
}

这里使用的方法将平原作为其唯一参数。这 这里需要注意的重要一点是,用于 必须与列匹配的键 表的名称,在数据库中定义。这是因为我们读取了元数据 构造实际的 insert 语句。executejava.util.MapMap

使用 检索自动生成的密钥SimpleJdbcInsert

下一个示例使用与上一个示例相同的插入,但不是传入 ,而是 检索自动生成的密钥并将其设置在新对象上。当它创建时 除了指定表名外,它还指定了名称 生成的键列与方法。以下 列表显示了它是如何工作的:idActorSimpleJdbcInsertusingGeneratedKeyColumns

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcInsert insertActor;

	public void setDataSource(DataSource dataSource) {
		this.insertActor = new SimpleJdbcInsert(dataSource)
				.withTableName("t_actor")
				.usingGeneratedKeyColumns("id");
	}

	public void add(Actor actor) {
		Map<String, Object> parameters = new HashMap<>(2);
		parameters.put("first_name", actor.getFirstName());
		parameters.put("last_name", actor.getLastName());
		Number newId = insertActor.executeAndReturnKey(parameters);
		actor.setId(newId.longValue());
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val insertActor = SimpleJdbcInsert(dataSource)
			.withTableName("t_actor").usingGeneratedKeyColumns("id")

	fun add(actor: Actor): Actor {
		val parameters = mapOf(
				"first_name" to actor.firstName,
				"last_name" to actor.lastName)
		val newId = insertActor.executeAndReturnKey(parameters);
		return actor.copy(id = newId.toLong())
	}

	// ... additional methods
}

使用第二种方法运行插入时的主要区别在于,您不这样做 将 添加到 中,然后调用该方法。这将返回一个对象,您可以使用该对象创建数值类型的实例 在域类中使用。不能依赖所有数据库来返回特定的 Java 这里的类。 是可以依赖的基类。如果您有 多个自动生成的列或生成的值为非数值,可以 使用从该方法返回的 A。idMapexecuteAndReturnKeyjava.lang.Numberjava.lang.NumberKeyHolderexecuteAndReturnKeyHolder

指定SimpleJdbcInsert

可以通过使用该方法指定列名列表来限制插入的列,如以下示例所示:usingColumns

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcInsert insertActor;

	public void setDataSource(DataSource dataSource) {
		this.insertActor = new SimpleJdbcInsert(dataSource)
				.withTableName("t_actor")
				.usingColumns("first_name", "last_name")
				.usingGeneratedKeyColumns("id");
	}

	public void add(Actor actor) {
		Map<String, Object> parameters = new HashMap<>(2);
		parameters.put("first_name", actor.getFirstName());
		parameters.put("last_name", actor.getLastName());
		Number newId = insertActor.executeAndReturnKey(parameters);
		actor.setId(newId.longValue());
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val insertActor = SimpleJdbcInsert(dataSource)
			.withTableName("t_actor")
			.usingColumns("first_name", "last_name")
			.usingGeneratedKeyColumns("id")

	fun add(actor: Actor): Actor {
		val parameters = mapOf(
				"first_name" to actor.firstName,
				"last_name" to actor.lastName)
		val newId = insertActor.executeAndReturnKey(parameters);
		return actor.copy(id = newId.toLong())
	}

	// ... additional methods
}

插入的执行与依赖元数据来确定的执行方式相同 要使用的列。

用于提供参数值SqlParameterSource

使用 a 提供参数值可以正常工作,但这不是最方便的 要使用的类。Spring 提供了几个接口的实现,你可以改用它们。第一个是, 如果您有一个符合 JavaBean 的类,并且包含 您的价值观。它使用相应的 getter 方法来提取参数 值。以下示例演示如何使用:MapSqlParameterSourceBeanPropertySqlParameterSourceBeanPropertySqlParameterSource

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcInsert insertActor;

	public void setDataSource(DataSource dataSource) {
		this.insertActor = new SimpleJdbcInsert(dataSource)
				.withTableName("t_actor")
				.usingGeneratedKeyColumns("id");
	}

	public void add(Actor actor) {
		SqlParameterSource parameters = new BeanPropertySqlParameterSource(actor);
		Number newId = insertActor.executeAndReturnKey(parameters);
		actor.setId(newId.longValue());
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val insertActor = SimpleJdbcInsert(dataSource)
			.withTableName("t_actor")
			.usingGeneratedKeyColumns("id")

	fun add(actor: Actor): Actor {
		val parameters = BeanPropertySqlParameterSource(actor)
		val newId = insertActor.executeAndReturnKey(parameters)
		return actor.copy(id = newId.toLong())
	}

	// ... additional methods
}

另一种选择是类似于 a 但提供更 可以链接的方便方法。以下示例演示如何使用它:MapSqlParameterSourceMapaddValue

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcInsert insertActor;

	public void setDataSource(DataSource dataSource) {
		this.insertActor = new SimpleJdbcInsert(dataSource)
				.withTableName("t_actor")
				.usingGeneratedKeyColumns("id");
	}

	public void add(Actor actor) {
		SqlParameterSource parameters = new MapSqlParameterSource()
				.addValue("first_name", actor.getFirstName())
				.addValue("last_name", actor.getLastName());
		Number newId = insertActor.executeAndReturnKey(parameters);
		actor.setId(newId.longValue());
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val insertActor = SimpleJdbcInsert(dataSource)
			.withTableName("t_actor")
			.usingGeneratedKeyColumns("id")

	fun add(actor: Actor): Actor {
		val parameters = MapSqlParameterSource()
					.addValue("first_name", actor.firstName)
					.addValue("last_name", actor.lastName)
		val newId = insertActor.executeAndReturnKey(parameters)
		return actor.copy(id = newId.toLong())
	}

	// ... additional methods
}

如您所见,配置是相同的。只有执行代码必须更改为 使用这些替代输入类。

使用SimpleJdbcCall

该类使用数据库中的元数据来查找 和 参数的名称,这样您就不必显式声明它们。您可以 如果您愿意这样做,或者如果您的参数(如 或)没有自动映射到 Java 类,请声明参数。第一个例子 显示一个简单的过程,该过程仅返回 AND 格式的标量值 来自 MySQL 数据库。示例过程读取指定的 actor 条目,并以参数的形式返回 、 和列。 以下列表显示了第一个示例:SimpleJdbcCallinoutARRAYSTRUCTVARCHARDATEfirst_namelast_namebirth_dateout

CREATE PROCEDURE read_actor (
	IN in_id INTEGER,
	OUT out_first_name VARCHAR(100),
	OUT out_last_name VARCHAR(100),
	OUT out_birth_date DATE)
BEGIN
	SELECT first_name, last_name, birth_date
	INTO out_first_name, out_last_name, out_birth_date
	FROM t_actor where id = in_id;
END;

该参数包含您正在查找的 actor 的参数。参数返回从表中读取的数据。in_ididout

您可以采用类似于声明 的方式进行声明。你 应该在数据访问的初始化方法中实例化和配置类 层。与类相比,您不需要创建子类 并且无需声明可在数据库元数据中查找的参数。 以下配置示例使用前面存储的 过程(除了 之外,唯一的配置选项是名称 存储过程):SimpleJdbcCallSimpleJdbcInsertStoredProcedureSimpleJdbcCallDataSource

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcCall procReadActor;

	public void setDataSource(DataSource dataSource) {
		this.procReadActor = new SimpleJdbcCall(dataSource)
				.withProcedureName("read_actor");
	}

	public Actor readActor(Long id) {
		SqlParameterSource in = new MapSqlParameterSource()
				.addValue("in_id", id);
		Map out = procReadActor.execute(in);
		Actor actor = new Actor();
		actor.setId(id);
		actor.setFirstName((String) out.get("out_first_name"));
		actor.setLastName((String) out.get("out_last_name"));
		actor.setBirthDate((Date) out.get("out_birth_date"));
		return actor;
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val procReadActor = SimpleJdbcCall(dataSource)
			.withProcedureName("read_actor")


	fun readActor(id: Long): Actor {
		val source = MapSqlParameterSource().addValue("in_id", id)
		val output = procReadActor.execute(source)
		return Actor(
				id,
				output["out_first_name"] as String,
				output["out_last_name"] as String,
				output["out_birth_date"] as Date)
	}

		// ... additional methods
}

为执行调用编写的代码涉及创建包含 IN 参数的代码。您必须与为输入值提供的名称匹配 替换为在存储过程中声明的参数名称。案例没有 匹配,因为您使用元数据来确定应如何引用数据库对象 在存储过程中。在存储过程的源中指定的内容不是 必然是它在数据库中的存储方式。某些数据库将名称转换为所有 大写,而其他人则使用小写或按指定使用大小写。SqlParameterSource

该方法采用 IN 参数并返回一个,其中包含按名称键控的任何参数,如存储过程中所指定的那样。在本例中,它们是 、 和 。executeMapoutout_first_nameout_last_nameout_birth_date

该方法的最后一部分创建一个实例,用于返回 检索到的数据。同样,使用参数的名称很重要,因为它们 在存储过程中声明。此外,结果映射中存储的参数名称的大小写与结果映射中的参数名称的大小写匹配 数据库,这可能因数据库而异。为了使代码更具可移植性,您应该 执行不区分大小写的查找或指示 Spring 使用 . 若要执行后者,可以创建自己的属性并将属性设置为 。然后,您可以将此自定义实例传递到 的构造函数 .以下示例演示了此配置:executeActoroutoutoutLinkedCaseInsensitiveMapJdbcTemplatesetResultsMapCaseInsensitivetrueJdbcTemplateSimpleJdbcCall

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcCall procReadActor;

	public void setDataSource(DataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		jdbcTemplate.setResultsMapCaseInsensitive(true);
		this.procReadActor = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName("read_actor");
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private var procReadActor = SimpleJdbcCall(JdbcTemplate(dataSource).apply {
		isResultsMapCaseInsensitive = true
	}).withProcedureName("read_actor")

	// ... additional methods
}

通过执行此操作,您可以避免在用于 返回的参数。out

显式声明要用于SimpleJdbcCall

在本章前面,我们介绍了如何从元数据中推断参数,但您可以声明它们 如果你愿意,可以明确地。您可以通过创建和配置 该方法,它接受可变数量的对象 作为输入。有关如何定义 .SimpleJdbcCalldeclareParametersSqlParameterSqlParameter

如果您使用的数据库不是 Spring 支持的数据库,则需要显式声明 数据库。目前,Spring 支持对存储过程调用进行元数据查找 以下数据库:Apache Derby、DB2、MySQL、Microsoft SQL Server、Oracle 和 Sybase。 我们还支持MySQL,Microsoft SQL Server,存储函数的元数据查找。 和 Oracle。

您可以选择显式声明一个、部分或所有参数。参数 在未显式声明参数的情况下,仍会使用元数据。绕过所有 处理潜在参数的元数据查找,并仅使用声明的 参数,您可以调用该方法作为 声明。假设您为 数据库函数。在这种情况下,您调用以指定列表 要为给定签名包含的 IN 参数名称。withoutProcedureColumnMetaDataAccessuseInParameterNames

下面的示例演示一个完全声明的过程调用,并使用来自 前面的示例:

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcCall procReadActor;

	public void setDataSource(DataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		jdbcTemplate.setResultsMapCaseInsensitive(true);
		this.procReadActor = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName("read_actor")
				.withoutProcedureColumnMetaDataAccess()
				.useInParameterNames("in_id")
				.declareParameters(
						new SqlParameter("in_id", Types.NUMERIC),
						new SqlOutParameter("out_first_name", Types.VARCHAR),
						new SqlOutParameter("out_last_name", Types.VARCHAR),
						new SqlOutParameter("out_birth_date", Types.DATE)
				);
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

		private val procReadActor = SimpleJdbcCall(JdbcTemplate(dataSource).apply {
			isResultsMapCaseInsensitive = true
		}).withProcedureName("read_actor")
				.withoutProcedureColumnMetaDataAccess()
				.useInParameterNames("in_id")
				.declareParameters(
						SqlParameter("in_id", Types.NUMERIC),
						SqlOutParameter("out_first_name", Types.VARCHAR),
						SqlOutParameter("out_last_name", Types.VARCHAR),
						SqlOutParameter("out_birth_date", Types.DATE)
	)

		// ... additional methods
}

这两个示例的执行和最终结果是相同的。第二个示例指定所有 显式细节,而不是依赖于元数据。

如何定义SqlParameters

为类和 RDBMS 操作定义参数 可以使用的类(在将 JDBC 操作建模为 Java 对象中介绍)或其子类之一。 为此,通常在构造函数中指定参数名称和 SQL 类型。SQL 类型 通过使用常量指定。在本章的前面,我们看到了声明 类似于以下内容:SimpleJdbcSqlParameterjava.sql.Types

  • Java

  • Kotlin

new SqlParameter("in_id", Types.NUMERIC),
new SqlOutParameter("out_first_name", Types.VARCHAR),
SqlParameter("in_id", Types.NUMERIC),
SqlOutParameter("out_first_name", Types.VARCHAR),

带有 的第一行声明 IN 参数。可以使用 IN 参数 对于存储过程调用和查询,使用 和 其 子类(在了解 SqlQuery 中介绍)。SqlParameterSqlQuery

第二行(带有 )声明要在 存储过程调用。还有一个 for 参数 (为过程提供 IN 值并返回值的参数)。SqlOutParameteroutSqlInOutParameterInOut

仅声明为 和 的参数用于 提供输入值。这与类不同,该类(对于 向后兼容原因)允许为参数提供输入值 声明为 .SqlParameterSqlInOutParameterStoredProcedureSqlOutParameter

对于 IN 参数,除了名称和 SQL 类型之外,还可以指定 数值数据或自定义数据库类型的类型名称。对于参数,您可以 提供 用于处理从游标返回的行的映射。另一个 选项是指定一个提供定义机会的机会 返回值的自定义处理。outRowMapperREFSqlReturnType

使用SimpleJdbcCall

调用存储函数的方式与调用存储过程的方式几乎相同,但 提供函数名称而不是过程名称。使用该方法作为配置的一部分来指示要使 对函数的调用,并生成函数调用的相应字符串。一个 专门调用 () 用于运行函数,它 将函数返回值作为指定类型的对象,这意味着您执行 不必从结果映射中检索返回值。类似的便利方法 (named ) 也可用于只有一个参数的存储过程。以下示例(对于 MySQL)基于一个名为 Actor 全名的存储函数:withFunctionNameexecuteFunctionexecuteObjectoutget_actor_name

CREATE FUNCTION get_actor_name (in_id INTEGER)
RETURNS VARCHAR(200) READS SQL DATA
BEGIN
	DECLARE out_name VARCHAR(200);
	SELECT concat(first_name, ' ', last_name)
		INTO out_name
		FROM t_actor where id = in_id;
	RETURN out_name;
END;

为了调用这个函数,我们再次在初始化方法中创建一个, 如以下示例所示:SimpleJdbcCall

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcCall funcGetActorName;

	public void setDataSource(DataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		jdbcTemplate.setResultsMapCaseInsensitive(true);
		this.funcGetActorName = new SimpleJdbcCall(jdbcTemplate)
				.withFunctionName("get_actor_name");
	}

	public String getActorName(Long id) {
		SqlParameterSource in = new MapSqlParameterSource()
				.addValue("in_id", id);
		String name = funcGetActorName.executeFunction(String.class, in);
		return name;
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

	private val jdbcTemplate = JdbcTemplate(dataSource).apply {
		isResultsMapCaseInsensitive = true
	}
	private val funcGetActorName = SimpleJdbcCall(jdbcTemplate)
			.withFunctionName("get_actor_name")

	fun getActorName(id: Long): String {
		val source = MapSqlParameterSource().addValue("in_id", id)
		return funcGetActorName.executeFunction(String::class.java, source)
	}

	// ... additional methods
}

使用的方法返回 a,其中包含来自 函数调用。executeFunctionString

ResultSetSimpleJdbcCall

调用返回结果集的存储过程或函数有点棘手。一些 数据库在 JDBC 结果处理期间返回结果集,而其他数据库则需要 显式注册的特定类型的参数。这两种方法都需要 用于循环访问结果集并处理返回的行的其他处理。跟 ,您可以使用该方法并声明要用于特定参数的实现。如果结果集是 在结果处理过程中返回,没有定义任何名称,因此返回的 结果必须与声明实现的顺序匹配。指定的名称仍用于存储已处理的结果列表 在从语句返回的结果映射中。outSimpleJdbcCallreturningResultSetRowMapperRowMapperexecute

下一个示例(对于 MySQL)使用不带 IN 参数的存储过程并返回 表中的所有行:t_actor

CREATE PROCEDURE read_all_actors()
BEGIN
 SELECT a.id, a.first_name, a.last_name, a.birth_date FROM t_actor a;
END;

若要调用此过程,可以声明 .因为你想要的类 要映射遵循 JavaBean 规则,可以使用由 传入要映射到的方法中所需的类。 以下示例演示如何执行此操作:RowMapperBeanPropertyRowMappernewInstance

  • Java

  • Kotlin

public class JdbcActorDao implements ActorDao {

	private SimpleJdbcCall procReadAllActors;

	public void setDataSource(DataSource dataSource) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		jdbcTemplate.setResultsMapCaseInsensitive(true);
		this.procReadAllActors = new SimpleJdbcCall(jdbcTemplate)
				.withProcedureName("read_all_actors")
				.returningResultSet("actors",
				BeanPropertyRowMapper.newInstance(Actor.class));
	}

	public List getActorsList() {
		Map m = procReadAllActors.execute(new HashMap<String, Object>(0));
		return (List) m.get("actors");
	}

	// ... additional methods
}
class JdbcActorDao(dataSource: DataSource) : ActorDao {

		private val procReadAllActors = SimpleJdbcCall(JdbcTemplate(dataSource).apply {
			isResultsMapCaseInsensitive = true
		}).withProcedureName("read_all_actors")
				.returningResultSet("actors",
						BeanPropertyRowMapper.newInstance(Actor::class.java))

	fun getActorsList(): List<Actor> {
		val m = procReadAllActors.execute(mapOf<String, Any>())
		return m["actors"] as List<Actor>
	}

	// ... additional methods
}

调用传入空 ,因为此调用不采用任何参数。 然后,从结果映射中检索参与者列表并返回给调用方。executeMap