本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理。 包括错误处理。它包括以下主题:
用JdbcTemplate
JdbcTemplate是 JDBC 核心包中的中心类。它处理
创建和释放资源,这有助于您避免常见错误,例如
忘记关闭连接。它执行核心 JDBC 的基本任务
工作流(例如语句创建和执行),让应用程序代码提供
SQL 并提取结果。类:JdbcTemplate
- 
运行 SQL 查询
 - 
更新语句和存储过程调用
 - 
对实例执行迭代并提取返回的参数值。
ResultSet - 
捕获 JDBC 异常并将它们转换为泛型、信息量更大的异常 hierarchy 中定义的 Hierarchy。(请参阅一致的异常层次结构。
org.springframework.dao 
当您使用 for your code 时,您只需实现 callback
接口,为它们提供明确定义的 Contract。给定类提供的 a 后,回调接口会创建一个准备好的
语句,提供 SQL 和任何必要的参数。这同样适用于创建可调用语句的接口。该接口从 .JdbcTemplateConnectionJdbcTemplatePreparedStatementCreatorCallableStatementCreatorRowCallbackHandlerResultSet
您可以通过直接实例化在 DAO 实现中使用
替换为引用,或者您可以在 Spring IoC 容器中配置它并将其提供给
DAO 作为 bean 引用。JdbcTemplateDataSource
应该始终在 Spring IoC 容器中配置为 bean。在
第一种情况是将 bean 直接提供给服务;在第二种情况下,它给出了
添加到准备好的模板中。DataSource | 
此类发出的所有 SQL 都记录在类别
对应于 Template 实例的完全限定类名(通常为 ,但如果您使用类的自定义子类,则可能会有所不同)。DEBUGJdbcTemplateJdbcTemplate
以下部分提供了一些用法示例。这些例子
并不是 .
有关此内容,请参阅随附的 javadoc。JdbcTemplateJdbcTemplate
查询 (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 删除两个 lambda 表达式中存在的重复项,使用 sense
将它们提取到一个字段中,然后 DAO 方法可以根据需要引用该字段。
例如,最好按如下方式编写前面的代码片段:RowMapper
- 
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)
}
更新 (, , 和 )INSERTUPDATEDELETEJdbcTemplate
您可以使用该方法执行插入、更新和删除操作。
参数值通常作为变量参数提供,或者作为对象数组提供。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
您可以使用该方法运行任意 SQL。因此,
method 通常用于 DDL 语句。它严重过载了采用
回调接口、绑定变量数组等。以下示例创建一个
桌子:execute(..)
- 
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最佳实践
类的实例一旦配置,就是线程安全的。这是
重要,因为这意味着您可以配置单个实例,然后将此共享引用安全地注入多个 DAO(或存储库)。
这是有状态的,因为它维护对 的引用 ,但
此状态不是对话状态。JdbcTemplateJdbcTemplateJdbcTemplateDataSource
使用该类(以及关联的 NamedParameterJdbcTemplate 类)时的常见做法是
在 Spring 配置文件中配置 a,然后 dependency-inject
将 bean 共享到您的 DAO 类中。在
.这导致了类似于以下内容的 DAO:JdbcTemplateDataSourceDataSourceJdbcTemplateDataSource
- 
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>
显式配置的替代方法是使用组件扫描和注释
支持依赖项注入。在这种情况下,你可以用 (这使它成为组件扫描的候选者) 来注释类,并 注释 setter
方法与 .以下示例显示了如何执行此操作:@RepositoryDataSource@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@Autowired | 
| 3 | 使用 创建新的 .JdbcTemplateDataSource | 
@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 | 使用 创建新的 .JdbcTemplateDataSource | 
以下示例显示了相应的 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 的类和各种 JDBC 支持的 DAO 类
extend 的子类会从该类继承一个 method。您可以选择是否从此类继承。该类仅为方便起见而提供。JdbcDaoSupportsetDataSource(..)JdbcDaoSupportJdbcDaoSupport
无论您选择使用上述哪种模板初始化样式(或
not),则很少需要为每个
运行时间。配置后,实例是线程安全的。
如果您的应用程序访问多个数据库,则可能需要多个实例,这需要多个实例,随后需要多个不同的实例
配置的实例。JdbcTemplateJdbcTemplateJdbcTemplateDataSourcesJdbcTemplate
应该始终在 Spring IoC 容器中配置为 bean。在
第一种情况是将 bean 直接提供给服务;在第二种情况下,它给出了
添加到准备好的模板中。DataSource | 
| 1 | 用 .@Repository | 
| 2 | 使用 .DataSource@Autowired | 
| 3 | 使用 创建新的 .JdbcTemplateDataSource | 
| 1 | 用 .@Repository | 
| 2 | 构造函数注入 .DataSource | 
| 3 | 使用 创建新的 .JdbcTemplateDataSource | 
用NamedParameterJdbcTemplate
该类添加了对 JDBC 语句编程的支持
通过使用命名参数,而不是仅使用经典
placeholder ( ) 参数。该类包装 a 并委托 wraps 完成其大部分工作。这
部分仅描述类中不同的区域
从自身 — 即使用 named 对 JDBC 语句进行编程
参数。以下示例演示如何使用:NamedParameterJdbcTemplate'?'NamedParameterJdbcTemplateJdbcTemplateJdbcTemplateNamedParameterJdbcTemplateJdbcTemplateNamedParameterJdbcTemplate
- 
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)!!
}
请注意,在分配给变量的值中使用了命名参数表示法,以及插入变量(类型 )的相应值。sqlnamedParametersMapSqlParameterSource
或者,您也可以使用基于 的样式将命名参数及其相应的值传递给实例。剩余的
由 暴露 和 由类实现的方法遵循类似的模式,此处不作介绍。NamedParameterJdbcTemplateMapNamedParameterJdbcOperationsNamedParameterJdbcTemplate
以下示例显示了基于 -的样式的用法: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)!!
}
一个与 相关的不错的功能(并且存在于相同的
Java 包)是接口。您已经看到了一个
在前面的代码片段之一(类)中实现此接口。An 是命名参数的源
值设置为 .该类是一个
简单的实现,它是围绕 的适配器,其中键
是参数名称,值是参数值。NamedParameterJdbcTemplateSqlParameterSourceMapSqlParameterSourceSqlParameterSourceNamedParameterJdbcTemplateMapSqlParameterSourcejava.util.Map
另一个实现是 class.此类包装一个任意 JavaBean(即
遵守
JavaBean 约定),并使用包装的 JavaBean 的属性作为源
的命名参数值。SqlParameterSourceBeanPropertySqlParameterSource
以下示例显示了一个典型的 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)
以下示例使用 a 返回
上例所示的类的成员: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)!!
}
请记住,该类包装了一个经典模板。如果您需要访问包装的实例以访问
功能,则可以使用该方法通过接口访问 Wrapped。NamedParameterJdbcTemplateJdbcTemplateJdbcTemplateJdbcTemplategetJdbcOperations()JdbcTemplateJdbcOperations
另请参见JdbcTemplate最佳实践,以获取有关在应用程序上下文中使用该类的指南。NamedParameterJdbcTemplate
统一的 JDBC 查询/更新操作:JdbcClient
从 6.1 开始,命名参数语句和 positional
常规的 parameter 语句可通过统一的客户端 API 获得
使用 Fluent 交互模型。NamedParameterJdbcTemplateJdbcTemplate
例如,使用位置参数:
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();
除了 custom 之外,您还可以指定要映射到的类。
例如,假设 has 和 properties
作为 Record 类、自定义构造函数、Bean Properties 或 Plain Fields:RowMapperActorfirstNamelastName
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,它
provides 和 properties,例如上面的类:firstNamelastNameActor
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
		.paramSource(new Actor("Leonor", "Watling")
		.update();
参数的自动类映射以及上面的查询结果是
通过 Implicit 和 Strategies 提供,这些策略也可用于直接使用。它们可以作为常见的替代品
for 和 /,
也与和他们自己。ActorSimplePropertySqlParameterSourceSimplePropertyRowMapperBeanPropertySqlParameterSourceBeanPropertyRowMapperDataClassRowMapperJdbcTemplateNamedParameterJdbcTemplate
JdbcClient是 JDBC 查询/更新语句的灵活但简化的外观。
高级功能(如批量插入和存储过程调用)通常需要
额外的自定义:考虑 Spring 的 and classes 或
对于 中不可用的任何此类功能,请直接使用。SimpleJdbcInsertSimpleJdbcCallJdbcTemplateJdbcClient | 
JdbcClient是 JDBC 查询/更新语句的灵活但简化的外观。
高级功能(如批量插入和存储过程调用)通常需要
额外的自定义:考虑 Spring 的 and classes 或
对于 中不可用的任何此类功能,请直接使用。SimpleJdbcInsertSimpleJdbcCallJdbcTemplateJdbcClient | 
用SQLExceptionTranslator
SQLExceptionTranslator是由可以翻译的类实现的接口
在 s 和 Spring 自己的 ,
这在数据访问策略方面是不可知的。实现可以是通用的(对于
示例,使用 JDBC 的 SQLState 代码)或专有代码(例如,使用 Oracle 错误
代码)以获得更高的精度。此异常转换机制在
common 和入口点
propagate 而不是 。SQLExceptionorg.springframework.dao.DataAccessExceptionJdbcTemplateJdbcTransactionManagerSQLExceptionDataAccessException
从 6.0 开始,默认的异常转换器是 ,
通过一些额外的检查来检测 JDBC 4 子类,并使用回退
到 内省 通过 。这通常是
足以进行常见的数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用 as
下面描述,可能带有自定义错误代码映射。SQLExceptionSubclassTranslatorSQLExceptionSQLStateSQLStateSQLExceptionTranslatorSQLErrorCodeSQLExceptionTranslator | 
SQLErrorCodeSQLExceptionTranslator是 当名为 的 file 存在于根
的 classpath 中。此实施使用特定的供应商代码。它比 OR 子类翻译更精确。错误代码翻译基于
on 保存在名为 .这个类被创建并
由 填充 ,它(顾名思义)是
根据名为 的配置文件的内容创建 。此文件填充了供应商代码,并基于从 .实际代码
数据库。SQLExceptionTranslatorsql-error-codes.xmlSQLStateSQLExceptionSQLErrorCodesSQLErrorCodesFactorySQLErrorCodessql-error-codes.xmlDatabaseProductNameDatabaseMetaData
按以下顺序应用匹配规则:SQLErrorCodeSQLExceptionTranslator
- 
由子类实现的任何自定义翻译。通常,使用提供的混凝土,因此此规则不适用。它 仅当您实际提供了 subclass 实现时适用。
SQLErrorCodeSQLExceptionTranslator - 
提供的接口的任何自定义实现 作为类的属性。
SQLExceptionTranslatorcustomSqlExceptionTranslatorSQLErrorCodes - 
搜索类的实例列表(为类的属性提供)以查找匹配项。
CustomSQLErrorCodesTranslationcustomTranslationsSQLErrorCodes - 
应用错误代码匹配。
 - 
使用回退转换器。 是默认回退 在线翻译。如果此翻译不可用,则下一个回退翻译器为 这。
SQLExceptionSubclassTranslatorSQLStateSQLExceptionTranslator 
默认情况下,用于定义错误代码和自定义
异常翻译。它们在名为
classpath 的 URL,并且匹配的实例基于数据库进行定位
name 来自正在使用的数据库的数据库元数据。SQLErrorCodesFactorysql-error-codes.xmlSQLErrorCodes | 
您可以扩展 ,如下例所示: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
	}
}
在前面的示例中,特定的错误代码 () 被转换,而
其他错误留给 default translator 实现进行转换。
要使用此自定义转换器,您必须通过
方法,并且必须对所有
需要此转换器的数据访问处理。以下示例显示了
如何使用此自定义转换器:-12345JdbcTemplatesetExceptionTranslatorJdbcTemplate
- 
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
从 6.0 开始,默认的异常转换器是 ,
通过一些额外的检查来检测 JDBC 4 子类,并使用回退
到 内省 通过 。这通常是
足以进行常见的数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用 as
下面描述,可能带有自定义错误代码映射。SQLExceptionSubclassTranslatorSQLExceptionSQLStateSQLStateSQLExceptionTranslatorSQLErrorCodeSQLExceptionTranslator | 
默认情况下,用于定义错误代码和自定义
异常翻译。它们在名为
classpath 的 URL,并且匹配的实例基于数据库进行定位
name 来自正在使用的数据库的数据库元数据。SQLErrorCodesFactorysql-error-codes.xmlSQLErrorCodes | 
Running 语句
运行 SQL 语句需要的代码非常少。您需要 a 和 a ,包括随 .以下示例显示了您需要为最小但
完全功能类来创建新表:DataSourceJdbcTemplateJdbcTemplate
- 
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))")
	}
}
运行查询
某些查询方法返回单个值。从
一行,请使用 .后者将返回的 JDBC 转换为
作为参数传入的 Java 类。如果类型转换无效,则引发 an。以下示例包含两个
query 方法,一个用于 an,一个用于查询 a:queryForObject(..)TypeInvalidDataAccessApiUsageExceptionintString
- 
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")
}
除了单个结果查询方法之外,还有几个方法返回一个带有
条目。最通用的方法是 ,
,它返回一个 where each element is a 包含每列的一个条目,
使用列名作为键。如果向前面的示例添加方法以检索
列表中,它可能如下所示:queryForList(..)ListMap
- 
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")
}
返回的列表将类似于以下内容:
[{name=Bob, id=1}, {name=Mary, id=2}]
更新数据库
以下示例更新某个主键的列:
- 
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 语句具有行参数的占位符。您可以传递参数值 in 作为 varargs 或对象数组。因此,您应该显式包装 primitives 在 primitive wrapper 类中,或者您应该使用 auto-boxing。
检索自动生成的密钥
一种便捷的方法支持检索由
数据库。此支持是 JDBC 3.0 标准的一部分。请参阅
规格。该方法将 a 作为其第一个
参数,这是指定所需 INSERT 语句的方式。另一个
argument 是一个 ,其中包含从
更新。没有标准的单一方法来创建适当的 (这解释了为什么方法签名是这样的)。以下示例有效
在 Oracle 上,但可能无法在其他平台上工作:update()PreparedStatementCreatorKeyHolderPreparedStatement
- 
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