Spring中的数据库异常体系

使用JDBC(不使用Spring)的时候,我们需要强制捕获SQLException,否则无法使用JDBC处理任何事情。SQLException表示尝试访问数据库的时候出现问题,但是这个异常却没有告诉我们哪里出错以及如何处理。可能出现SQLException的情况有:

  • 应用程序无法连接数据库
  • 查询的SQL有问题
  • 查询中的表,列不存在
  • 尝试插入或更新的数据违法数据库的约束

还有其他的数据库有关的异常,但是真正抛出SQLException的时候,我们没有办法准确的定位,只有靠经验与猜测去处理。而且抛出SQLException意味着出现致命性的错误,程序也没有办法继续处理。

一些持久层框架都有相对丰富的异常体系,例如Hibernate有二十几左右的异常,分别对应特定的数据访问问题。这样就可以针对特定的异常编写catch代码块。但是,这种Hibernate的异常是与对应的框架相对应的,我们如果希望特定的持久框架独立于数据访问层。如果我们将这些Hibernate的异常抛出去,那么我们对Hibernate的使用将渗透到其他层。如果不这样做,就需要将这些特定的异常捕获然后将其转换为与平台无关的异常再次处理。

一方面,JDBC的异常过于简单,而Hibernate的异常体系又是与其平台有关的。Spring 针对这个问题提供了多个数据访问异常,Spring的异常体系并没有与特定持久方法相关联,这样我们就不用关心所选择的持久化方案。这有助于我们将所选择的具体持久化技术与数据访问层隔离开。而且Spring的数据访问异常都继承于DataAccessException,这是一个非检查异常,也就是说没有必要捕获Spriing这些数据访问异常(如果想捕获也是完全可以的)。

第一步 配置数据源

Spring的模板类处理数据访问层的国定部分——事务控制,管理控制以及处理异常。同时,应用程序相关的数据访问——语句,绑定参数以及真理数据集需要我们自己处理。针对不同的持久化平台,Spring提供了可选的模板,如果使用JDBC就选择JdbcTemplate,如果使用对象关系映射框架,那么可以使用HibernateTemplcate或JpaTemplcate。

但首先要说明的是Spring所支持的持久功能都需要依赖数据源。所以不要忘记配置数据源,并将其作为第一步(建议使用数据库连接池或者JNDI的方式,如果有可能使用Spring的profile也是极好的)。

  1. @Bean
  2. public DataSource dataSource() {
  3. BasicDataSource dataSource = new BasicDataSource();
  4. dataSource.setUrl("jdbc:mysql:///crud");
  5. dataSource.setUsername("root");
  6. dataSource.setPassword("root");
  7. dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  8. return dataSource;
  9. }

第二步 在Spring中使用JDBC

JDBC是建立在SQL之上的,而SQL本身就是数据库访问语言,此外,与其他的技术相比,直接使用JDBC可以更好对数据库进行调优,JDBC允许使用数据库的所有特性,而这是其他框架不鼓励甚至是禁止的。相对于其他的持久层框架,JDBC能够让我们在更底层上处理数据,我们完全可以控制应用程序如何读取和管理数据,这是一种细粒度的数据访问方式。

使用JDBC模板

Spring的JDBC模板承担了资源管理和异常处理的工作,从而简化了JDBC代码,将数据访问的样板代码抽象到模板类中,Spring为JDBC提供了三个JDBC模版类:

  • JdbcTemplate:最基本的Spring JDBC模板类,支持基于索引参数(?)的查询。
  • NamedParameterJdbcTemplate:使用该模板类执行查询的时候使用命名参数的方式绑定到SQL,而不是用索引参数。
  • SimpleJdbcTemplate:Spring3.1之后已弃用

只有我们需要使用命名参数的时候,才使用NamedParameterJdbcTemplate,对于的大多数JDBC任务使用JdbcTemplate就是最好的方案。为了JdbcTemplate可以正常工作,只需要为其配置DataSource:

  1. @Bean
  2. public JdbcTemplate jdbcTemplate(DataSource dataSource) {
  3. return new JdbcTemplate(dataSource);
  4. }

