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也是极好的)。

    @Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql:///crud");
dataSource.setUsername("root");
dataSource.setPassword("root");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}

第二步 在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:

    @Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}

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

@Repository
public class JdbcRepository { @Autowired
private JdbcOperations jdbcOperations;

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

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

使用JdbcTemplate读取数据

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

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

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

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

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

使用命名参数

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

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

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

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

    @Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}

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

public void addSpitter(Spitter spitter){
Map<String,Object> map=new HashMap<String,Object>();
map.put("username",spitter.getUsername());
map.put("password",spitter.getPassword());
map.put("username",spitter.getFullName()); jdbcOperations.update(SQL_INSERT_SPITTER, map);
}

因为命名参数需要通过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了

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

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

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

@EnableTransactionManagement
@ComponentScan("cn.lynu")
@Configuration
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. CentOS6.4x86EngCustomize120g__20160307.rar

    安装的镜像包: CentOS-6.4-i386-bin-DVD1to2(CentOS-6.4-i386-bin-DVD1.iso / CentOS-6.4-i386-bin-DVD2.iso) 1. ...

  2. 这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)

    前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下最热的一个网络请求库 今天,我将献上一份非常详细Retrofit v2.0的使用教程,希望你们会 ...

  3. 使用H5 canvas画一个坦克

      具体步骤如下:   1. 首先做出绘图区,作为坦克的战场   <canvas id="floor" width="800px" height=&quo ...

  4. tp5集成淘宝,微信,网易,新浪等第三方登录

    tp5集成淘宝,微信,网易,新浪等第三方登录 一.总结 一句话总结: 接口 链接 实现的话就是这些平台给的一个接口(链接),你通过这些接口登录进去之后,它会给你返回用户名,头像之类的信息,我们的网站存 ...

  5. UriComponentsBuilder和UriComponents url编码

    Spring MVC 提供了一种机制,可以构造和编码URI -- 使用UriComponentsBuilder和UriComponents. 功能相当于 urlencode()函数,对url进行编码, ...

  6. Learn Rails5.2 Routes。( 很少用到的参数:constraints和redirect)

    Naming a Route get 'home/index', as: "different_name" 会得到prefix: different_name代替home_inde ...

  7. Linux命令详解-echo

    echo会将输入的字符串送往标准输出.输出的字符串间以空白字符隔开,并在最后加上换行号. 1.命令格式: file [ -bchikLnNprsvz ] [ -f namefile ] [ -F se ...

  8. 操作ACCESS数据库,报INSERT INTO 语句的语法错误

    错误类型:Microsoft JET Database Engine (0x80040E14)INSERT INTO 语句的语法错误. 有时候非常郁闷,明明看起来自己的语句没错,为什么还是报错呢?其实 ...

  9. 002——php字符串中的处理函数(一)

    <?php /** * 字符串处理函数: * 一.PHP处理字符串的空格: * strlen 显示字符串长度 * * trim 对字符串左右空格删除: * ltrim 对字符串左侧空格删除 * ...

  10. ajax下载,前端js下载(转)

    前面一直做过下载的功能.就是后台将文件流写入response里面,然后就好了.前台会自动弹出下载提示等. 今天打算做一个ajax下载.想当然的结果死活浏览器没反应.我擦. 然后浏览器调试,发现resp ...