此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13! |
此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13! |
和 类提供了简化的配置
通过利用可通过 JDBC 驱动程序检索的数据库元数据。
这意味着您需要预先配置的更少,尽管您可以覆盖或关闭
元数据处理(如果您希望在代码中提供所有详细信息)。SimpleJdbcInsert
SimpleJdbcCall
使用 插入数据SimpleJdbcInsert
我们首先查看具有最小
配置选项。您应该在数据访问中实例化
图层的初始化方法。对于此示例,初始化方法是 method.您不需要对类进行子类化。相反
您可以通过该方法创建新实例并设置表名。
此类的配置方法遵循返回实例的样式
,它允许您链接所有配置方法。以下内容
example 仅使用一种配置方法(我们稍后将展示多种方法的示例):SimpleJdbcInsert
SimpleJdbcInsert
setDataSource
SimpleJdbcInsert
withTableName
fluid
SimpleJdbcInsert
-
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
}
这里使用的方法将 plain 作为其唯一的参数。这
这里需要注意的重要一点是,用于 Must match the column 的键
表的名称,如数据库中所定义。这是因为我们读取了元数据
构造实际的 INSERT 语句。execute
java.util.Map
Map
使用 检索自动生成的密钥SimpleJdbcInsert
下一个示例使用与上一个示例相同的 insert,但是,它不是传入 , it
检索自动生成的键并将其设置在新对象上。当它创建
的 ,除了指定 table name 之外,它还指定 name
生成的键列。以下内容
清单显示了它是如何工作的:id
Actor
SimpleJdbcInsert
usingGeneratedKeyColumns
-
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
}
使用第二种方法运行 insert 时的主要区别在于,您不会
将 添加到 中,然后调用该方法。这将返回一个对象,您可以使用该对象创建数字类型的实例,该实例
用于 domain 类。您不能依赖所有数据库都返回特定的 Java
类。 是您可以依赖的基类。如果你有
多个自动生成的列或生成的值为非数值,则可以
使用从方法返回的 a。id
Map
executeAndReturnKey
java.lang.Number
java.lang.Number
KeyHolder
executeAndReturnKeyHolder
为 指定列SimpleJdbcInsert
您可以通过使用 method 指定列名列表来限制插入的列,如下例所示: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 方法提取参数
值。以下示例演示如何使用:Map
SqlParameterSource
BeanPropertySqlParameterSource
BeanPropertySqlParameterSource
-
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 但提供更多
可以链接的便捷方法。以下示例演示如何使用它:MapSqlParameterSource
Map
addValue
-
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
}
如您所见,配置是相同的。只有执行代码必须更改为 使用这些替代输入类。
使用 调用 Stored ProcedureSimpleJdbcCall
该类使用数据库中的元数据来查找 的名称和参数,因此您不必显式声明它们。您可以
声明参数(如果您更喜欢这样做,或者您有不这样做的参数)
具有到 Java 类的自动映射。第一个示例显示了一个简单的过程
,它仅返回 MySQL 数据库中的 AND 格式的标量值。
示例过程读取指定的 actor 条目,并以参数的形式返回 、 和 列。以下内容
清单显示了第一个示例:SimpleJdbcCall
in
out
VARCHAR
DATE
first_name
last_name
birth_date
out
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_id
id
out
您可以采用类似于声明 .你
应在 data-access 的 initialization method 中实例化和配置类
层。与类相比,您无需创建子类
并且您无需声明可在数据库元数据中查找的参数。
下面的配置示例使用前面存储的
过程(除了 之外,唯一的配置选项是名称
的存储过程):SimpleJdbcCall
SimpleJdbcInsert
StoredProcedure
SimpleJdbcCall
DataSource
-
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 参数。您必须与为 input 值提供的名称匹配
替换为存储过程中声明的参数名称。case 没有
匹配,因为您使用元数据来确定应如何引用数据库对象
在存储过程中。在存储过程的源中指定的内容不是
必然是它在数据库中的存储方式。一些数据库将名称转换为所有
大写,而其他 Cookie 则使用 Lower Case 或使用指定的大小写。SqlParameterSource
该方法采用 IN 参数并返回 a,其中包含由存储过程中指定的名称键控的任何参数。在本例中,它们是 、 和 。execute
Map
out
out_first_name
out_last_name
out_birth_date
该方法的最后一部分创建一个实例,用于返回
检索到的数据。同样,使用参数的名称很重要,因为它们
在存储过程中声明。此外,结果映射中存储的参数名称的大小写与
数据库,这可能因数据库而异。为了使您的代码更具可移植性,您应该
执行不区分大小写的查找或指示 Spring 使用 .
要执行后者,您可以创建自己的 ID 并将属性设置为 。然后,您可以将此自定义实例传递到
你的 .以下示例显示了此配置:execute
Actor
out
out
out
LinkedCaseInsensitiveMap
JdbcTemplate
setResultsMapCaseInsensitive
true
JdbcTemplate
SimpleJdbcCall
-
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
在本章的前面,我们介绍了如何从元数据中推导出参数,但您可以声明它们
如果你愿意的话,明确地。您可以通过创建和配置
该方法,它接受可变数量的对象
作为输入。有关如何定义 .SimpleJdbcCall
declareParameters
SqlParameter
SqlParameter
如果您使用的数据库不支持 Spring,则显式声明是必需的 数据库。目前, Spring 支持对 以下数据库:Apache Derby、DB2、MySQL、Microsoft SQL Server、Oracle 和 Sybase。 我们还支持 MySQL、Microsoft SQL Server、 和 Oracle。 |
您可以选择显式声明一个、部分或所有参数。参数
在未显式声明参数的情况下,仍使用 metadata。要绕过所有
处理元数据查找潜在参数,并仅使用声明的
参数,您可以将该方法作为
声明。假设您为
database 函数。在这种情况下,您可以调用以指定列表
要为给定签名包含的 IN 参数名称。withoutProcedureColumnMetaDataAccess
useInParameterNames
以下示例显示了一个完全声明的过程调用,并使用 前面的示例:
-
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
}
两个示例的执行和最终结果是相同的。第二个示例指定所有 details 而不是依赖元数据。
如果您使用的数据库不支持 Spring,则显式声明是必需的 数据库。目前, Spring 支持对 以下数据库:Apache Derby、DB2、MySQL、Microsoft SQL Server、Oracle 和 Sybase。 我们还支持 MySQL、Microsoft SQL Server、 和 Oracle。 |
如何定义SqlParameters
为类和 RDBMS 操作定义参数
类(在 将 JDBC 操作建模为 Java 对象中介绍)或其子类之一。
为此,您通常在构造函数中指定参数名称和 SQL 类型。SQL 类型
是使用常量指定的。在本章的前面,我们看到了声明
类似于以下内容:SimpleJdbc
SqlParameter
java.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),
带有 the 的第一行声明一个 IN 参数。您可以使用 IN 参数
对于存储过程调用和查询,使用 及其
子类(在 Understanding SqlQuery
中介绍)。SqlParameter
SqlQuery
第二行(带有 )声明要在
stored procedure 调用。还有一个 for 参数
(为过程提供 IN 值并返回值的参数)。SqlOutParameter
out
SqlInOutParameter
InOut
只有声明为 和 的参数才用于
提供输入值。这与类不同,类(对于
向后兼容性原因)允许为参数提供输入值
声明为 .SqlParameter SqlInOutParameter StoredProcedure SqlOutParameter |
对于 IN 参数,除了名称和 SQL 类型之外,您还可以为
数字数据或自定义数据库类型的类型名称。对于参数,您可以
提供 a 以处理从游标返回的行的映射。另一个
option 是指定一个 API,该 API 提供了一个定义
自定义返回值的处理。out
RowMapper
REF
SqlReturnType
只有声明为 和 的参数才用于
提供输入值。这与类不同,类(对于
向后兼容性原因)允许为参数提供输入值
声明为 .SqlParameter SqlInOutParameter StoredProcedure SqlOutParameter |
使用 Called a stored FunctionSimpleJdbcCall
您可以采用与调用存储过程几乎相同的方式调用存储函数,但
提供函数名称而不是过程名称。您将该方法用作配置的一部分,以指示您希望
对函数的调用,以及函数调用的相应字符串。一个
专门的调用 () 用于运行函数,并且
将函数返回值作为指定类型的对象,这意味着您执行
不必从 Results Map 中检索返回值。类似的便捷方法
(named ) 也可用于只有一个参数的存储过程。以下示例(适用于 MySQL)基于名为 的存储函数,该函数返回 actor 的全名:withFunctionName
executeFunction
executeObject
out
get_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,其中包含来自
函数调用。executeFunction
String
从 返回 或 REF 游标ResultSet
SimpleJdbcCall
调用返回结果集的存储过程或函数有点棘手。一些
数据库在 JDBC 结果处理期间返回结果集,而其他数据库需要
显式注册的特定类型的参数。这两种方法都需要
additional processing 循环结果集并处理返回的行。跟
的 ,您可以使用该方法并声明要用于特定参数的实现。如果结果集为
返回,则没有定义名称,因此返回的
results 必须与声明 implementations 的顺序匹配。指定的名称仍用于存储已处理的结果列表
在从语句返回的结果映射中。out
SimpleJdbcCall
returningResultSet
RowMapper
RowMapper
execute
下一个示例(对于 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 规则,您可以使用由
传入要在方法中映射到的必需类。
以下示例显示了如何执行此操作:RowMapper
BeanPropertyRowMapper
newInstance
-
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
}
调用以空 传递,因为此调用不采用任何参数。
然后,从结果映射中检索参与者列表并将其返回给调用者。execute
Map