对于最新的稳定版本,请使用 Spring Framework 6.2.4spring-doc.cadn.net.cn

使用 JDBC 核心类控制基本的 JDBC 处理和错误处理

JdbcTemplate

JdbcTemplate是 JDBC 核心包中的中心类。它处理 创建和释放资源,这有助于您避免常见错误,例如 忘记关闭连接。它执行核心 JDBC 的基本任务 工作流(例如语句创建和执行),让应用程序代码提供 SQL 并提取结果。这JdbcTemplate类:spring-doc.cadn.net.cn

当您使用JdbcTemplate对于您的代码,您只需实现 callback 接口,为它们提供明确定义的 Contract。给定一个ConnectionJdbcTemplate类中,使用PreparedStatementCreatorcallback 接口会创建一个准备好的 语句,提供 SQL 和任何必要的参数。对于CallableStatementCreator接口,用于创建可调用语句。这RowCallbackHandlerinterface 从ResultSet.spring-doc.cadn.net.cn

您可以使用JdbcTemplate在 DAO 实现中通过直接实例化 替换为DataSource引用,或者你可以在 Spring IoC 容器中配置它并将其提供给 DAO 作为 bean 引用。spring-doc.cadn.net.cn

DataSource应该始终配置为 Spring IoC 容器中的 bean。在 第一种情况是将 bean 直接提供给服务;在第二种情况下,它给出了 添加到准备好的模板中。

此类发出的所有 SQL 都记录在DEBUG类别下的级别 对应于模板实例的完全限定类名(通常为JdbcTemplate,但如果您使用JdbcTemplate类)。spring-doc.cadn.net.cn

以下部分提供了一些示例JdbcTemplate用法。这些例子 并不是JdbcTemplate. 有关此内容,请参阅随附的 javadocspring-doc.cadn.net.cn

查询 (SELECT)

以下查询获取关系中的行数:spring-doc.cadn.net.cn

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!

以下查询使用 bind 变量:spring-doc.cadn.net.cn

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:spring-doc.cadn.net.cn

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))!!

以下查询查找并填充单个域对象:spring-doc.cadn.net.cn

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"))
	}

以下查询查找并填充域对象列表:spring-doc.cadn.net.cn

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 删除两者中存在的重复项RowMapperlambda 表达式和 将它们提取到一个字段中,然后 DAO 方法可以根据需要引用该字段。 例如,最好按如下方式编写前面的代码片段:spring-doc.cadn.net.cn

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,UPDATEDELETE) 替换为JdbcTemplate

您可以使用update(..)方法执行 INSERT、UPDATE 和 DELETE作。 参数值通常作为变量参数提供,或者作为对象数组提供。spring-doc.cadn.net.cn

以下示例插入一个新条目:spring-doc.cadn.net.cn

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")

以下示例更新现有条目:spring-doc.cadn.net.cn

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)

以下示例删除条目:spring-doc.cadn.net.cn

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 语句。它严重过载了采用 回调接口、绑定变量数组等。以下示例创建一个 桌子:spring-doc.cadn.net.cn

this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")

以下示例调用存储过程:spring-doc.cadn.net.cn

this.jdbcTemplate.update(
		"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
		Long.valueOf(unionId));
jdbcTemplate.update(
		"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
		unionId.toLong())

稍后将介绍更复杂的存储过程支持。spring-doc.cadn.net.cn

JdbcTemplate最佳实践

的实例JdbcTemplate类是线程安全的,一旦配置。这是 重要,因为这意味着您可以配置JdbcTemplate然后安全地将此共享引用注入多个 DAO(或存储库)。 这JdbcTemplate是有状态的,因为它维护了对DataSource但 此状态不是对话状态。spring-doc.cadn.net.cn

使用JdbcTemplate类(以及关联的NamedParameterJdbcTemplateclass) 为 配置DataSource在 Spring 配置文件中,然后 dependency-inject 那个分享了DataSourcebean 添加到您的 DAO 类中。这JdbcTemplate在 的 setterDataSource.这导致了类似于以下内容的 DAO:spring-doc.cadn.net.cn

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 配置:spring-doc.cadn.net.cn

