此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Framework 6.2.10! |
使用 JDBC 核心类控制基本的 JDBC 处理和错误处理
本节介绍如何使用 JDBC 核心类来控制基本的 JDBC 处理, 包括错误处理。它包括以下主题:
用JdbcTemplate
JdbcTemplate
是 JDBC 核心包中的中心类。它处理
创建和释放资源,这有助于避免常见错误,例如
忘记关闭连接。它执行核心 JDBC 的基本任务
工作流(如语句创建和执行),让应用程序代码提供
SQL 和提取结果。这JdbcTemplate
类:
-
运行 SQL 查询
-
更新语句和存储过程调用
-
执行迭代
ResultSet
实例和返回参数值的提取。 -
捕获 JDBC 异常,并将其转换为通用的、信息量更大的异常 层次结构在
org.springframework.dao
包。(请参阅一致异常层次结构。
当您使用JdbcTemplate
对于您的代码,您只需实现回调
接口,为它们提供明确定义的契约。给定一个Connection
由JdbcTemplate
类,则PreparedStatementCreator
callback 接口创建一个准备好的
语句,提供 SQL 和任何必要的参数。对于CallableStatementCreator
接口,它创建可调用语句。这RowCallbackHandler
接口从每个行中提取值ResultSet
.
您可以使用JdbcTemplate
通过直接实例化在 DAO 实现中
使用DataSource
引用,或者您可以在 Spring IoC 容器中配置它并将其提供给
DAO 作为 bean 参考。
这DataSource 应始终配置为 Spring IoC 容器中的 bean。在
第一种情况是 bean 直接提供给服务;在第二种情况下,它给出了
到准备好的模板。 |
此类发出的所有 SQL 都记录在DEBUG
类别下的级别
对应于模板实例的完全限定类名(通常JdbcTemplate
,但如果您使用JdbcTemplate
类)。
以下部分提供了一些示例JdbcTemplate
用法。这些例子
不是JdbcTemplate
.
请参阅随附的 javadoc 。
查询 (SELECT
)
以下查询获取关系中的行数:
-
Java
-
Kotlin
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);
val rowCount = jdbcTemplate.queryForObject<Int>("select count(*) from t_actor")!!
以下查询使用绑定变量:
-
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"))
如果最后两个代码片段实际上存在于同一个应用程序中,它将使
消除两者中存在的重复的意义RowMapper
lambda 表达式和
将它们提取到单个字段中,然后可以根据需要由 DAO 方法引用。
例如,最好按如下方式编写前面的代码片段:
-
Java
-
Kotlin
private final RowMapper<Actor> actorRowMapper = (resultSet, rowNum) -> {
Actor actor = new Actor();
actor.setFirstName(resultSet.getString("first_name"));
actor.setLastName(resultSet.getString("last_name"));
return actor;
};
public List<Actor> findAllActors() {
return this.jdbcTemplate.query("select first_name, last_name from t_actor", actorRowMapper);
}
val actorMapper = RowMapper<Actor> { rs: ResultSet, rowNum: Int ->
Actor(rs.getString("first_name"), rs.getString("last_name"))
}
fun findAllActors(): List<Actor> {
return jdbcTemplate.query("select first_name, last_name from t_actor", actorMapper)
}
更新 (INSERT
,UPDATE
和DELETE
) 替换为JdbcTemplate
您可以使用update(..)
执行插入、更新和删除作的方法。
参数值通常作为变量参数提供,或者作为对象数组提供。
以下示例插入一个新条目:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");
jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling")
以下示例更新现有条目:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);
jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L)
以下示例删除条目:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"delete from t_actor where id = ?",
Long.valueOf(actorId));
jdbcTemplate.update("delete from t_actor where id = ?", actorId.toLong())
其他JdbcTemplate
操作
您可以使用execute(..)
方法来运行任何任意 SQL。因此,
方法通常用于 DDL 语句。它严重超载了变体,这些变体需要
回调接口、绑定变量数组等。以下示例创建了一个
桌子:
-
Java
-
Kotlin
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
以下示例调用存储过程:
-
Java
-
Kotlin
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));
jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
unionId.toLong())
稍后将介绍更复杂的存储过程支持。
JdbcTemplate
最佳实践
的实例JdbcTemplate
一旦配置,类是线程安全的。这是
重要,因为这意味着您可以配置JdbcTemplate
然后安全地将此共享引用注入多个 DAO(或存储库)。
这JdbcTemplate
是有状态的,因为它维护对DataSource
但
此状态不是对话状态。
使用JdbcTemplate
类(以及关联的NamedParameterJdbcTemplate
类)是
配置一个DataSource
在 Spring 配置文件中,然后 dependency-inject
那共享DataSource
bean 加入你的 DAO 类。这JdbcTemplate
创建于
的 setterDataSource
或在构造函数中。这导致了类似于以下内容的 DAO:
-
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 的JdbcDaoSupport
类和各种 JDBC 支持的 DAO 类
从中扩展,您的子类继承了setDataSource(..)
方法JdbcDaoSupport
类。您可以选择是否继承自此类。这JdbcDaoSupport
课程仅为方便起见而提供。
无论您选择使用上述哪种模板初始化样式(或
not),很少需要创建JdbcTemplate
每个类别
时间。配置后,一个JdbcTemplate
实例是线程安全的。
如果您的应用程序访问多个数据库,您可能需要多个JdbcTemplate
实例,这需要多个DataSources
随后,以不同的方式进行多次
配置JdbcTemplate
实例。
用NamedParameterJdbcTemplate
这NamedParameterJdbcTemplate
class 添加了对 JDBC 语句编程的支持
使用命名参数,而不是仅使用经典
占位符 ('?'
) 参数。这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
variable(类型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)!!
}
与NamedParameterJdbcTemplate
(并存在于同一
Java 包)是SqlParameterSource
接口。你已经看过一个例子
在前面的代码片段之一中实现此接口(MapSqlParameterSource
类)。一SqlParameterSource
是命名参数的来源
值设置为NamedParameterJdbcTemplate
.这MapSqlParameterSource
class 是一个
简单的实现,即围绕java.util.Map
,其中键
是参数名称,值是参数值。
另一个SqlParameterSource
实现是BeanPropertySqlParameterSource
类。此类包装一个任意 JavaBean(即,类的实例
遵守
JavaBean 约定),并使用包装的 JavaBean 的属性作为源
的命名参数值。
以下示例显示了一个典型的 JavaBean:
-
Java
-
Kotlin
public class Actor {
private Long id;
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public Long getId() {
return this.id;
}
// setters omitted...
}
data class Actor(val id: Long, val firstName: String, val lastName: String)
以下示例使用NamedParameterJdbcTemplate
返回
前面示例中所示的类的成员:
-
Java
-
Kotlin
// some JDBC-backed DAO class...
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
public int countOfActors(Actor exampleActor) {
// notice how the named parameters match the properties of the above 'Actor' class
String sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName";
SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(exampleActor);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}
// some JDBC-backed DAO class...
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
private val namedParameterJdbcTemplate = NamedParameterJdbcTemplate(dataSource)
fun countOfActors(exampleActor: Actor): Int {
// notice how the named parameters match the properties of the above 'Actor' class
val sql = "select count(*) from t_actor where first_name = :firstName and last_name = :lastName"
val namedParameters = BeanPropertySqlParameterSource(exampleActor)
return namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Int::class.java)!!
}
请记住,NamedParameterJdbcTemplate
Class 包装经典JdbcTemplate
模板。如果您需要访问包装的JdbcTemplate
要访问的实例
仅存在于JdbcTemplate
类,您可以使用getJdbcOperations()
访问包装的JdbcTemplate
通过JdbcOperations
接口。
也可以看看JdbcTemplate
最佳实践有关使用NamedParameterJdbcTemplate
类。
统一的JDBC查询/更新作:JdbcClient
从 6.1 开始,命名参数语句NamedParameterJdbcTemplate
和位置
参数语句JdbcTemplate
可通过统一的客户端 API 获得
具有流畅的交互模型。
例如,使用位置参数:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = ?")
.param(firstName)
.query(Integer.class).single();
}
例如,使用命名参数:
private JdbcClient jdbcClient = JdbcClient.create(dataSource);
public int countOfActorsByFirstName(String firstName) {
return this.jdbcClient.sql("select count(*) from t_actor where first_name = :firstName")
.param("firstName", firstName)
.query(Integer.class).single();
}
RowMapper
还提供功能,具有灵活的结果分辨率:
List<Actor> actors = this.jdbcClient.sql("select first_name, last_name from t_actor")
.query((rs, rowNum) -> new Actor(rs.getString("first_name"), rs.getString("last_name")))
.list();
而不是自定义RowMapper
,您还可以指定要映射到的类。
例如,假设Actor
有firstName
和lastName
性能
作为记录类、自定义构造函数、Bean 属性或普通字段:
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();
或者带有命名参数的更新语句:
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();
自动Actor
参数的类映射以及上面的查询结果是
通过隐式提供SimplePropertySqlParameterSource
和SimplePropertyRowMapper
也可以直接使用的策略。它们可以作为常见的替代品
为BeanPropertySqlParameterSource
和BeanPropertyRowMapper
/DataClassRowMapper
,
还与JdbcTemplate
和NamedParameterJdbcTemplate
他们自己。
JdbcClient 是 JDBC 查询/更新语句的灵活但简化的外观。
批处理插入和存储过程调用等高级功能通常需要
额外自定义:考虑 Spring 的SimpleJdbcInsert 和SimpleJdbcCall classes 或
普通直接JdbcTemplate 用于JdbcClient . |
用SQLExceptionTranslator
SQLExceptionTranslator
是一个由可以转换的类实现的接口
之间SQLException
s 和 Spring 自己的org.springframework.dao.DataAccessException
,
这在数据访问策略方面是不可知论的。实现可以是通用的(对于
例如,对 JDBC 使用 SQLState 代码)或专有(例如,使用 Oracle 错误)
代码)以获得更高的精度。这种异常转换机制用于
常见JdbcTemplate
和JdbcTransactionManager
没有的入口点
传播SQLException
而是DataAccessException
.
从 6.0 开始,默认异常转换器为SQLExceptionSubclassTranslator ,
检测 JDBC 4SQLException 子类,带有一些额外的检查,并带有回退
自SQLState 通过SQLStateSQLExceptionTranslator .这通常是
足以满足常见数据库访问的需求,并且不需要特定于提供商的检测。
为了向后兼容,请考虑使用SQLErrorCodeSQLExceptionTranslator 如
下面将介绍,可能带有自定义错误代码映射。 |
SQLErrorCodeSQLExceptionTranslator
是实现SQLExceptionTranslator
默认情况下,当名为sql-error-codes.xml
存在于根中
的类路径。此实现使用特定的提供商代码。它比SQLState
或SQLException
子类翻译。错误代码转换基于
在名为SQLErrorCodes
.创建此类并
由SQLErrorCodesFactory
,它(顾名思义)是一个工厂
创建SQLErrorCodes
基于名为sql-error-codes.xml
.此文件填充了提供商代码,并基于DatabaseProductName
取自DatabaseMetaData
.实际的代码
您正在使用的数据库。
这SQLErrorCodeSQLExceptionTranslator
按以下顺序应用匹配规则:
-
由子类实现的任何自定义翻译。通常,提供的混凝土
SQLErrorCodeSQLExceptionTranslator
,因此此规则不适用。它 仅当您实际提供了子类实现时才适用。 -
任何自定义实现
SQLExceptionTranslator
提供的接口 作为customSqlExceptionTranslator
属性的SQLErrorCodes
类。 -
的实例列表
CustomSQLErrorCodesTranslation
类(为customTranslations
属性的SQLErrorCodes
class) 中搜索匹配项。 -
应用错误代码匹配。
-
使用回退转换器。
SQLExceptionSubclassTranslator
是默认回退 在线翻译。如果此翻译不可用,则下一个回退转换器为 这SQLStateSQLExceptionTranslator
.
这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
.
运行语句
运行 SQL 语句需要很少的代码。你需要一个DataSource
和JdbcTemplate
,包括JdbcTemplate
.以下示例显示了您需要包含哪些内容,以实现最小但
创建新表的全功能类:
-
Java
-
Kotlin
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public class ExecuteAStatement {
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public void doExecute() {
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");
}
}
import javax.sql.DataSource
import org.springframework.jdbc.core.JdbcTemplate
class ExecuteAStatement(dataSource: DataSource) {
private val jdbcTemplate = JdbcTemplate(dataSource)
fun doExecute() {
jdbcTemplate.execute("create table mytable (id integer, name varchar(100))")
}
}
运行查询
某些查询方法返回单个值。要从
一行,使用queryForObject(..)
.后者转换返回的 JDBCType
到
作为参数传入的 Java 类。如果类型转换无效,则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 语句具有行参数的占位符。您可以传递参数值 in 作为 varargs 或对象数组。因此,您应该显式包装基元 在原始包装器类中,或者您应该使用自动装箱。
检索自动生成的键
一update()
方便方法支持检索由
数据库。此支持是 JDBC 3.0 标准的一部分。请参阅第 13.6 章
规格了解详情。该方法采用PreparedStatementCreator
作为它的第一个
参数,这是指定所需 insert 语句的方式。其他
参数是一个KeyHolder
,其中包含从
更新。没有标准的单一方法来创建适当的PreparedStatement
(这解释了为什么方法签名是这样的)。以下示例适用于在 Oracle 上,但在其他平台上可能不起作用:
-
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