本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理。 包括错误处理。它包括以下主题:
用JdbcTemplate
JdbcTemplate
是 JDBC 核心包中的中心类。它处理
创建和释放资源,这有助于避免常见错误,例如
忘记关闭连接。它执行核心JDBC的基本任务
工作流(如语句创建和执行),将应用程序代码留给提供
SQL 并提取结果。班级:JdbcTemplate
-
运行 SQL 查询
-
更新语句和存储过程调用
-
对实例进行迭代并提取返回的参数值。
ResultSet
-
捕获 JDBC 异常,并将其转换为通用的、信息量更大的异常 包中定义的层次结构。(请参阅一致的异常层次结构。
org.springframework.dao
当您使用 for 代码时,只需实现回调即可
接口,给他们一个明确定义的契约。给定类提供的 a,回调接口将创建一个准备好的
语句,提供 SQL 和任何必要的参数。创建可调用语句的接口也是如此。该接口从 .JdbcTemplate
Connection
JdbcTemplate
PreparedStatementCreator
CallableStatementCreator
RowCallbackHandler
ResultSet
您可以通过直接实例化在 DAO 实现中使用
引用,也可以在 Spring IoC 容器中配置它并将其提供给
DAO 作为 Bean 参考。JdbcTemplate
DataSource
应始终将其配置为 Spring IoC 容器中的 bean。在
第一种情况是直接将 Bean 提供给服务;在第二种情况下,它给出
到准备好的模板。DataSource |
此类发出的所有 SQL 都记录在类别下的级别
对应于模板实例的完全限定类名(通常为 ,但如果使用类的自定义子类,则可能会有所不同)。DEBUG
JdbcTemplate
JdbcTemplate
以下各节提供了一些用法示例。这些例子
不是 公开的所有功能的详尽列表。
有关这一点,请参阅随附的 javadoc。JdbcTemplate
JdbcTemplate
查询 (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 表达式中存在的重复项,并且
将它们提取到单个字段中,然后可以根据需要由 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)
}
将 (, 和 ) 更新为INSERT
UPDATE
DELETE
JdbcTemplate
您可以使用该方法执行插入、更新和删除操作。
参数值通常以变量参数的形式提供,或者作为对象数组提供。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。因此,
方法通常用于 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(或存储库)。
是有状态的,因为它保持对 的引用,但
此状态不是会话状态。JdbcTemplate
JdbcTemplate
JdbcTemplate
DataSource
使用类(以及关联的 NamedParameterJdbcTemplate
类)时的常见做法是
在 Spring 配置文件中配置一个,然后进行依赖注入
将 Bean 共享到您的 DAO 类中。创建于
. 的 setter这导致了类似于以下内容的 DAO:JdbcTemplate
DataSource
DataSource
JdbcTemplate
DataSource
-
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
方法。以下示例演示如何执行此操作:@Repository
DataSource
@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 | 用 注释 setter 方法。DataSource @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 的类和各种 JDBC 支持的 DAO 类
从它扩展,您的子类从该类继承一个方法。您可以选择是否继承此类。该课程仅为方便起见而提供。JdbcDaoSupport
setDataSource(..)
JdbcDaoSupport
JdbcDaoSupport
无论您选择使用上述哪种模板初始化样式(或
不是),很少需要为每个类创建一个新实例
要运行 SQL 的时间。配置后,实例是线程安全的。
如果应用程序访问多个数据库,则可能需要多个实例,这需要多个实例,随后需要多个实例
已配置的实例。JdbcTemplate
JdbcTemplate
JdbcTemplate
DataSources
JdbcTemplate
应始终将其配置为 Spring IoC 容器中的 bean。在
第一种情况是直接将 Bean 提供给服务;在第二种情况下,它给出
到准备好的模板。DataSource |
1 | 用 批注类。@Repository |
2 | 用 注释 setter 方法。DataSource @Autowired |
3 | 使用 .JdbcTemplate DataSource |
1 | 用 批注类。@Repository |
2 | 构造函数注入 .DataSource |
3 | 使用 .JdbcTemplate DataSource |
用NamedParameterJdbcTemplate
该类增加了对 JDBC 语句编程的支持
通过使用命名参数,而不是仅使用经典 JDBC 语句进行编程
占位符 ( ) 参数。该类包装 a 并委托给包装以完成其大部分工作。这
部分仅描述类中不同的区域
从本身 — 即,使用 named 对 JDBC 语句进行编程
参数。以下示例演示如何使用:NamedParameterJdbcTemplate
'?'
NamedParameterJdbcTemplate
JdbcTemplate
JdbcTemplate
NamedParameterJdbcTemplate
JdbcTemplate
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
或者,您可以使用基于 -的样式将命名参数及其对应值传递给实例。剩下的
由 类公开和实现的方法遵循类似的模式,此处不作介绍。NamedParameterJdbcTemplate
Map
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)!!
}
一个很好的功能与 (并存在于同一个
Java package) 是接口。您已经看过一个示例
在前面的代码片段之一(类)中实现此接口。An 是命名参数的源
值设置为 .该类是一个
简单的实现,即围绕一个适配器,其中键
是参数名称,值是参数值。NamedParameterJdbcTemplate
SqlParameterSource
MapSqlParameterSource
SqlParameterSource
NamedParameterJdbcTemplate
MapSqlParameterSource
java.util.Map
另一个实现是类。此类包装任意 JavaBean(即
坚持
JavaBean 约定),并使用包装的 JavaBean 的属性作为源
的命名参数值。SqlParameterSource
BeanPropertySqlParameterSource
以下示例显示了一个典型的 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)!!
}
请记住,该类包装了一个经典模板。如果您需要访问打包的实例才能访问
仅存在于类中的功能,您可以使用该方法通过接口访问包装。NamedParameterJdbcTemplate
JdbcTemplate
JdbcTemplate
JdbcTemplate
getJdbcOperations()
JdbcTemplate
JdbcOperations
另请参阅 JdbcTemplate
最佳实践,了解在应用程序上下文中使用该类的准则。NamedParameterJdbcTemplate
统一的JDBC查询/更新操作:JdbcClient
从 6.1 开始,命名参数语句 和 位置
常规的参数语句可通过统一的客户端 API 获得
具有流畅的交互模型。NamedParameterJdbcTemplate
JdbcTemplate
例如,使用位置参数:
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();
除了自定义 ,您还可以指定要映射到的类。
例如,假设具有 和 属性
作为记录类、自定义构造函数、Bean 属性或纯字段:RowMapper
Actor
firstName
lastName
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();
或者带有命名参数的 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();
参数的自动类映射以及上面的查询结果是
通过隐式和策略提供,这些策略也可用于直接使用。它们可以作为常见的替代品
for 和 /,
也和他们自己。Actor
SimplePropertySqlParameterSource
SimplePropertyRowMapper
BeanPropertySqlParameterSource
BeanPropertyRowMapper
DataClassRowMapper
JdbcTemplate
NamedParameterJdbcTemplate
JdbcClient 是 JDBC 查询/更新语句的灵活但简化的外观。
批处理插入和存储过程调用等高级功能通常需要
额外的定制:考虑 Spring 的和类或
直接用于 中不可用的任何此类功能。SimpleJdbcInsert SimpleJdbcCall JdbcTemplate JdbcClient |
JdbcClient 是 JDBC 查询/更新语句的灵活但简化的外观。
批处理插入和存储过程调用等高级功能通常需要
额外的定制:考虑 Spring 的和类或
直接用于 中不可用的任何此类功能。SimpleJdbcInsert SimpleJdbcCall JdbcTemplate JdbcClient |
用SQLExceptionTranslator
SQLExceptionTranslator
是一个接口,由可以翻译的类实现
在 s 和 Spring 自己的之间 ,
这在数据访问策略方面是不可知的。实现可以是通用的(对于
例如,将 SQLState 代码用于 JDBC)或专有代码(例如,使用 Oracle 错误
codes) 以获得更高的精度。这种异常转换机制用于
不常见的和不这样做的入口点
传播,而是.SQLException
org.springframework.dao.DataAccessException
JdbcTemplate
JdbcTransactionManager
SQLException
DataAccessException
从 6.0 开始,默认异常转换器为 ,
通过一些额外的检查和回退来检测 JDBC 4 子类
通过.这通常是
足以进行通用数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用
下面介绍,可能使用自定义错误代码映射。SQLExceptionSubclassTranslator SQLException SQLState SQLStateSQLExceptionTranslator SQLErrorCodeSQLExceptionTranslator |
SQLErrorCodeSQLExceptionTranslator
是当根目录中存在名为
类路径。此实现使用特定的供应商代码。它比 或子类翻译更精确。错误代码转换基于
在名为 的 JavaBean 类型类中保存的代码上。创建此类并
由 ,它(顾名思义)是 的工厂
基于名为 的配置文件的内容创建。此文件填充了供应商代码,并基于 .实际代码
使用您正在使用的数据库。SQLExceptionTranslator
sql-error-codes.xml
SQLState
SQLException
SQLErrorCodes
SQLErrorCodesFactory
SQLErrorCodes
sql-error-codes.xml
DatabaseProductName
DatabaseMetaData
按以下顺序应用匹配规则:SQLErrorCodeSQLExceptionTranslator
-
由子类实现的任何自定义翻译。通常,使用的是提供的混凝土,因此此规则不适用。它 仅当您实际提供了子类实现时才适用。
SQLErrorCodeSQLExceptionTranslator
-
提供的接口的任何自定义实现 作为类的属性。
SQLExceptionTranslator
customSqlExceptionTranslator
SQLErrorCodes
-
将搜索类的实例列表(为类的属性提供)以查找匹配项。
CustomSQLErrorCodesTranslation
customTranslations
SQLErrorCodes
-
应用错误代码匹配。
-
使用回退转换器。 是默认回退 在线翻译。如果此翻译不可用,则下一个回退翻译器是 这。
SQLExceptionSubclassTranslator
SQLStateSQLExceptionTranslator
默认情况下,用于定义错误代码和自定义
例外翻译。它们在名为
classpath,并且匹配的实例基于数据库进行定位
名称来自正在使用的数据库的数据库元数据。SQLErrorCodesFactory sql-error-codes.xml SQLErrorCodes |
您可以扩展 ,如以下示例所示: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
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
从 6.0 开始,默认异常转换器为 ,
通过一些额外的检查和回退来检测 JDBC 4 子类
通过.这通常是
足以进行通用数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用
下面介绍,可能使用自定义错误代码映射。SQLExceptionSubclassTranslator SQLException SQLState SQLStateSQLExceptionTranslator SQLErrorCodeSQLExceptionTranslator |
默认情况下,用于定义错误代码和自定义
例外翻译。它们在名为
classpath,并且匹配的实例基于数据库进行定位
名称来自正在使用的数据库的数据库元数据。SQLErrorCodesFactory sql-error-codes.xml SQLErrorCodes |
运行语句
运行 SQL 语句需要很少的代码。您需要 a 和 a ,包括随 .以下示例显示了您需要包含的最小但
创建新表的全功能类: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))")
}
}
运行查询
某些查询方法返回单个值。从中检索计数或特定值
一行,使用 .后者将返回的 JDBC 转换为
作为参数传入的 Java 类。如果类型转换无效,则会抛出 an。下面的示例包含两个
查询方法,一个用于 an,一个用于查询 a:queryForObject(..)
Type
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")
}
返回的列表将类似于以下内容:
[{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 语句具有行参数的占位符。您可以传递参数值 作为 varargs 或对象数组。因此,您应该显式包装基元 在原始包装器类中,或者您应该使用自动装箱。
检索自动生成的密钥
一种方便的方法支持检索由
数据库。此支持是 JDBC 3.0 标准的一部分。请参阅第 13.6 章
详见规格。该方法以 a 为第一个
参数,这是指定所需 insert 语句的方式。其他
参数是一个 ,它包含从
更新。没有标准的单一方法来创建适当的(这解释了为什么方法签名是这样的)。以下示例有效
在 Oracle 上,但可能无法在其他平台上运行:update()
PreparedStatementCreator
KeyHolder
PreparedStatement
-
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