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

简化JDBC作,使用SimpleJdbc

SimpleJdbc插入SimpleJdbcCall类提供了简化的配置 通过利用可通过JDBC驱动检索的数据库元数据。 这意味着你一开始需要配置的空间会减少,虽然你可以覆盖或关闭 如果你愿意在代码中提供所有细节,那就是元数据处理。spring-doc.cadn.net.cn

通过使用SimpleJdbc插入

我们首先从看SimpleJdbc插入具有最小 配置选项。你应该实例化SimpleJdbc插入在数据访问中 图层的初始化方法。对于这个例子,初始化方法是setDataSource方法。你不需要对SimpleJdbc插入类。相反 你可以创建一个新实例,并通过使用withTableName方法。 该类的配置方法如下:流体返回实例的样式 关于SimpleJdbc插入,这允许你串联所有配置方法。如下 示例仅使用一种配置方法(稍后我们展示多种方法的示例):spring-doc.cadn.net.cn

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
}

执行这里用的方法是一个平板java.util.Map作为唯一的参数。这 这里需要注意的是,用于地图必须与列匹配 表的名称,均为数据库中定义的。这是因为我们读取元数据 构造实际插入语句。spring-doc.cadn.net.cn

通过以下方式获取自动生成密钥SimpleJdbc插入

下一个例子使用了与前例相同的插入,但不是通过身份证它 检索自动生成的密钥并将其置于演员对象。当它创造时 这SimpleJdbc插入除了指定表的名称外,它还指定了 生成的密钥列使用生成键列方法。如下 列表展示了其运作方式:spring-doc.cadn.net.cn

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.lang.Number你可以用该对象创建一个数值类型的实例,使得 在你的领域类中使用。你不能指望所有数据库都能返回特定的 Java 版本 这里是同学。java.lang.Number是你可以依赖的基础类。如果你有 多个自动生成的列,或者生成的值是非数字的,你可以 使用一个钥匙持有者该 返回于执行并返回密钥持有者方法。spring-doc.cadn.net.cn

为 a 指定列SimpleJdbc插入

你可以通过指定列名列表来限制插入的列数,并使用使用Columns如下例所示:spring-doc.cadn.net.cn

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
}

插入的执行方式与依赖元数据来确定是一样的 使用哪些列。spring-doc.cadn.net.cn

SqlParameterSource以提供参数值

使用地图提供参数值可以,但并不方便 使用类别。Spring 提供了几种实现方式SqlParameterSource你可以用来代替的界面。第一个是BeanPropertySqlParameterSource, 如果你有一个兼容 JavaBean 的类,且包含以下内容,这个类非常方便。 你的价值观。它使用对应的 getter 方法来提取参数 值。以下示例展示了如何使用BeanPropertySqlParameterSource:spring-doc.cadn.net.cn

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
}

另一种选择是MapSql参数源类似于地图但提供了更多 方便加值可以串联的方法。以下示例展示了如何使用它:spring-doc.cadn.net.cn

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
}

如你所见,配置是一样的。只有执行代码需要更改为 使用这些替代输入类。spring-doc.cadn.net.cn

调用 的存储过程SimpleJdbcCall

SimpleJdbcCall类使用数据库中的元数据查找参数,这样你就不必显式声明它们。您可以 如果你喜欢声明参数,或者你有不需要的参数,可以声明 有自动映射到 Java 类。第一个例子展示了一个简单的程序 该函数仅返回标量值瓦尔查尔日期格式来自MySQL数据库。 示例过程读取指定的演员条目并返回first_name,last_namebirth_date参数。如下 列表展示了第一个例子:spring-doc.cadn.net.cn

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;

in_id参数包含身份证你正在查找的演员。这参数返回从表中读取的数据。spring-doc.cadn.net.cn

你可以申报SimpleJdbcCall类似于宣告SimpleJdbc插入.你 应该在你的数据访问初始化方法中实例化并配置该类 层。与存储过程类,你不需要创建子类 你也不需要声明可以在数据库元数据中查找的参数。 以下示例为SimpleJdbcCall配置使用之前存储的 程序(唯一的配置选项,除了数据来源,是名称 存储过程的记录):spring-doc.cadn.net.cn

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
}

你为执行调用编写的代码涉及创建一个SqlParameterSource包含IN参数。你必须匹配输入值的名称 与存储过程中声明的参数名称。该案不包含 匹配是因为你用元数据来决定数据库对象的引用方式 在存储过程中。存储过程源代码中指定的内容则不然 必然是数据库中存储方式。有些数据库会将名称转换为所有 而其他则使用小写或按指定大小写。spring-doc.cadn.net.cn

执行方法取IN参数并返回地图包含任意参数由存储过程指定的名字键入。在这种情况下,他们是out_first_name,out_last_nameout_birth_date.spring-doc.cadn.net.cn

最后一部分执行该方法产生演员实例 以返回 数据检索。同样,使用名称非常重要参数 在存储过程中声明。此外,名称中的情况结果映射中存储的参数与参数名称 数据库,不同数据库之间可能有所不同。为了让你的代码更便携,你应该这样做 进行大小写不相关的查找,或者指示 Spring 使用LinkedCaseInsensitiveMap. 如果是后者,你可以自己创建Jdbc模板并设置setResultsMapCaseInsensitive属性到true.然后你可以通过这个定制化Jdbc模板实例化 你的构造者SimpleJdbcCall.以下示例展示了该配置:spring-doc.cadn.net.cn

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
}

