|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
简化JDBC作,使用SimpleJdbc类
这SimpleJdbc插入和SimpleJdbcCall类提供了简化的配置
通过利用可通过JDBC驱动检索的数据库元数据。
这意味着你一开始需要配置的空间会减少,虽然你可以覆盖或关闭
如果你愿意在代码中提供所有细节,那就是元数据处理。
通过使用SimpleJdbc插入
我们首先从看SimpleJdbc插入具有最小
配置选项。你应该实例化SimpleJdbc插入在数据访问中
图层的初始化方法。对于这个例子,初始化方法是setDataSource方法。你不需要对SimpleJdbc插入类。相反
你可以创建一个新实例,并通过使用withTableName方法。
该类的配置方法如下:流体返回实例的样式
关于SimpleJdbc插入,这允许你串联所有配置方法。如下
示例仅使用一种配置方法(稍后我们展示多种方法的示例):
-
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
}
这执行这里用的方法是一个平板java.util.Map作为唯一的参数。这
这里需要注意的是,用于地图必须与列匹配
表的名称,均为数据库中定义的。这是因为我们读取元数据
构造实际插入语句。
通过以下方式获取自动生成密钥SimpleJdbc插入
下一个例子使用了与前例相同的插入,但不是通过身份证它
检索自动生成的密钥并将其置于演员对象。当它创造时
这SimpleJdbc插入除了指定表的名称外,它还指定了
生成的密钥列使用生成键列方法。如下
列表展示了其运作方式:
-
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.lang.Number你可以用该对象创建一个数值类型的实例,使得
在你的领域类中使用。你不能指望所有数据库都能返回特定的 Java 版本
这里是同学。java.lang.Number是你可以依赖的基础类。如果你有
多个自动生成的列,或者生成的值是非数字的,你可以
使用一个钥匙持有者该 返回于执行并返回密钥持有者方法。
为 a 指定列SimpleJdbc插入
你可以通过指定列名列表来限制插入的列数,并使用使用Columns如下例所示:
-
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以提供参数值
使用地图提供参数值可以,但并不方便
使用类别。Spring 提供了几种实现方式SqlParameterSource你可以用来代替的界面。第一个是BeanPropertySqlParameterSource,
如果你有一个兼容 JavaBean 的类,且包含以下内容,这个类非常方便。
你的价值观。它使用对应的 getter 方法来提取参数
值。以下示例展示了如何使用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
}
另一种选择是MapSql参数源类似于地图但提供了更多
方便加值可以串联的方法。以下示例展示了如何使用它:
-
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
这SimpleJdbcCall类使用数据库中的元数据查找在和外参数,这样你就不必显式声明它们。您可以
如果你喜欢声明参数,或者你有不需要的参数,可以声明
有自动映射到 Java 类。第一个例子展示了一个简单的程序
该函数仅返回标量值瓦尔查尔和日期格式来自MySQL数据库。
示例过程读取指定的演员条目并返回first_name,last_name和birth_date以外参数。如下
列表展示了第一个例子:
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参数包含身份证你正在查找的演员。这外参数返回从表中读取的数据。
你可以申报SimpleJdbcCall类似于宣告SimpleJdbc插入.你
应该在你的数据访问初始化方法中实例化并配置该类
层。与存储过程类,你不需要创建子类
你也不需要声明可以在数据库元数据中查找的参数。
以下示例为SimpleJdbcCall配置使用之前存储的
程序(唯一的配置选项,除了数据来源,是名称
存储过程的记录):
-
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
}
你为执行调用编写的代码涉及创建一个SqlParameterSource包含IN参数。你必须匹配输入值的名称
与存储过程中声明的参数名称。该案不包含
匹配是因为你用元数据来决定数据库对象的引用方式
在存储过程中。存储过程源代码中指定的内容则不然
必然是数据库中存储方式。有些数据库会将名称转换为所有
而其他则使用小写或按指定大小写。
这执行方法取IN参数并返回地图包含任意外参数由存储过程指定的名字键入。在这种情况下,他们是out_first_name,out_last_name和out_birth_date.
最后一部分执行该方法产生演员实例 以返回
数据检索。同样,使用名称非常重要外参数
在存储过程中声明。此外,名称中的情况外结果映射中存储的参数与外参数名称
数据库,不同数据库之间可能有所不同。为了让你的代码更便携,你应该这样做
进行大小写不相关的查找,或者指示 Spring 使用LinkedCaseInsensitiveMap.
如果是后者,你可以自己创建Jdbc模板并设置setResultsMapCaseInsensitive属性到true.然后你可以通过这个定制化Jdbc模板实例化
你的构造者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
}
采取此举可以避免姓名大小写的冲突
返回外参数。
明确声明用于 的参数SimpleJdbcCall
本章早些时候,我们描述了如何从元数据推导出参数,但你可以声明它们
如果你愿意,可以明确地说。你可以通过创建和配置来实现SimpleJdbcCall跟
这声明参数该方法取一个可变数量的SqlParameter对象
作为输入。关于如何定义SqlParameter.
| 如果你使用的数据库不是 Spring 支持的,显式声明是必要的 数据库。目前,Spring 支持对存储过程调用的元数据查找 以下数据库包括:Apache Derby、DB2、MySQL、Microsoft SQL Server、Oracle 和 Sybase。 我们还支持MySQL、Microsoft SQL Server的存储函数元数据查找, 以及神谕者。 |
你可以选择明确声明其中一个、部分或全部参数。参数
在未明确声明参数的情况下,元数据仍会被使用。绕过所有
处理元数据查找,以查找潜在参数,并仅使用声明的
参数,你可以调用withoutProcedureColumnMetaDataAccess作为
声明。假设你为
数据库功能。在这种情况下,你会打电话useInParameterNames指定列表
包含给定签名的IN参数名称。
以下示例展示了一个完全声明的过程调用,并使用了以下信息 上述例子:
-
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
定义一个参数SimpleJdbc以及用于关系数据库管理系统的作
你可以使用的类(在建模JDBC作中以Java对象形式进行介绍)SqlParameter或者其子类之一。
为此,通常你在构造函数中指定参数名称和SQL类型。SQL 类型
通过使用java.sql.类型常数。在本章早些时候,我们看到了声明
类似于以下内容:
-
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),
第一行SqlParameter声明一个IN参数。你可以用IN参数
对于存储过程调用和通过使用SqlQuery以及其
子类(涵盖于理解SqlQuery).
第二行(带有SqlOutParameter) 宣称外参数用于
存储过程调用。还有一个SqlInOutParameter为进攻参数
(为过程提供IN值且返回值的参数)。
仅有参数声明为SqlParameter和SqlInOutParameter习惯于
提供输入值。这与存储过程类,其中(对于
向后兼容性原因)允许为参数提供输入值
被宣布为SqlOutParameter. |
对于IN参数,除了名称和SQL类型外,你可以指定一个缩放
数字数据或自定义数据库类型的类型名称。为外参数,你可以
提供一个行图仪用于处理从A返回的行的映射裁判光标。另一个
选项是指定一个SqlReturnType这为定义提供了机会
对返回值进行定制处理。
通过以下方式调用存储函数SimpleJdbcCall
你可以调用存储函数,方式几乎和调用存储过程一样,但
你提供函数名而不是过程名。你用的是withFunctionName方法作为配置的一部分,表示你想做的
调用函数,生成相应的函数调用字符串。一个
专门呼叫(执行函数)用于运行该函数,且
函数返回值为指定类型的对象,这意味着你会
无需从结果映射中检索返回值。类似的便利方法
(名为执行对象)也适用于只有一个的存储过程外参数。以下示例(针对 MySQL)基于一个名为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
}
这执行函数所用方法返回字符串包含 的返回值来自
函数调用。
返回结果集或从SimpleJdbcCall
调用返回结果集的存储过程或函数有点棘手。一些
数据库在 JDBC 结果处理过程中返回结果集,而其他数据库则需要
明确注册外特定类型的参数。这两种方法都需要
额外的处理环绕结果集并处理返回的行。跟
这SimpleJdbcCall,你可以使用returningResultSet方法并声明一个行图仪用于特定参数的实现。如果结果集为
在结果处理过程中返回时,没有定义名称,因此返回
结果必须与你声明行图仪实现。指定的名称仍用于存储处理后的结果列表
在返回的结果映射中执行陈述。
下一个例子(针对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 规则,你可以使用BeanPropertyRowMapper由 创建
通过映射到的需要的类新实例方法。
以下示例展示了如何实现:
-
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
}
这执行呼叫在空时传递地图,因为该调用不涉及任何参数。
然后从结果映射中检索演员列表并返回给调用者。