<?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(这使它成为组件扫描的候选者)并注释DataSourcesetter method 替换为@Autowired.以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@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 注释DataSourcesetter 方法替换为@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 配置:spring-doc.cadn.net.cn

<?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类。您可以选择是否从此类继承。这JdbcDaoSupportclass 仅为方便起见而提供。spring-doc.cadn.net.cn

无论您选择使用上述哪种模板初始化样式(或 not),则很少需要创建JdbcTemplate每个类 运行时间。配置后,JdbcTemplate实例是线程安全的。 如果您的应用程序访问多个数据库,则可能需要多个JdbcTemplate实例,这需要多个DataSources随后,多个不同的 配置JdbcTemplate实例。spring-doc.cadn.net.cn

NamedParameterJdbcTemplate

NamedParameterJdbcTemplateclass 添加了对 JDBC 语句编程的支持 通过使用命名参数,而不是仅使用经典 placeholder ('?') 参数。这NamedParameterJdbcTemplate类将JdbcTemplate和委托给包装的JdbcTemplate来完成它的大部分工作。这 部分仅介绍了NamedParameterJdbcTemplate类 从JdbcTemplate本身 — 即使用 named 对 JDBC 语句进行编程 参数。以下示例演示如何使用NamedParameterJdbcTemplate:spring-doc.cadn.net.cn

// 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变量和插入到namedParametersvariable(类型MapSqlParameterSource).spring-doc.cadn.net.cn

或者,您可以将命名参数及其相应的值传递给NamedParameterJdbcTemplate实例Map-基于样式。剩余的 由NamedParameterJdbcOperations并由NamedParameterJdbcTemplate类遵循类似的模式,此处不作介绍。spring-doc.cadn.net.cn

以下示例显示了Map-based 样式:spring-doc.cadn.net.cn

// 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.这MapSqlParameterSourceclass 是一个 简单的实现,它是围绕java.util.Map,其中键 是参数名称,值是参数值。spring-doc.cadn.net.cn

另一个SqlParameterSourceimplementation 是BeanPropertySqlParameterSource类。此类包装一个任意 JavaBean(即 遵守 JavaBean 约定),并使用包装的 JavaBean 的属性作为源 的命名参数值。spring-doc.cadn.net.cn

以下示例显示了一个典型的 JavaBean:spring-doc.cadn.net.cn

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要返回 上例所示的类的成员:spring-doc.cadn.net.cn

// 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接口。spring-doc.cadn.net.cn

另请参阅JdbcTemplate最佳实践有关使用NamedParameterJdbcTemplate类。spring-doc.cadn.net.cn

统一的 JDBC 查询/更新作:JdbcClient

从 6.1 开始,NamedParameterJdbcTemplate和位置 parameter 语句的常规JdbcTemplate可通过统一的客户端 API 使用 使用 Fluent 交互模型。spring-doc.cadn.net.cn

例如,使用位置参数:spring-doc.cadn.net.cn

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();
}

例如,使用命名参数:spring-doc.cadn.net.cn

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功能也可用,具有灵活的结果分辨率:spring-doc.cadn.net.cn

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具有firstNamelastName性能 作为 Record 类、自定义构造函数、Bean Properties 或 Plain Fields:spring-doc.cadn.net.cn

List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
		.query(Actor.class)
		.list();

具有必需的单个对象结果:spring-doc.cadn.net.cn

Actor actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
		.param(1212L)
		.query(Actor.class)
		.single();

使用java.util.Optional结果:spring-doc.cadn.net.cn

Optional<Actor> actor = this.jdbcClient.sql("select first_name, last_name from t_actor where id = ?")
		.param(1212L)
		.query(Actor.class)
		.optional();

对于 update 语句:spring-doc.cadn.net.cn

this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (?, ?)")
		.param("Leonor").param("Watling")
		.update();

或者带有命名参数的 update 语句:spring-doc.cadn.net.cn

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,它 提供firstNamelastName属性,例如Actorclass 的spring-doc.cadn.net.cn

