此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Framework 6.2.4! |
使用 JDBC 核心类控制基本的 JDBC 处理和错误处理
本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理。 包括错误处理。它包括以下主题:
用JdbcTemplate
JdbcTemplate
是 JDBC 核心包中的中心类。它处理
创建和释放资源,这有助于您避免常见错误,例如
忘记关闭连接。它执行核心 JDBC 的基本任务
工作流(例如语句创建和执行),让应用程序代码提供
SQL 并提取结果。这JdbcTemplate
类:
-
运行 SQL 查询
-
更新语句和存储过程调用
-
执行迭代
ResultSet
实例和返回参数值的提取。 -
捕获 JDBC 异常并将它们转换为泛型、信息量更大的异常 层次结构在
org.springframework.dao
包。(请参阅一致的异常层次结构。
当您使用JdbcTemplate
对于您的代码,您只需实现 callback
接口,为它们提供明确定义的 Contract。给定一个Connection
由JdbcTemplate
类中,使用PreparedStatementCreator
callback 接口会创建一个准备好的
语句,提供 SQL 和任何必要的参数。对于CallableStatementCreator
接口,用于创建可调用语句。这RowCallbackHandler
interface 从ResultSet
.
您可以使用JdbcTemplate
在 DAO 实现中通过直接实例化
替换为DataSource
引用,或者你可以在 Spring IoC 容器中配置它并将其提供给
DAO 作为 bean 引用。
这DataSource 应该始终配置为 Spring IoC 容器中的 bean。在
第一种情况是将 bean 直接提供给服务;在第二种情况下,它给出了
添加到准备好的模板中。 |
此类发出的所有 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")!!
以下查询使用 bind 变量:
-
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"))
如果最后两个代码片段实际存在于同一个应用程序中,那么它将使
sense 删除两者中存在的重复项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)
}
更新 (INSERT
,UPDATE
和DELETE
) 替换为JdbcTemplate
您可以使用update(..)
方法执行 INSERT、UPDATE 和 DELETE作。
参数值通常作为变量参数提供,或者作为对象数组提供。
以下示例插入一个新条目:
-
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。因此,
method 通常用于 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
class) 为
配置DataSource
在 Spring 配置文件中,然后 dependency-inject
那个分享了DataSource
bean 添加到您的 DAO 类中。这JdbcTemplate
在
的 setterDataSource
.这导致了类似于以下内容的 DAO:
-
Java
-
Kotlin
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao {
private val jdbcTemplate = JdbcTemplate(dataSource)
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
以下示例显示了相应的 XML 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="corporateEventDao" class="com.example.JdbcCorporateEventDao">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
显式配置的替代方法是使用组件扫描和注释
支持依赖项注入。在这种情况下,您可以使用@Repository
(这使它成为组件扫描的候选者)并注释DataSource
setter
method 替换为@Autowired
.以下示例显示了如何执行此作:
-
Java
-
Kotlin
@Repository (1)
public class JdbcCorporateEventDao implements CorporateEventDao {
private JdbcTemplate jdbcTemplate;
@Autowired (2)
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource); (3)
}
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
1 | 用@Repository . |
2 | 注释DataSource setter 方法替换为@Autowired . |
3 | 新建JdbcTemplate 使用DataSource . |
@Repository (1)
class JdbcCorporateEventDao(dataSource: DataSource) : CorporateEventDao { (2)
private val jdbcTemplate = JdbcTemplate(dataSource) (3)
// JDBC-backed implementations of the methods on the CorporateEventDao follow...
}
1 | 用@Repository . |
2 | 构造函数注入DataSource . |
3 | 新建JdbcTemplate 使用DataSource . |
以下示例显示了相应的 XML 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.springframework.docs.test" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
</beans>
如果您使用 Spring 的JdbcDaoSupport
类和你的各种 JDBC 支持的 DAO 类
extend 的setDataSource(..)
方法从JdbcDaoSupport
类。您可以选择是否从此类继承。这JdbcDaoSupport
class 仅为方便起见而提供。
无论您选择使用上述哪种模板初始化样式(或
not),则很少需要创建JdbcTemplate
每个类
运行时间。配置后,JdbcTemplate
实例是线程安全的。
如果您的应用程序访问多个数据库,则可能需要多个JdbcTemplate
实例,这需要多个DataSources
随后,多个不同的
配置JdbcTemplate
实例。
用NamedParameterJdbcTemplate
这NamedParameterJdbcTemplate
class 添加了对 JDBC 语句编程的支持
通过使用命名参数,而不是仅使用经典
placeholder ('?'
) 参数。这NamedParameterJdbcTemplate
类将JdbcTemplate
和委托给包装的JdbcTemplate
来完成它的大部分工作。这
部分仅介绍了NamedParameterJdbcTemplate
类
从JdbcTemplate
本身 — 即使用 named 对 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
variable(类型MapSqlParameterSource
).
或者,您可以将命名参数及其相应的值传递给NamedParameterJdbcTemplate
实例Map
-基于样式。剩余的
由NamedParameterJdbcOperations
并由NamedParameterJdbcTemplate
类遵循类似的模式,此处不作介绍。
以下示例显示了Map
-based 样式:
-
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
class 是一个
简单的实现,它是围绕java.util.Map
,其中键
是参数名称,值是参数值。
另一个SqlParameterSource
implementation 是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()
方法来访问包装的JdbcTemplate
通过JdbcOperations
接口。
另请参阅JdbcTemplate
最佳实践有关使用NamedParameterJdbcTemplate
类。
统一的 JDBC 查询/更新作:JdbcClient
从 6.1 开始,NamedParameterJdbcTemplate
和位置
parameter 语句的常规JdbcTemplate
可通过统一的客户端 API 使用
使用 Fluent 交互模型。
例如,使用位置参数:
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
性能
作为 Record 类、自定义构造函数、Bean Properties 或 Plain Fields:
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();
对于 update 语句:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
.param("Leonor").param("Watling")
.update();
或者带有命名参数的 update 语句:
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.param("firstName", "Leonor").param("lastName", "Watling")
.update();
除了单独的命名参数,您还可以指定一个参数源对象 –
例如,一个 Record 类、一个具有 Bean 属性的类或一个 Plain Field Holder,它
提供firstName
和lastName
属性,例如Actor
class 的
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling")
.update();
自动Actor
参数的类映射以及上面的查询结果是
通过 Implicit 提供SimplePropertySqlParameterSource
和SimplePropertyRowMapper
策略,这些策略也可以直接使用。它们可以作为常见的替代品
为BeanPropertySqlParameterSource
和BeanPropertyRowMapper
/DataClassRowMapper
,
还与JdbcTemplate
和NamedParameterJdbcTemplate
他们自己。
JdbcClient 是 JDBC 查询/更新语句的灵活但简化的外观。
高级功能(如批量插入和存储过程调用)通常需要
额外的自定义:考虑 Spring 的SimpleJdbcInsert 和SimpleJdbcCall 类或
普通直接JdbcTemplate 用于 中不可用的任何此类功能JdbcClient . |
用SQLExceptionTranslator
SQLExceptionTranslator
是由可以翻译的类实现的接口
之间SQLException
s 和 Spring 自己的org.springframework.dao.DataAccessException
,
这在数据访问策略方面是不可知的。实现可以是通用的(对于
示例,使用 JDBC 的 SQLState 代码)或专有代码(例如,使用 Oracle 错误
代码)以获得更高的精度。此异常转换机制在
常见JdbcTemplate
和JdbcTransactionManager
不
传播SQLException
而是DataAccessException
.
从 6.0 开始,默认的异常转换器是SQLExceptionSubclassTranslator ,
检测 JDBC 4SQLException 子类,并带有 fallback
自SQLState 内省SQLStateSQLExceptionTranslator .这通常是
足以进行常见的数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用SQLErrorCodeSQLExceptionTranslator 如
下面描述,可能带有自定义错误代码映射。 |
SQLErrorCodeSQLExceptionTranslator
是SQLExceptionTranslator
,当名为sql-error-codes.xml
存在于根
的 classpath 中。此实施使用特定的供应商代码。它比SQLState
或SQLException
子类 translation 的错误代码翻译基于
在名为SQLErrorCodes
.这个类被创建并
由SQLErrorCodesFactory
,它(顾名思义)是
创建SQLErrorCodes
基于名为sql-error-codes.xml
.此文件填充了供应商代码,并基于DatabaseProductName
摘自DatabaseMetaData
.实际代码
数据库。
这SQLErrorCodeSQLExceptionTranslator
按以下顺序应用匹配规则:
-
由子类实现的任何自定义翻译。通常,提供的混凝土
SQLErrorCodeSQLExceptionTranslator
,因此此规则不适用。它 仅当您实际提供了 subclass 实现时适用。 -
任何
SQLExceptionTranslator
提供的接口 作为customSqlExceptionTranslator
属性的SQLErrorCodes
类。 -
的
CustomSQLErrorCodesTranslation
类(为customTranslations
属性的SQLErrorCodes
类)中搜索匹配项。 -
应用错误代码匹配。
-
使用回退转换器。
SQLExceptionSubclassTranslator
是默认回退 在线翻译。如果此翻译不可用,则下一个回退翻译器为 这SQLStateSQLExceptionTranslator
.
这SQLErrorCodesFactory 默认用于定义错误代码,自定义
异常翻译。它们在名为sql-error-codes.xml 从
classpath 和匹配的SQLErrorCodes 实例 是基于数据库定位的
name 来自正在使用的数据库的数据库元数据。 |
您可以扩展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
) 被翻译,而
其他错误留给 default translator 实现进行转换。
要使用此自定义转换器,必须将其传递给JdbcTemplate
通过
方法setExceptionTranslator
,并且您必须使用此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
.
Running 语句
运行 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))")
}
}
Running Queries
Some query methods return a single value. To retrieve a count or a specific value from
one row, use queryForObject(..)
. The latter converts the returned JDBC Type
to the
Java class that is passed in as an argument. If the type conversion is invalid, an
InvalidDataAccessApiUsageException
is thrown. The following example contains two
query methods, one for an int
and one that queries for a 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")
}
In addition to the single result query methods, several methods return a list with an
entry for each row that the query returned. The most generic method is queryForList(..)
,
which returns a List
where each element is a Map
containing one entry for each column,
using the column name as the key. If you add a method to the preceding example to retrieve a
list of all the rows, it might be as follows:
-
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")
}
The returned list would resemble the following:
[{name=Bob, id=1}, {name=Mary, id=2}]
Updating the Database
The following example updates a column for a certain primary key:
-
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)
}
}
In the preceding example,
an SQL statement has placeholders for row parameters. You can pass the parameter values
in as varargs or, alternatively, as an array of objects. Thus, you should explicitly wrap primitives
in the primitive wrapper classes, or you should use auto-boxing.
Retrieving Auto-generated Keys
An update()
convenience method supports the retrieval of primary keys generated by the
database. This support is part of the JDBC 3.0 standard. See Chapter 13.6 of the
specification for details. The method takes a PreparedStatementCreator
as its first
argument, and this is the way the required insert statement is specified. The other
argument is a KeyHolder
, which contains the generated key on successful return from the
update. There is no standard single way to create an appropriate PreparedStatement
(which explains why the method signature is the way it is). The following example works
on Oracle but may not work on other platforms:
-
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