此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13! |
此版本仍在开发中,尚未被视为稳定版本。最新的稳定版本请使用 Spring Framework 6.1.13! |
本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理。 包括错误处理。它包括以下主题:
用JdbcTemplate
JdbcTemplate
是 JDBC 核心包中的中心类。它处理
创建和释放资源,这有助于您避免常见错误,例如
忘记关闭连接。它执行核心 JDBC 的基本任务
工作流(例如语句创建和执行),让应用程序代码提供
SQL 并提取结果。类:JdbcTemplate
-
运行 SQL 查询
-
更新语句和存储过程调用
-
对实例执行迭代并提取返回的参数值。
ResultSet
-
捕获 JDBC 异常并将它们转换为泛型、信息量更大的异常 hierarchy 中定义的 Hierarchy。(请参阅一致的异常层次结构。
org.springframework.dao
当您使用 for your code 时,您只需实现 callback
接口,为它们提供明确定义的 Contract。给定类提供的 a 后,回调接口会创建一个准备好的
语句,提供 SQL 和任何必要的参数。这同样适用于创建可调用语句的接口。该接口从 .JdbcTemplate
Connection
JdbcTemplate
PreparedStatementCreator
CallableStatementCreator
RowCallbackHandler
ResultSet
您可以通过直接实例化在 DAO 实现中使用
替换为引用,或者您可以在 Spring IoC 容器中配置它并将其提供给
DAO 作为 bean 引用。JdbcTemplate
DataSource
应该始终在 Spring IoC 容器中配置为 bean。在
第一种情况是将 bean 直接提供给服务;在第二种情况下,它给出了
添加到准备好的模板中。DataSource |
此类发出的所有 SQL 都记录在类别
对应于 Template 实例的完全限定类名(通常为 ,但如果您使用类的自定义子类,则可能会有所不同)。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 表达式中存在的重复项,使用 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)
}
更新 (, , 和 )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。因此,
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(或存储库)。
这是有状态的,因为它维护对 的引用 ,但
此状态不是对话状态。JdbcTemplate
JdbcTemplate
JdbcTemplate
DataSource
使用该类(以及关联的 NamedParameterJdbcTemplate
类)时的常见做法是
在 Spring 配置文件中配置 a,然后 dependency-inject
将 bean 共享到您的 DAO 类中。在
构造函数中 or 的 setter。这导致了类似于以下内容的 DAO:JdbcTemplate
DataSource
DataSource
JdbcTemplate
DataSource
-
Java
-
Kotlin
public class JdbcCorporateEventDao implements CorporateEventDao {
private final JdbcTemplate jdbcTemplate;
public JdbcCorporateEventDao(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...
}
以下示例显示了相应的配置:
-
Java
-
Kotlin
-
Xml
@Bean
JdbcCorporateEventDao corporateEventDao(DataSource dataSource) {
return new JdbcCorporateEventDao(dataSource);
}
@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
@Bean
fun corporateEventDao(dataSource: DataSource) = JdbcCorporateEventDao(dataSource)
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:hsql://localhost:"
username = "sa"
password = ""
}
<bean id="corporateEventDao" class="org.example.jdbc.JdbcCorporateEventDao">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp2.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"/>
显式配置的替代方法是使用组件扫描和注释
支持依赖项注入。在这种情况下,您可以使用 (这使它成为组件扫描的候选者) 来注释类。以下示例显示了如何执行此操作:@Repository
@Repository
public class JdbcCorporateEventRepository implements CorporateEventRepository {
private JdbcTemplate jdbcTemplate;
// Implicitly autowire the DataSource constructor parameter
public JdbcCorporateEventRepository(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
// JDBC-backed implementations of the methods on the CorporateEventRepository follow...
}
以下示例显示了相应的配置:
-
Java
-
Kotlin
-
Xml
@Configuration
@ComponentScan("org.example.jdbc")
public class JdbcCorporateEventRepositoryConfiguration {
@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
}
@Configuration
@ComponentScan("org.example.jdbc")
class JdbcCorporateEventRepositoryConfiguration {
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:hsql://localhost:"
username = "sa"
password = ""
}
}
<!-- Scans within the base package of the application for @Component classes to configure as beans -->
<context:component-scan base-package="org.example.jdbc" />
<bean id="dataSource" class="org.apache.commons.dbcp2.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"/>
如果你使用 Spring 的类和各种 JDBC 支持的 DAO 类
extend 的子类会从该类继承一个 method。您可以选择是否从此类继承。该类仅为方便起见而提供。JdbcDaoSupport
setDataSource(..)
JdbcDaoSupport
JdbcDaoSupport
无论您选择使用上述哪种模板初始化样式(或
not),则很少需要为每个
运行时间。配置后,实例是线程安全的。
如果您的应用程序访问多个数据库,则可能需要多个实例,这需要多个实例,随后需要多个不同的实例
配置的实例。JdbcTemplate
JdbcTemplate
JdbcTemplate
DataSources
JdbcTemplate
应该始终在 Spring IoC 容器中配置为 bean。在
第一种情况是将 bean 直接提供给服务;在第二种情况下,它给出了
添加到准备好的模板中。DataSource |
用NamedParameterJdbcTemplate
该类添加了对 JDBC 语句编程的支持
通过使用命名参数,而不是仅使用经典
placeholder ( ) 参数。该类包装 a 并委托 wraps 完成其大部分工作。这
部分仅描述类中不同的区域
从自身 — 即使用 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 包)是接口。您已经看到了一个
在前面的代码片段之一(类)中实现此接口。An 是命名参数的源
值设置为 .该类是一个
简单的实现,它是围绕 的适配器,其中键
是参数名称,值是参数值。NamedParameterJdbcTemplate
SqlParameterSource
MapSqlParameterSource
SqlParameterSource
NamedParameterJdbcTemplate
MapSqlParameterSource
java.util.Map
另一个实现是 class.此类包装一个任意 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)!!
}
请记住,该类包装了一个经典模板。如果您需要访问包装的实例以访问
功能,则可以使用该方法通过接口访问 Wrapped。NamedParameterJdbcTemplate
JdbcTemplate
JdbcTemplate
JdbcTemplate
getJdbcOperations()
JdbcTemplate
JdbcOperations
另请参见JdbcTemplate
最佳实践,以获取有关在应用程序上下文中使用该类的指南。NamedParameterJdbcTemplate
统一的 JDBC 查询/更新操作:JdbcClient
从 6.1 开始,命名参数语句和 positional
常规的 parameter 语句可通过统一的客户端 API 获得
使用 Fluent 交互模型。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();
除了 custom 之外,您还可以指定要映射到的类。
例如,假设 has 和 properties
作为 Record 类、自定义构造函数、Bean Properties 或 Plain Fields: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();
对于 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,例如上面的类:firstName
lastName
Actor
this.jdbcClient.sql("insert into t_actor (first_name, last_name) values (:firstName, :lastName)")
.paramSource(new Actor("Leonor", "Watling")
.update();
参数的自动类映射以及上面的查询结果是
通过 Implicit 和 Strategies 提供,这些策略也可用于直接使用。它们可以作为常见的替代品
for 和 /,
也与和他们自己。Actor
SimplePropertySqlParameterSource
SimplePropertyRowMapper
BeanPropertySqlParameterSource
BeanPropertyRowMapper
DataClassRowMapper
JdbcTemplate
NamedParameterJdbcTemplate
JdbcClient 是 JDBC 查询/更新语句的灵活但简化的外观。
高级功能(如批量插入和存储过程调用)通常需要
额外的自定义:考虑 Spring 的 and classes 或
对于 中不可用的任何此类功能,请直接使用。SimpleJdbcInsert SimpleJdbcCall JdbcTemplate JdbcClient |
JdbcClient 是 JDBC 查询/更新语句的灵活但简化的外观。
高级功能(如批量插入和存储过程调用)通常需要
额外的自定义:考虑 Spring 的 and classes 或
对于 中不可用的任何此类功能,请直接使用。SimpleJdbcInsert SimpleJdbcCall JdbcTemplate JdbcClient |
用SQLExceptionTranslator
SQLExceptionTranslator
是由可以翻译的类实现的接口
在 s 和 Spring 自己的 ,
这在数据访问策略方面是不可知的。实现可以是通用的(对于
示例,使用 JDBC 的 SQLState 代码)或专有代码(例如,使用 Oracle 错误
代码)以获得更高的精度。此异常转换机制在
common 和入口点
propagate 而不是 。SQLException
org.springframework.dao.DataAccessException
JdbcTemplate
JdbcTransactionManager
SQLException
DataAccessException
从 6.0 开始,默认的异常转换器是 ,
通过一些额外的检查来检测 JDBC 4 子类,并使用回退
到 内省 通过 。这通常是
足以进行常见的数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用 as
下面描述,可能带有自定义错误代码映射。SQLExceptionSubclassTranslator SQLException SQLState SQLStateSQLExceptionTranslator SQLErrorCodeSQLExceptionTranslator |
SQLErrorCodeSQLExceptionTranslator
是 当名为 的 file 存在于根
的 classpath 中。此实施使用特定的供应商代码。它比 OR 子类翻译更精确。错误代码翻译基于
on 保存在名为 .这个类被创建并
由 填充 ,它(顾名思义)是
根据名为 的配置文件的内容创建 。此文件填充了供应商代码,并基于从 .实际代码
数据库。SQLExceptionTranslator
sql-error-codes.xml
SQLState
SQLException
SQLErrorCodes
SQLErrorCodesFactory
SQLErrorCodes
sql-error-codes.xml
DatabaseProductName
DatabaseMetaData
按以下顺序应用匹配规则:SQLErrorCodeSQLExceptionTranslator
-
由子类实现的任何自定义翻译。通常,使用提供的混凝土,因此此规则不适用。它 仅当您实际提供了 subclass 实现时适用。
SQLErrorCodeSQLExceptionTranslator
-
提供的接口的任何自定义实现 作为类的属性。
SQLExceptionTranslator
customSqlExceptionTranslator
SQLErrorCodes
-
搜索类的实例列表(为类的属性提供)以查找匹配项。
CustomSQLErrorCodesTranslation
customTranslations
SQLErrorCodes
-
应用错误代码匹配。
-
使用回退转换器。 是默认回退 在线翻译。如果此翻译不可用,则下一个回退翻译器为 这。
SQLExceptionSubclassTranslator
SQLStateSQLExceptionTranslator
默认情况下,用于定义错误代码和自定义
异常翻译。它们在名为
classpath 的 URL,并且匹配的实例基于数据库进行定位
name 来自正在使用的数据库的数据库元数据。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
}
}
在前面的示例中,特定的错误代码 () 被转换,而
其他错误留给 default translator 实现进行转换。
要使用此自定义转换器,您必须通过
方法,并且必须对所有
需要此转换器的数据访问处理。以下示例显示了
如何使用此自定义转换器:-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 子类,并使用回退
到 内省 通过 。这通常是
足以进行常见的数据库访问,并且不需要特定于供应商的检测。
为了向后兼容,请考虑使用 as
下面描述,可能带有自定义错误代码映射。SQLExceptionSubclassTranslator SQLException SQLState SQLStateSQLExceptionTranslator SQLErrorCodeSQLExceptionTranslator |
默认情况下,用于定义错误代码和自定义
异常翻译。它们在名为
classpath 的 URL,并且匹配的实例基于数据库进行定位
name 来自正在使用的数据库的数据库元数据。SQLErrorCodesFactory sql-error-codes.xml SQLErrorCodes |
Running 语句
运行 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。以下示例包含两个
query 方法,一个用于 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")
}
除了单个结果查询方法之外,还有几个方法返回一个带有
条目。最通用的方法是 ,
,它返回一个 where each element is a 包含每列的一个条目,
使用列名作为键。如果向前面的示例添加方法以检索
列表中,它可能如下所示: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 语句具有行参数的占位符。您可以传递参数值 in 作为 varargs 或对象数组。因此,您应该显式包装 primitives 在 primitive wrapper 类中,或者您应该使用 auto-boxing。
检索自动生成的密钥
一种便捷的方法支持检索由
数据库。此支持是 JDBC 3.0 标准的一部分。请参阅
规格。该方法将 a 作为其第一个
参数,这是指定所需 INSERT 语句的方式。另一个
argument 是一个 ,其中包含从
更新。没有标准的单一方法来创建适当的 (这解释了为什么方法签名是这样的)。以下示例有效
在 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