this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
		.paramSource(new Actor("Leonor", "Watling")
		.update();

自动Actor参数的类映射以及上面的查询结果是 通过 Implicit 提供SimplePropertySqlParameterSourceSimplePropertyRowMapper策略,这些策略也可以直接使用。它们可以作为常见的替代品 为BeanPropertySqlParameterSourceBeanPropertyRowMapper/DataClassRowMapper, 还与JdbcTemplateNamedParameterJdbcTemplate他们自己。spring-doc.cadn.net.cn

JdbcClient是 JDBC 查询/更新语句的灵活但简化的外观。 高级功能(如批量插入和存储过程调用)通常需要 额外的自定义:考虑 Spring 的SimpleJdbcInsertSimpleJdbcCall类或 普通直接JdbcTemplate用于 中不可用的任何此类功能JdbcClient.

SQLExceptionTranslator

SQLExceptionTranslator是由可以翻译的类实现的接口 之间SQLExceptions 和 Spring 自己的org.springframework.dao.DataAccessException, 这在数据访问策略方面是不可知的。实现可以是通用的(对于 示例,使用 JDBC 的 SQLState 代码)或专有代码(例如,使用 Oracle 错误 代码)以获得更高的精度。此异常转换机制在 常见JdbcTemplateJdbcTransactionManager不 传播SQLException而是DataAccessException.spring-doc.cadn.net.cn

从 6.0 开始,默认的异常转换器是SQLExceptionSubclassTranslator, 检测 JDBC 4SQLException子类,并带有 fallback 自SQLState内省SQLStateSQLExceptionTranslator.这通常是 足以进行常见的数据库访问,并且不需要特定于供应商的检测。 为了向后兼容,请考虑使用SQLErrorCodeSQLExceptionTranslator如 下面描述,可能带有自定义错误代码映射。

SQLErrorCodeSQLExceptionTranslatorSQLExceptionTranslator,当名为sql-error-codes.xml存在于根 的 classpath 中。此实施使用特定的供应商代码。它比SQLStateSQLException子类 translation 的错误代码翻译基于 在名为SQLErrorCodes.这个类被创建并 由SQLErrorCodesFactory,它(顾名思义)是 创建SQLErrorCodes基于名为sql-error-codes.xml.此文件填充了供应商代码,并基于DatabaseProductName摘自DatabaseMetaData.实际代码 数据库。spring-doc.cadn.net.cn

SQLErrorCodeSQLExceptionTranslator按以下顺序应用匹配规则:spring-doc.cadn.net.cn

  1. 由子类实现的任何自定义翻译。通常,提供的混凝土SQLErrorCodeSQLExceptionTranslator,因此此规则不适用。它 仅当您实际提供了 subclass 实现时适用。spring-doc.cadn.net.cn

  2. 任何SQLExceptionTranslator提供的接口 作为customSqlExceptionTranslator属性的SQLErrorCodes类。spring-doc.cadn.net.cn

  3. CustomSQLErrorCodesTranslation类(为customTranslations属性的SQLErrorCodes类)中搜索匹配项。spring-doc.cadn.net.cn

  4. 应用错误代码匹配。spring-doc.cadn.net.cn

  5. 使用回退转换器。SQLExceptionSubclassTranslator是默认回退 在线翻译。如果此翻译不可用,则下一个回退翻译器为 这SQLStateSQLExceptionTranslator.spring-doc.cadn.net.cn

SQLErrorCodesFactory默认用于定义错误代码,自定义 异常翻译。它们在名为sql-error-codes.xml从 classpath 和匹配的SQLErrorCodes实例 是基于数据库定位的 name 来自正在使用的数据库的数据库元数据。

您可以扩展SQLErrorCodeSQLExceptionTranslator,如下例所示:spring-doc.cadn.net.cn

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对于所有 需要此转换器的数据访问处理。以下示例显示了 如何使用此自定义转换器:spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

Running 语句

运行 SQL 语句需要的代码非常少。您需要一个DataSource以及JdbcTemplate,包括随JdbcTemplate.以下示例显示了您需要为最小但 完全功能类来创建新表:spring-doc.cadn.net.cn

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:spring-doc.cadn.net.cn

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:spring-doc.cadn.net.cn

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:spring-doc.cadn.net.cn

[{name=Bob, id=1}, {name=Mary, id=2}]

Updating the Database

The following example updates a column for a certain primary key:spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn

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:spring-doc.cadn.net.cn

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