- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
10.3.2 使用 JDBC 模板
Spring的JDBC框架承担了资源管理和异常处理的工作,从而简化了JDBC代码,让我们只需编写从数据库读写数据的必需代码。
正如前面小节所介绍过的,Spring将数据访问的样板代码抽象到模板类之中。Spring为JDBC提供了三个模板类供选择:
JdbcTemplate:最基本的Spring JDBC模板,这个模板支持简单的JDBC数据库访问功能以及基于索引参数的查询;
NamedParameterJdbcTemplate:使用该模板类执行查询时可以将值以命名参数的形式绑定到SQL中,而不是使用简单的索引参数;
SimpleJdbcTemplate:该模板类利用Java 5的一些特性如自动装箱、泛型以及可变参数列表来简化JDBC模板的使用。
以前,在选择哪一个JDBC模板的时候,我们需要仔细权衡。但是从Spring 3.1开始,做这个决定变得容易多了。SimpleJdbcTemplate已经被废弃了,其Java 5的特性被转移到了JdbcTemplate中,并且只有在你需要使用命名参数的时候,才需要使用NamedParameterJdbcTemplate。这样的话,对于大多数的JDBC任务来说,JdbcTemplate就是最好的可选方案,这也是本小节中所关注的方案。
使用JdbcTemplate来插入数据
为了让JdbcTemplate正常工作,只需要为其设置DataSource就可以了,这使得在Spring中配置JdbcTemplate非常容易,如下面的@Bean方法所示:
在这里,DataSource是通过构造器参数注入进来的。这里所引用的dataSourcebean可以是javax.sql.DataSource的任意实现,包括我们在10.2小节中所创建的。
现在,我们可以将jdbcTemplate装配到Repository中并使用它来访问数据库。例如,SpitterRepository使用了JdbcTemplate:
在这里,JdbcSpitterRepository类上使用了@Repository注解,这表明它将会在组件扫描的时候自动创建。它的构造器上使用了@Inject注解,因此在创建的时候,会自动获得一个JdbcOperations对象。JdbcOperations是一个接口,定义了JdbcTemplate所实现的操作。通过注入JdbcOperations,而不是具体的JdbcTemplate,能够保证JdbcSpitterRepository通过JdbcOperations接口达到与JdbcTemplate保持松耦合。
作为另外一种组件扫描和自动装配的方案,我们可以将JdbcSpitterRepository显式声明为Spring中的bean,如下所示:
在Repository中具备可用的JdbcTemplate后,我们可以极大地简化程序清单10.4中的addSpitter()方法。基于JdbcTemplate的addSpitter()方法如下:
程序清单10.7 基于JdbcTemplate的addSpitter()方法
这个版本的addSpitter()方法简单多了。这里没有了创建连接和语句的代码,也没有异常处理的代码,只剩下单纯的数据插入代码。
不能因为你看不到这些样板代码,就意味着它们不存在。样板代码被巧妙地隐藏到JDBC模板类中了。当update()方法被调用的时候JdbcTemplate将会获取连接、创建语句并执行插入SQL。
在这里,你也看不到对SQLException处理的代码。在内部,JdbcTemplate将会捕获所有可能抛出的SQLException,并将通用的SQLException转换为表10.1所列的那些更明确的数据访问异常,然后将其重新抛出。因为Spring的数据访问异常都是运行时异常,所以我们不必在addSpring ()方法中进行捕获。
使用JdbcTemplate来读取数据
JdbcTemplate也简化了数据的读取操作。程序清单10.8展现了新版本的findOne()方法,它使用了JdbcTemplate的回调,实现根据ID查询Spitter,并将结果集映射为Spitter对象。
程序清单10.8 使用JdbcTemplate查询Spitter
在这个findOne()方法中使用了JdbcTemplate的queryForObject()方法来从数据库查询Spitter。queryForObject()方法有三个参数:
String对象,包含了要从数据库中查找数据的SQL;
RowMapper对象,用来从ResultSet中提取数据并构建域对象(本例中为Spitter);
可变参数列表,列出了要绑定到查询上的索引参数值。
真正奇妙的事情发生在SpitterRowMapper对象中,它实现了RowMapper接口。对于查询返回的每一行数据,JdbcTemplate将会调用RowMapper的mapRow()方法,并传入一个ResultSet和包含行号的整数。在SpitterRowMapper的mapRow()方法中,我们创建了Spitter对象并将ResultSet中的值填充进去。
就像addSpitter()那样,findOne()方法也不用写JDBC模板代码。不同于传统的JDBC,这里没有资源管理或者异常处理代码。使用JdbcTemplate的方法只需关注于如何从数据库中获取Spitter对象即可。
在JdbcTemplate中使用Java 8的Lambda表达式
因为RowMapper接口只声明了addRow()这一个方法,因此它完全符合函数式接口(functional interface)的标准。这意味着如果使用Java 8来开发应用的话,我们可以使用Lambda来表达RowMapper的实现,而不必再使用具体的实现类了。
例如,程序清单10.8中的findOne()方法可以使用Java 8的Lambda表达式改写,如下所示:
我们可以看到,Lambda表达式要比完整的RowMapper实现更为易读,不过它们的功能是相同的。Java会限制RowMapper中的Lambda表达式,使其满足所传入的参数。
另外,我们还可以使用Java 8的方法引用,在单独的方法中定义映射逻辑:
不管采用哪种方式,我们都不必显式实现RowMapper接口,但是与实现RowMapper类似,我们所提供的Lambda表达式和方法必须要接受相同的参数,并返回相同的类型。
使用命名参数
在清单10.7的代码中,addSpitter()方法使用了索引参数。这意味着我们需要留意查询中参数的顺序,在将值传递给update()方法的时候要保持正确的顺序。如果在修改SQL时更改了参数的顺序,那我们还需要修改参数值的顺序。
除了这种方法之外,我们还可以使用命名参数。命名参数可以赋予SQL中的每个参数一个明确的名字,在绑定值到查询语句的时候就通过该名字来引用参数。例如,假设SQL_INSERT_SPITTER查询语句是这样定义的:
使用命名参数查询,绑定值的顺序就不重要了,我们可以按照名字来绑定值。如果查询语句发生了变化导致参数的顺序与之前不一致,我们不需要修改绑定的代码。
NamedParameterJdbcTemplate是一个特殊的JDBC模板类,它支持使用命名参数。在Spring中,NamedParameterJdbcTemplate的声明方式与常规的JdbcTemplate几乎完全相同:
在这里,我们将NamedParameterJdbcOperations(NamedParameterJdbcTemplate所实现的接口)注入到Repository中,用它来替代JdbcOperations。现在的addSpitter()方法如下所示:
程序清单10.9 使用Spring JDBC模板的命名参数功能
这个版本的addSpitter()比前一版本的代码要长一些。这是因为命名参数是通过java.util.Map来进行绑定的。不过,每行代码都关注于往数据库中插入Spitter对象。这个方法的核心功能并不会被资源管理或异常处理这样的代码所充斥。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论