目录
jdbcTemplate更新操作update/delete...
概述
Spring框架提供了可扩展的SQL数据库的支持。然而spring的东西使用久了之后,如果不去了解背后的原理的话可能越来越迷糊,本篇博客的目的是为了 分析spring框架如何实现的和各种数据库在各种场景下的连接配置的问题。我们将从最原生java数据库连接讲起。
JDBC
相信不少学习java的朋友在第一次接触数据库的时候,一定都接触过最原生的数据库连接,以mysql为例,大致代码按如下:
try{
//加载JDBC驱动程序:
Class.forName(\"com.mysql.jdbc.Driver\");
String url = \"jdbc:mysql://127.0.0.1:3306/dbname\";
String username = \"\";
String password = \"\";
// 创建与MySQL数据库的连接类的实例
Connection conn = DriverManager.getConnection(url, username, password);
// 用conn创建Statement对象类实例
Statement sql_statement = conn.createStatement();
// 执行sql
ResultSet result = sql_statement.executeQuery(query);
//处理结果
...
}catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
// 关闭连接
try {
conn.close();
System.out.println(\"Database connection terminated\");
} catch (Exception e) { /* ignore close errors */
}
}
}
上面的代码就是纯jdbc的连接数据库代码,只需安装jdk导入java 的原生包就可以写出来,不需要使用任何第三方的包。
上述代码主要有下面这几个步骤:
- 加载JDBC驱动程序
- 创建数据库的连接(需要用户名密码等信息)
- 创建一个Statement
- 执行SQL语句(处理结果)
- 处理异常,关闭连接
上面的代码可以看出一些问题,比如模版代码过多,对于一个用户来说,他可能只是想执行一条sql语句,并不想关心其他连接,异常处理,加载驱动的问题。我们可以很容易想到的是,如果有一个方法或者一个工具,让用户可以只需要指定好数据库连接信息,就可以执行各种数据库操作,岂不是非常好吗?这就是框架开发这想做的事情。
JdbcTemplate
JdbcTemplate是spring提供的最经典也是最流行的jdbc实现方式之一。现在除了JdbcTemplate之外,还有SimpleJdbcInsert,SimpleJdbcCall两个更新的实现方式。
JdbcTemplate是“最底层”的方法,所有其他spring的方法在他们的包装之下都使用了JdbcTemplate。
JdbcTemplate是spring JDBC core这个包中的重点类。 它处理资源的创建和释放,帮助我们避免常见错误,例如上面直接使用java原生代码而忘记关闭连接。 它执行核心JDBC工作流的基本任务(例如语句创建和执行),应用程序代码只需要提供SQL并提取结果。 JdbcTemplate类主要包含下面这些功能:
- 执行SQL查询语句
- 执行更新语句和存储过程调用
- 对ResultSet实例执行迭代并提取返回的参数值。
- 捕获JDBC异常并将它们转换为org.springframework.dao packag中定义的通用的,信息量更大的异常层次结构。
使用jdbcTemplate需要配置datasource。使用spring ioc容器的话,则可以把datasource配置为一个bean,直接让数据库操作的时候引用即可。spring文档特别指出:应始终将DataSource配置为Spring IoC容器中的bean。这说明spring对于ioc容器中的datasource bean提供了非常多方便的功能。
我们可以使用jdbcTemplate来执行查询,更新,以及一些其他操作。如下:
jdbcTemplate查询query
下面的例子是返回数据的数量
int rowCount = this.jdbcTemplate.queryForObject(\"select count(*) from t_actor\", Integer.class);
下面的查询是返回一个字符串:
String lastName = this.jdbcTemplate.queryForObject(
\"select last_name from t_actor where id = ?\",
new Object[]{1212L}, String.class);
也可以查询并填充单个对象:
Actor actor = this.jdbcTemplate.queryForObject(
\"select first_name, last_name from t_actor where id = ?\",
new Object[]{1212L},
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString(\"first_name\"));
actor.setLastName(rs.getString(\"last_name\"));
return actor;
}
});
查询并填充一系列对象:
List<Actor> actors = this.jdbcTemplate.query(
\"select first_name, last_name from t_actor\",
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString(\"first_name\"));
actor.setLastName(rs.getString(\"last_name\"));
return actor;
}
});
在应用程序当中为了分离逻辑,我们常常将上述代码的RowMapper逻辑提出去,变成如下两个方法:
public List<Actor> findAllActors() {
return this.jdbcTemplate.query( \"select first_name, last_name from t_actor\", new ActorMapper());
}
private static final class ActorMapper implements RowMapper<Actor> {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
Actor actor = new Actor();
actor.setFirstName(rs.getString(\"first_name\"));
actor.setLastName(rs.getString(\"last_name\"));
return actor;
}
}
值得一提的是,上述代码在spring batch的读数据环节也有应用。
jdbcTemplate更新操作update/delete...
下面是一个insert插入的例子:
this.jdbcTemplate.update(
\"insert into t_actor (first_name, last_name) values (?, ?)\",
\"Leonor\", \"Watling\");
下面是一个更新update的例子:
this.jdbcTemplate.update(
\"update t_actor set last_name = ? where id = ?\",
\"Banjo\", 5276L);
下面是一个删除的例子:
this.jdbcTemplate.update(
\"delete from actor where id = ?\",
Long.valueOf(actorId));
事实上上述三个操作我们可以看到调用的都是jdbcTemplate的同一个方法,只是sql语句不同。
jdbcTemplate最佳实践
使用jdbcTemplate的好处有线程安全,配置简单,只需一次配置即可等有点。在使用jdbcTemplate的时候也有最佳实践可以遵循。
使用JdbcTemplate类时的常见做法是在Spring配置文件中配置DataSource,然后将共享的DataSource的bean依赖注入到DAO类中。 JdbcTemplate是在DataSource的setter中创建的。 这意味着DAO类有似于以下的代码:
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...
}
对应的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
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://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>
在使用springboot开发的实际体验中,我们常常用java注解配置以及application.properties配置文件代替上述内容,但是其内容都是一样的,必定有驱动名,url,用户名,密码等信息,这就是和我们使用原生jdbc代码去连数据库有联系的地方。无论使用何种方式,该有的一个也不会少。
无论您选择使用上述哪种模板初始化样式,每次要运行SQL时,很少有需要创建JdbcTemplate类的新实例。 配置完成后,JdbcTemplate实例是线程安全的。 如果我们应用程序访问多个数据库,则可能需要多个JdbcTemplate实例(例如做数据迁移,就是一个典型的场景),这需要多个DataSource,随后需要多个不同配置的JdbcTemplate实例。
关于JdbcTemplate的内容我们暂时了解到这里,他当然还提供了很多其他方法,可以在需要使用时直接查看其文档或者接口即可。
数据库连接和DataSource
我们在上面已极多次提到了DataSource,可以猜出它是spring用来管理数据库连接的,现在,我们专门来研究一下dataSource的内容。
Spring通过DataSource获取与数据库的连接。 DataSource是java定义的JDBC规范的一部分,是一个通用的连接工厂。 它允许容器或框架从应用程序代码中隐藏连接池和事务管理问题。在大型项目中,作为开发人员,则无需了解有关如何连接到数据库的详细信息。 这是设置数据源的管理员的责任。
使用Spring的JDBC层时,可以从JNDI获取数据源,也可以使用第三方提供的连接池实现配置自己的数据源。 流行的实现是Apache Jakarta Commons DBCP和C3P0。 Spring发行版中的实现仅用于测试目的,不提供池。
以下示例是如何在Java中配置DriverManagerDataSource类型的datasource的例子:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(\"org.hsqldb.jdbcDriver\");
dataSource.setUrl(\"jdbc:hsqldb:hsql://localhost:\");
dataSource.setUsername(\"sa\");
dataSource.setPassword(\"\");
可以看到配置的就是连接数据库必须的信息。
下面是一个DBCP实现的DataSource配置
<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\"/>
虽然实现类不一样,其他其要求的配置都是一样的。
除了上述的配置方式外,我们还有下面这些方法配置datasource
- 使用
DataSourceUtils类提供的功能, 实现SmartDataSource- 继承
AbstractDataSource - 使用
SingleConnectionDataSource 使用DriverManagerDataSource使用TransactionAwareDataSourceProxy
上述方式就不一一展开,我们可以在使用时根据需求选择。
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。