然后,我们就可以将JdbcTemplate装配到Repository中了。

  1. @Repository
  2. public class JdbcRepository {
  3.  
  4. @Autowired
  5. private JdbcOperations jdbcOperations;

这里使用的是 JdbcOperations ,JdbcOperations接口定义了JdbcTemplate模板类所实现的方法,使用接口而不是具体的某一个JDBC模板类,保证松耦合。这里使用Spring的@AutoWired,其实使用@Inject作也可以,只不过后者是Java的注入规范中的注解。

使用模板类之后所有的样板代码隐藏到JDBC模板类中,并且也不用对SQLException进行处理,在内部,JdbcTemplate将会捕获说有可能抛出的SQLException,并将其转换为Spring中通用的数据访问异常,然后将其重新抛出。

使用JdbcTemplate读取数据

JdbcTemplate中有众多query...()的方法用于读取数据,但是对于需要将返回结果封装为对象的操作,需要有一些特殊处理。这个对象需要实现RowMapper接口,对于查询所返回的每一行数据,JdbcTemplate将会调用RowMapper的mapRow方法,并传入一个ResultSet和包含行号的整数。

  1. private static final class SpitterRowMapper implements RowMapper<Spitter> {
  2. public Spitter mapRow(ResultSet rs, int rowNum) throws SQLException {
  3. //这段代码摘自书本
  4. long id = rs.getLong("id");
  5. String username = rs.getString("username");
  6. String password = rs.getString("password");
  7. String fullName = rs.getString("fullname");
  8. String email = rs.getString("email");
  9. boolean updateByEmail = rs.getBoolean("updateByEmail");
  10. return new Spitter(id, username, password, fullName, email, updateByEmail);
  11. }
  12. }

因为RowMapper接口中只有一个mapRow方法,它完全符合函数式接口的标准。这意味着使用Java8来开发应用的化可以使用Lambda表达式,而不必使用具体的实现类了。或者使用Java8的方法引用,在单独的方法中定义映射规则:

  1. public Dept findOne(Integer deptId) {
  2. return jdbcOperations.queryForObject(SELECT_SQL, this::mapDept,deptId);
  3. }
  4.  
  5. private Dept mapDept(ResultSet rs,int row)throws SQLException {
  6. return new Dept(rs.getInt("dept_id"),rs.getString("dept_name"));
  7. }

但是,不论使用Lambda表达式还是方法引用,都需要保证接受与mapRow相同的参数。

使用命名参数

使用索引参数的时候,需要注意参数的顺序,在将值传递给繁华的是否需要保证正确的顺序。如果在修改SQL时改变了参数的顺序,那我们还需要修改参数值的顺序。而我们使用命名参数,命名参数可以给SQL中的每一个参数一个明确的名字,在绑定值得时候通过名字来引用参数。

  1. private static final String SQL_INSERT_SPITTER="insert into spitter(username,password,fullname)"+"values(:username,:password,:fullname)";

使用命名参数查询,绑定值得顺序就不重要了,如果SQL改变导致参数顺序与之前不一致,我们不需要修改绑定的代码。

NamedParameterJdbcTemplate是一个特殊得JDBC模板类,它使用命名参数。NamedParameterJdbcTemplate得用法与JdbcTemplate一致:

  1. @Bean
  2. public JdbcTemplate jdbcTemplate(DataSource dataSource) {
  3. return new NamedParameterJdbcTemplate(dataSource);
  4. }

然后,就开始在Repository中使用吧:

  1. public void addSpitter(Spitter spitter){
  2. Map<String,Object> map=new HashMap<String,Object>();
  3. map.put("username",spitter.getUsername());
  4. map.put("password",spitter.getPassword());
  5. map.put("username",spitter.getFullName());
  6.  
  7. jdbcOperations.update(SQL_INSERT_SPITTER, map);
  8. }

因为命名参数需要通过java.util.Map进行绑定,所以需要将参数放进Map中。

使用事务

因为采用JavaConfig得方式配置Spring,所以对于事务得处理也需要通过Java代码的方式来声明,至于使用,还是与XML得方式一样来使用即可,先来回顾一下XML中配置事务的方式(使用事务注解,而不是AOP配置事务):

  1. 与平台相关的事务管理器
  2. 开启事务注解驱动:<tx:annotation-driven transaction-manager="transactionManage"/>
  3. 在需要事务的方法上使用@Transactional

Java代码的方式也是按照这个步骤去进行的,首先,我们配置事务管理器,JDBC和MyBatis都是使用数据源的事务管理(DataSourceTransactionManager),Hibernate和JPA使用分别各自的事务管理器(HibernateTransactionManager, JpaTransactionManager)。 因为这里使用JDBC就用DataSourceTransactionManager了

  1. //注册事务管理器在容器中
  2. @Bean
  3. public PlatformTransactionManager transactionManager() throws Exception{
  4. return new DataSourceTransactionManager(dataSource());
  5. }

需要将数据源交给事务管理器,这样管理器可以控制commit或rollback

接下来在配置类上启用 @EnableTransactionManagement 表示开启事务注解驱动

  1. @EnableTransactionManagement
  2. @ComponentScan("cn.lynu")
  3. @Configuration
  4. public class TxConfig {

最后在需要事务的方法上使用@Transactional注解即可,使用方法与XML的配置没有什么不同。

Spring中使用JDBC的更多相关文章

  1. 【转】在Spring中基于JDBC进行数据访问时怎么控制超时

    http://www.myexception.cn/database/1651797.html 在Spring中基于JDBC进行数据访问时如何控制超时 超时分类 超时根据作用域可做如下层级划分: Tr ...

  2. spring框架总结(04)----介绍的是Spring中的JDBC模板

    1.1  Jdbc模板概述 它是spring框架中提供的一个对象,是对原始Jdbc API对象的简单封装.spring框架为我们提供了很多的操作模板类,入下图所示: 我们今天的主角在spring-jd ...

  3. Spring框架学习(3)spring中使用jdbc

    内容源自:spring中使用jdbc spring dao层中对jdbc进行了封装,使用模板模式的设计模式,通过ioc被动注入的方式将jdbcTemplate这个模板类注入到数据对象中,进行数据库操作 ...

  4. 在Spring中配置jdbc为什么不能用${username}问题

    楼主在spring中配置jdbc时,引用的是dbcp.jar包,在dataSource.properties配置文件中,有mysql用户名,楼主自然的选择了使用username,密码是root, 然后 ...

  5. Spring 中的 JDBC 事务

    Spring 对 JDBC 的支持 JdbcTemplate 简介 •为了使 JDBC 更加易于使用, Spring 在 JDBC API 上定义了一个抽象层, 以此建立一个 JDBC 存取框架. • ...

  6. Spring中的JDBC操作

    一.Spring模板JdbcTemplate 为了使 JDBC 更加易于使用, Spring 在 JDBC API 上定义了一个抽象层, 以此建立一个 JDBC 存取框架JdbcTemplate. 作 ...

  7. Spring中的Jdbc事务管理

    Spring提供了对事务的声明式事务管理,只需要在配置文件中做一些配置,即可把操作纳入到事务管理当中,解除了和代码的耦合. Spring声明式事务管理,核心实现就是基于Aop. Spring声明式事务 ...

  8. Spring中的JDBC模板类入门

    1.Spring框架中提供了很多持久层的模板类来简化编程,使用模板类编写程序会变的简单 2.提供了JDBC模板,Spring框架提供的 *JdbcTemplate类 3.Spring框架可以整合Hib ...

  9. 在Spring中基于JDBC进行数据访问时如何控制超时

    超时分类 超时根据作用域可做如下层级划分: Transaction Timeout > Statement Timeout > JDBC Driver Socket Timeout Tra ...

随机推荐

  1. c++ 计算指定半径圆的面积

    #include <iostream> #define PI 3.14 using namespace std; class Circle { float radius; public: ...

  2. Java字符串分割(转)

    java.lang.String 的 split() 方法, JDK 1.4 or later public String[] split(String regex,int limit) 示例代码 p ...

  3. Angular4笔记——表单状态相关的属性

    表单状态字段(FromControl)touched和untouched用来判断用户是否访问过一个字段(也就是这个字段是否获取过焦点,如果获取过焦点,touched是true,untouched是fa ...

  4. 最短路径求解(Dijkstra)

    Dijkstra算法分析 题目分析参照<数据结构>(严蔚敏)7-6节 最短路径问题描述 参照日常生活中的公交查询系统.我们有选项: 少换乘/最少站数 价格最少/时间最短.... (ps:下 ...

  5. hdu1846巴什博弈

    巴什博弈:只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,最多取m个.最后取光者得胜. 结论:只要不能整除,那么必然是先手取胜,否则后手取胜. #include<map> ...

  6. Python - Learn Note (2)

    Python注释 Python的注释以#开头,后面的文字直到行尾都算注释 Python基本数据类型 整数.浮点数(浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置 ...

  7. BootStrap使用

    BootStrap简单使用 <深入理解BootStrap>这本书对BootStrap进行了全面的讲解包括设计思想以及源码解析对没有接触过的很有帮助 BootStrap可以说是最简单的一类框 ...

  8. Nginx安装配置详解

    http://nginx.org/download/ 下载对应的Nginx 安装nginx之前需要安装依赖包 yum install gcc gcc-c++ zlib-devel pcre-devel ...

  9. 20181009-8 选题 Scrum立会报告+燃尽图 07

    Scrum立会报告+燃尽图(07)选题 此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2197 一.小组介绍 组长:刘莹莹 ...

  10. C++面向对象高级编程(九)Reference与重载operator new和operator delete

    摘要: 技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一 Reference 引用:之前提及过,他的主要作用就是取别名,与指针很相似,实现也是基于指针. 1.引用必须有初值,且不能引用nul ...