采取此举可以避免姓名大小写的冲突 返回参数。spring-doc.cadn.net.cn

明确声明用于 的参数SimpleJdbcCall

本章早些时候,我们描述了如何从元数据推导出参数,但你可以声明它们 如果你愿意,可以明确地说。你可以通过创建和配置来实现SimpleJdbcCall跟 这声明参数该方法取一个可变数量的SqlParameter对象 作为输入。关于如何定义SqlParameter.spring-doc.cadn.net.cn

如果你使用的数据库不是 Spring 支持的,显式声明是必要的 数据库。目前,Spring 支持对存储过程调用的元数据查找 以下数据库包括:Apache Derby、DB2、MySQL、Microsoft SQL Server、Oracle 和 Sybase。 我们还支持MySQL、Microsoft SQL Server的存储函数元数据查找, 以及神谕者。

你可以选择明确声明其中一个、部分或全部参数。参数 在未明确声明参数的情况下,元数据仍会被使用。绕过所有 处理元数据查找,以查找潜在参数,并仅使用声明的 参数,你可以调用withoutProcedureColumnMetaDataAccess作为 声明。假设你为 数据库功能。在这种情况下,你会打电话useInParameterNames指定列表 包含给定签名的IN参数名称。spring-doc.cadn.net.cn

以下示例展示了一个完全声明的过程调用,并使用了以下信息 上述例子:spring-doc.cadn.net.cn

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
}

这两个例子的执行和最终结果是相同的。第二个例子指定了所有 具体细节,而不是依赖元数据。spring-doc.cadn.net.cn

如何定义SqlParameters

定义一个参数SimpleJdbc以及用于关系数据库管理系统的作 你可以使用的类(在建模JDBC作中以Java对象形式进行介绍)SqlParameter或者其子类之一。 为此,通常你在构造函数中指定参数名称和SQL类型。SQL 类型 通过使用java.sql.类型常数。在本章早些时候,我们看到了声明 类似于以下内容:spring-doc.cadn.net.cn

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

第一行SqlParameter声明一个IN参数。你可以用IN参数 对于存储过程调用和通过使用SqlQuery以及其 子类(涵盖于理解SqlQuery).spring-doc.cadn.net.cn

第二行(带有SqlOutParameter) 宣称参数用于 存储过程调用。还有一个SqlInOutParameter进攻参数 (为过程提供IN值且返回值的参数)。spring-doc.cadn.net.cn

仅有参数声明为SqlParameterSqlInOutParameter习惯于 提供输入值。这与存储过程类,其中(对于 向后兼容性原因)允许为参数提供输入值 被宣布为SqlOutParameter.

对于IN参数,除了名称和SQL类型外,你可以指定一个缩放 数字数据或自定义数据库类型的类型名称。为参数,你可以 提供一个行图仪用于处理从A返回的行的映射裁判光标。另一个 选项是指定一个SqlReturnType这为定义提供了机会 对返回值进行定制处理。spring-doc.cadn.net.cn

通过以下方式调用存储函数SimpleJdbcCall

你可以调用存储函数,方式几乎和调用存储过程一样,但 你提供函数名而不是过程名。你用的是withFunctionName方法作为配置的一部分,表示你想做的 调用函数,生成相应的函数调用字符串。一个 专门呼叫(执行函数)用于运行该函数,且 函数返回值为指定类型的对象,这意味着你会 无需从结果映射中检索返回值。类似的便利方法 (名为执行对象)也适用于只有一个的存储过程参数。以下示例(针对 MySQL)基于一个名为get_actor_name返回演员的全名:spring-doc.cadn.net.cn

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在初始化方法中, 如下示例所示:spring-doc.cadn.net.cn

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
}

执行函数所用方法返回字符串包含 的返回值来自 函数调用。spring-doc.cadn.net.cn

返回结果集或从SimpleJdbcCall

调用返回结果集的存储过程或函数有点棘手。一些 数据库在 JDBC 结果处理过程中返回结果集,而其他数据库则需要 明确注册特定类型的参数。这两种方法都需要 额外的处理环绕结果集并处理返回的行。跟 这SimpleJdbcCall,你可以使用returningResultSet方法并声明一个行图仪用于特定参数的实现。如果结果集为 在结果处理过程中返回时,没有定义名称,因此返回 结果必须与你声明行图仪实现。指定的名称仍用于存储处理后的结果列表 在返回的结果映射中执行陈述。spring-doc.cadn.net.cn

下一个例子(针对MySQL)使用了一个存储过程,不取IN参数并返回 所有行来自t_actor桌子:spring-doc.cadn.net.cn

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

要调用此过程,你可以声明行图仪.因为你想去的课程 要遵循 JavaBean 规则,你可以使用BeanPropertyRowMapper由 创建 通过映射到的需要的类新实例方法。 以下示例展示了如何实现:spring-doc.cadn.net.cn

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
}

执行呼叫在空时传递地图,因为该调用不涉及任何参数。 然后从结果映射中检索演员列表并返回给调用者。spring-doc.cadn.net.cn