使用 JDBC 核心类控制基本的 JDBC 处理和错误处理
本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理,包括错误处理。它包括以下主题:
使用 JdbcTemplate
JdbcTemplate
是 JDBC 核心包中的中心类。它处理资源的创建和释放,这有助于您避免常见的错误,例如忘记关闭连接。它执行核心 JDBC 工作流的基本任务(例如语句创建和执行),将应用程序代码留给提供 SQL 和提取结果。JdbcTemplate
类:
-
运行 SQL 查询
-
更新语句和存储过程调用
-
对
ResultSet
实例进行迭代并提取返回的参数值。 -
捕获 JDBC 异常并将其转换为
org.springframework.dao
包中定义的通用、更具信息性的异常层次结构。(请参阅 一致的异常层次结构。)
当您在代码中使用 JdbcTemplate
时,您只需要实现回调接口,赋予它们明确定义的契约。给定 JdbcTemplate
类提供的 Connection
,PreparedStatementCreator
回调接口创建一个预处理语句,提供 SQL 和任何必要的参数。CallableStatementCreator
接口也是如此,它创建可调用语句。RowCallbackHandler
接口从 ResultSet
的每一行中提取值。
您可以通过使用 DataSource
引用直接实例化 JdbcTemplate
来在 DAO 实现中使用它,或者您可以在 Spring IoC 容器中配置它并将其作为 bean 引用提供给 DAO。
|
此类的所有 SQL 都以 DEBUG
级别记录,类别对应于模板实例的完全限定类名(通常是 JdbcTemplate
,但如果您使用 JdbcTemplate
类的自定义子类,则可能不同)。
以下各节提供了一些 JdbcTemplate
用法的示例。这些示例并非 JdbcTemplate
公开的所有功能的详尽列表。请参阅 javadoc 以获取更多信息。
查询 (SELECT
)
以下查询获取关系中的行数:
-
Java
-
Kotlin
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!
以下查询使用绑定变量:
-
Java
-
Kotlin
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");
val countOfActorsNamedJoe = jdbcTemplate.queryForObject<Int>(
"select count(*) from t_actor where first_name = ?", arrayOf("Joe"))!!
以下查询查找 String
:
-
Java
-
Kotlin
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
String.class, 1212L);
val lastName = this.jdbcTemplate.queryForObject<String>(
"select last_name from t_actor where id = ?",
arrayOf(1212L))!!
以下查询查找并填充单个领域对象:
-
Java
-
Kotlin
Actor actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
(resultSet, rowNum) -> {
Actor newActor = new Actor();
newActor.setFirstName(resultSet.getString("first_name"));
newActor.setLastName(resultSet.getString("last_name"));
return newActor;
},
1212L);
val actor = jdbcTemplate.queryForObject(
"select first_name, last_name from t_actor where id = ?",
arrayOf(1212L)) { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
以下查询查找并填充领域对象列表:
-
Java
-
Kotlin
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
});
val actors = jdbcTemplate.query("select first_name, last_name from t_actor") { rs, _ ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
如果最后两段代码实际上存在于同一个应用程序中,那么删除两个 RowMapper
lambda 表达式中存在的重复并将其提取到一个可以根据需要由 DAO 方法引用的单个字段中将是有意义的。例如,前面的代码片段最好写成如下:
-
Java
-
Kotlin
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
};
public List<Actor> findAllActors() {
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}
val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
fun findAllActors(): List<Actor> {
return jdbcTemplate.query("select first_name, last_name from t_actor", actorMapper)
}
使用 JdbcTemplate
进行更新 (INSERT
、UPDATE
和 DELETE
)
您可以使用 update(..)
方法执行插入、更新和删除操作。参数值通常作为可变参数提供,或者作为对象数组提供。
以下示例插入一个新条目:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling")
以下示例更新现有条目:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L)
以下示例删除一个条目:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong())
其他 JdbcTemplate
操作
您可以使用 execute(..)
方法运行任何任意 SQL。因此,该方法通常用于 DDL 语句。它被大量重载,带有接受回调接口、绑定变量数组等的变体。以下示例创建一个表:
-
Java
-
Kotlin
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
以下示例调用存储过程:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
unionId.toLong())
更复杂的存储过程支持 稍后介绍。
JdbcTemplate
最佳实践
JdbcTemplate
类的实例在配置后是线程安全的。这很重要,因为它意味着您可以配置 JdbcTemplate
的单个实例,然后安全地将此共享引用注入到多个 DAO(或存储库)中。JdbcTemplate
是有状态的,因为它维护对 DataSource
的引用,但此状态不是会话状态。
使用 JdbcTemplate
类(以及相关的 NamedParameterJdbcTemplate
类)时的一个常见做法是在 Spring 配置文件中配置 DataSource
,然后将该共享 DataSource
bean 依赖注入到您的 DAO 类中。JdbcTemplate
在 DataSource
的 setter 中或在构造函数中创建。这导致 DAO 类似于以下内容:
以下示例显示了相应的配置:
显式配置的替代方法是使用组件扫描和依赖注入的注解支持。在这种情况下,您可以使用 @Repository
注解类(使其成为组件扫描的候选者)。以下示例显示了如何执行此操作:
以下示例显示了相应的配置:
如果您使用 Spring 的 JdbcDaoSupport
类并且您的各种基于 JDBC 的 DAO 类从它继承,您的子类将从 JdbcDaoSupport
类继承 setDataSource(..)
方法。您可以选择是否从该类继承。JdbcDaoSupport
类仅作为方便提供。
无论您选择使用(或不使用)上述哪种模板初始化样式,通常都无需在每次要运行 SQL 时创建 JdbcTemplate
类的新实例。配置后,JdbcTemplate
实例是线程安全的。如果您的应用程序访问多个数据库,您可能需要多个 JdbcTemplate
实例,这需要多个 DataSource
,随后需要多个不同配置的 JdbcTemplate
实例。
使用 NamedParameterJdbcTemplate
NamedParameterJdbcTemplate
类增加了对使用命名参数编程 JDBC 语句的支持,而不是仅使用经典占位符('?')参数编程 JDBC 语句。NamedParameterJdbcTemplate
类包装了一个 JdbcTemplate
并委托给包装的 JdbcTemplate
来完成大部分工作。本节仅描述 NamedParameterJdbcTemplate
类与 JdbcTemplate
本身不同的区域——即使用命名参数编程 JDBC 语句。以下示例显示了如何使用 NamedParameterJdbcTemplate
:
-
Java
-
Kotlin
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from t_actor where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActorsByFirstName(firstName: String): Int {
val sql = "select count(*) from t_actor where first_name = :first_name"
val namedParameters = MapSqlParameterSource("first_name", firstName)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
请注意 sql
变量赋值中命名参数表示法的使用,以及插入 namedParameters
变量(类型为 MapSqlParameterSource
)的相应值。
或者,您可以通过使用基于 Map
的样式将命名参数及其相应值传递给 NamedParameterJdbcTemplate
实例。NamedParameterJdbcOperations
公开并由 NamedParameterJdbcTemplate
类实现的其余方法遵循类似的模式,此处不再赘述。
以下示例显示了基于 Map
的样式的使用:
-
Java
-
Kotlin
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActorsByFirstName(String firstName) {
String sql = "select count(*) from t_actor where first_name = :first_name";
Map<String, String> namedParameters = Collections.singletonMap("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActorsByFirstName(firstName: String): Int {
val sql = "select count(*) from t_actor where first_name = :first_name"
val namedParameters = mapOf("first_name" to firstName)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
与 NamedParameterJdbcTemplate
相关的一个很好的特性(并且存在于同一个 Java 包中)是 SqlParameterSource
接口。您已经在前面代码片段之一(MapSqlParameterSource
类)中看到了此接口实现的一个示例。SqlParameterSource
是 NamedParameterJdbcTemplate
的命名参数值的来源。MapSqlParameterSource
类是一个简单的实现,它是 java.util.Map
的适配器,其中键是参数名称,值是参数值。
另一个 SqlParameterSource
实现是 BeanPropertySqlParameterSource
类。此类包装任意 JavaBean(即遵循 JavaBean 约定的类的实例),并使用包装的 JavaBean 的属性作为命名参数值的来源。
以下示例显示了一个典型的 JavaBean:
-
Java
-
Kotlin
public class Actor {
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public Long getId() {
return this.id;
}
// setters omitted...
}
data class Actor(val id: Long, val firstName: String, val lastName: String)
以下示例使用 NamedParameterJdbcTemplate
返回上例中显示的类的成员数:
-
Java
-
Kotlin
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor' class
String sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActors(exampleActor: Actor): Int {
// notice how the named parameters match the properties of the above 'Actor' class
val sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName"
val namedParameters = BeanPropertySqlParameterSource(exampleActor)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
请记住,NamedParameterJdbcTemplate
类包装了一个经典的 JdbcTemplate
模板。如果您需要访问包装的 JdbcTemplate
实例以访问仅存在于 JdbcTemplate
类中的功能,您可以使用 getJdbcOperations()
方法通过 JdbcOperations
接口访问包装的 JdbcTemplate
。
另请参阅 JdbcTemplate
最佳实践,了解在应用程序上下文中使用 NamedParameterJdbcTemplate
类的指南。
统一的 JDBC 查询/更新操作:JdbcClient
从 6.1 开始,NamedParameterJdbcTemplate
的命名参数语句和常规 JdbcTemplate
的位置参数语句通过具有流畅交互模型的统一客户端 API 提供。
例如,使用位置参数:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = ?")
.param(firstName)
.query(Integer.class).single();
}
例如,使用命名参数:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = :firstName")
.param("firstName", firstName)
.query(Integer.class).single();
}
RowMapper
功能也可用,具有灵活的结果解析:
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query((rs, rowNum) -> new Actor(rs.getString("first_name"), rs.getString("last_name")))
.list();
除了自定义 RowMapper
之外,您还可以指定要映射到的类。例如,假设 Actor
具有 firstName
和 lastName
属性作为记录类、自定义构造函数、bean 属性或普通字段:
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query(Actor.class)
.list();
对于必需的单个对象结果:
Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.single();
对于 java.util.Optional
结果:
Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
.param(1212L)
.query(Actor.class)
.optional();
对于更新语句:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.param("Leonor").param("Watling")
.update();
或者带有命名参数的更新语句:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.param("firstName", "Leonor").param("lastName", "Watling")
.update();
除了单个命名参数之外,您还可以指定一个参数源对象——例如,一个记录类、一个具有 bean 属性的类,或一个提供 firstName
和 lastName
属性的普通字段持有者,例如上面的 Actor
类:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling")
.update();
上述参数和查询结果的自动 Actor
类映射通过隐式 SimplePropertySqlParameterSource
和 SimplePropertyRowMapper
策略提供,这些策略也可用作 BeanPropertySqlParameterSource
和 BeanPropertyRowMapper
/DataClassRowMapper
的常见替代品,也适用于 JdbcTemplate
和 NamedParameterJdbcTemplate
本身。
|
使用 SQLExceptionTranslator
SQLExceptionTranslator
是一个接口,由可以在 SQLException
和 Spring 自己的 org.springframework.dao.DataAccessException
之间进行转换的类实现,后者与数据访问策略无关。实现可以是通用的(例如,使用 JDBC 的 SQLState 代码)或专有的(例如,使用 Oracle 错误代码)以获得更高的精度。此异常转换机制在不传播 SQLException
而是 DataAccessException
的常见 JdbcTemplate
和 JdbcTransactionManager
入口点后面使用。
从 6.0 开始,默认的异常转换器是 |
SQLErrorCodeSQLExceptionTranslator
是 SQLExceptionTranslator
的实现,当类路径根目录中存在名为 sql-error-codes.xml
的文件时,默认使用此实现。此实现使用特定的供应商代码。它比 SQLState
或 SQLException
子类转换更精确。错误代码转换基于名为 SQLErrorCodes
的 JavaBean 类型类中保存的代码。此类由 SQLErrorCodesFactory
创建和填充,SQLErrorCodesFactory
(顾名思义)是根据名为 sql-error-codes.xml
的配置文件内容创建 SQLErrorCodes
的工厂。此文件填充了供应商代码,并基于从 DatabaseMetaData
获取的 DatabaseProductName
。使用您正在使用的实际数据库的代码。
SQLErrorCodeSQLExceptionTranslator
按以下顺序应用匹配规则:
-
子类实现的任何自定义转换。通常,使用提供的具体
SQLErrorCodeSQLExceptionTranslator
,因此此规则不适用。它仅在您实际提供了子类实现时才适用。 -
任何作为
SQLErrorCodes
类的customSqlExceptionTranslator
属性提供的SQLExceptionTranslator
接口的自定义实现。 -
搜索
CustomSQLErrorCodesTranslation
类的实例列表(为SQLErrorCodes
类的customTranslations
属性提供)以查找匹配项。 -
应用错误代码匹配。
-
使用回退转换器。
SQLExceptionSubclassTranslator
是默认的回退转换器。如果此转换不可用,则下一个回退转换器是SQLStateSQLExceptionTranslator
。
|
您可以扩展 SQLErrorCodeSQLExceptionTranslator
,如下例所示:
-
Java
-
Kotlin
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {
if (sqlEx.getErrorCode() == -12345) {
return new DeadlockLoserDataAccessException(task, sqlEx);
}
return null;
}
}
class CustomSQLErrorCodesTranslator : SQLErrorCodeSQLExceptionTranslator() {
override fun customTranslate(task: String, sql: String?, sqlEx: SQLException): DataAccessException? {
if (sqlEx.errorCode == -12345) {
return DeadlockLoserDataAccessException(task, sqlEx)
}
return null
}
}
在前面的示例中,特定的错误代码(-12345
)被翻译,而其他错误则留给默认转换器实现进行翻译。要使用此自定义转换器,您必须通过 setExceptionTranslator
方法将其传递给 JdbcTemplate
,并且您必须将此 JdbcTemplate
用于需要此转换器的所有数据访问处理。以下示例显示了如何使用此自定义转换器:
-
Java
-
Kotlin
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
// create a JdbcTemplate and set data source
this.jdbcTemplate = new JdbcTemplate();
this.jdbcTemplate.setDataSource(dataSource);
// create a custom translator and set the DataSource for the default translation lookup
CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
tr.setDataSource(dataSource);
this.jdbcTemplate.setExceptionTranslator(tr);
}
public void updateShippingCharge(long orderId, long pct) {
// use the prepared JdbcTemplate for this update
this.jdbcTemplate.update("update orders" +
" set shipping_charge = shipping_charge * ? / 100" +
" where id = ?", pct, orderId);
}
// create a JdbcTemplate and set data source
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
// create a custom translator and set the DataSource for the default translation lookup
exceptionTranslator = CustomSQLErrorCodesTranslator().apply {
this.dataSource = dataSource
}
}
fun updateShippingCharge(orderId: Long, pct: Long) {
// use the prepared JdbcTemplate for this update
this.jdbcTemplate!!.update("update orders" +
" set shipping_charge = shipping_charge * ? / 100" +
" where id = ?", pct, orderId)
}
自定义转换器被传递一个数据源,以便在 sql-error-codes.xml
中查找错误代码。
运行语句
运行 SQL 语句需要很少的代码。您需要一个 DataSource
和一个 JdbcTemplate
,包括 JdbcTemplate
提供的便利方法。以下示例显示了创建一个新表的最小但功能齐全的类所需包含的内容:
-
Java
-
Kotlin
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAStatement {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void doExecute() {
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class ExecuteAStatement(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun doExecute() {
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
}
}
运行查询
一些查询方法返回单个值。要从一行中检索计数或特定值,请使用 queryForObject(..)
。后者将返回的 JDBC Type
转换为作为参数传入的 Java 类。如果类型转换无效,则抛出 InvalidDataAccessApiUsageException
。以下示例包含两个查询方法,一个用于 int
,一个用于 String
:
-
Java
-
Kotlin
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class RunAQuery {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public int getCount() {
return this.jdbcTemplate.queryForObject("select count(*) from mytable", Integer.class);
}
public String getName() {
return this.jdbcTemplate.queryForObject("select name from mytable", String.class);
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class RunAQuery(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
val count: Int
get() = jdbcTemplate.queryForObject("select count(*) from mytable")!!
val name: String?
get() = jdbcTemplate.queryForObject("select name from mytable")
}
除了单结果查询方法之外,还有几种方法返回一个列表,其中包含查询返回的每一行的条目。最通用的方法是 queryForList(..)
,它返回一个 List
,其中每个元素都是一个 Map
,包含每个列的一个条目,使用列名作为键。如果您在前面的示例中添加一个方法来检索所有行的列表,它可能如下所示:
-
Java
-
Kotlin
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public List<Map<String, Object>> getList() {
return this.jdbcTemplate.queryForList("select * from mytable");
}
private val jdbcTemplate = JdbcTemplate(dataSource)
fun getList(): List<Map<String, Any>> {
return jdbcTemplate.queryForList("select * from mytable")
}
返回的列表将类似于以下内容:
更新数据库
以下示例更新特定主键的列:
-
Java
-
Kotlin
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAnUpdate {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void setName(int id, String name) {
this.jdbcTemplate.update("update mytable set name = ? where id = ?", name, id);
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class ExecuteAnUpdate(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun setName(id: Int, name: String) {
jdbcTemplate.update("update mytable set name = ? where id = ?", name, id)
}
}
在前面的示例中,SQL 语句具有行参数的占位符。您可以将参数值作为可变参数或作为对象数组传递。因此,您应该显式地将基本类型包装在基本类型包装类中,或者使用自动装箱。
检索自动生成的主键
update()
便利方法支持检索数据库生成的主键。此支持是 JDBC 3.0 标准的一部分。有关详细信息,请参阅规范的第 13.6 章。该方法将 PreparedStatementCreator
作为其第一个参数,这是指定所需插入语句的方式。另一个参数是 KeyHolder
,它在成功从更新返回时包含生成的主键。没有标准的单一方法来创建适当的 PreparedStatement
(这解释了为什么方法签名是这样的)。以下示例在 Oracle 上有效,但在其他平台上可能无效:
-
Java
-
Kotlin
final String INSERT_SQL = "insert into my_test (name) values(?)";
final String name = "Rob";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(INSERT_SQL, new String[] { "id" });
ps.setString(1, name);
return ps;
}, keyHolder);
// keyHolder.getKey() now contains the generated key
val INSERT_SQL = "insert into my_test (name) values(?)"
val name = "Rob"
val keyHolder = GeneratedKeyHolder()
jdbcTemplate.update({
it.prepareStatement (INSERT_SQL, arrayOf("id")).apply { setString(1, name) }
}, keyHolder)
// keyHolder.getKey() now contains the generated key