本篇随笔是上两篇的延续:三种数据库访问——原生JDBC数据库连接池:Druid

Spring的JDBC框架

Spring JDBC提供了一套JDBC抽象框架,用于简化JDBC开发。

Spring主要提供JDBC模板方式、关系数据库对象化方式、SimpleJdbc方式、事务管理来简化JDBC编程

Spring提供了3个模板类:

  • JdbcTemplate:Spring里最基本的JDBC模板,利用JDBC和简单的索引参数查询提供对数据库的简单访问。
  • NamedParameterJdbcTemplate:能够在执行查询时把值绑定到SQL里的命名参数,而不是使用索引参数。
  • SimpleJdbcTemplate:利用Java 5的特性,比如自动装箱、通用(generic)和可变参数列表来简化JDBC模板的使用。

JdbcTemplate主要提供以下4类方法:

  • execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句;
  • update方法及batchUpdate方法:update方法用于执行新增、修改、删除等语句;batchUpdate方法用于执行批处理相关语句;
  • query方法及queryForXXX方法:用于执行查询相关语句;
  • call方法:用于执行存储过程、函数相关语句。

示例项目

接下来,通过一个示例项目来展示如何使用Spring的JDBC框架访问数据库。假设该项目的功能有:保存用户信息、查询用户信息。

1、首先创建数据库及用户表:

CREATE TABLE `user` (
`id` int(10) NOT NULL auto_increment,
`name` varchar(30) default NULL,
`age` int(3) default NULL,
PRIMARY KEY (`id`)
)

2、创建工程(Maven工程),添加依赖

依赖配置:

        <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>0.2.25</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.4.RELEASE</version>
</dependency>

依赖的包有:Junit、mysql驱动器、druid(阿里巴巴开发的高性能的数据库连接池)、spring-context、spring-jdbc

3、实体类User

public class User implements Serializable{
private Long id;
private String name;
private Integer age; //setter getter 略 public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}

4、Dao接口及实现

package edu.shao.springJdbc.dao;

import java.util.List;
import edu.shao.springJdbc.po.User; public interface IUserDao {
public void save(User user);
public List<User> query(String sql,Object[] args);
}
package edu.shao.springJdbc.dao.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import edu.shao.springJdbc.dao.IUserDao;
import edu.shao.springJdbc.po.User; public class UserDaoImpl extends JdbcDaoSupport implements IUserDao { class UserRowMapper implements RowMapper<User> {
//实现ResultSet到User实体的转换
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User m = new User();
m.setId(rs.getLong("id"));
m.setName(rs.getString("name"));
m.setAge(rs.getInt("age"));
return m;
}
}; public void save(User model) {
getJdbcTemplate().update("insert into user(name,age) values(?,?)",
model.getName(), model.getAge());
} public List<User> query(String sql, Object[] args) {
return getJdbcTemplate().query(sql, args, new UserRowMapper());
}
}

5、Service接口及实现:

package edu.shao.springJdbc.service;

public interface IUserService {
void saveUser();
void saveUserThrowException() throws Exception;
void findUsers();
}
package edu.shao.springJdbc.service.impl;

import java.util.List;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import edu.shao.springJdbc.dao.IUserDao;
import edu.shao.springJdbc.po.User;
import edu.shao.springJdbc.service.IUserService; @Transactional
public class UserServiceImpl implements IUserService {
private IUserDao userDao; public void saveUser() {
User u1=new User();
u1.setName("邵");
u1.setAge(24);
userDao.save(u1); if(1+1>1){
throw new RuntimeException("Runtime error...");//抛出运行时异常:RuntimeException
} User u2=new User();
u2.setName("陈");
u2.setAge(20);
userDao.save(u2);
} public void saveUserThrowException() throws Exception {
User u1=new User();
u1.setName("邵");
u1.setAge(24);
userDao.save(u1); if(1+1>1){
throw new Exception("Runtime error...");//抛出一般的异常:Exception
} User u2=new User();
u2.setName("陈");
u2.setAge(20);
userDao.save(u2);
} @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
public void findUsers() {
List<User> users=userDao.query("select * from user where age>?", new Object[]{17});
for (User user : users) {
System.out.println(user);
} } //setter getter略
}

Spring对事务的管理有丰富的支持,Spring提供了编程式配置事务和声明式配置事务,其中,声明式事务有以下两种方式 :

  1. 一种是使用Annotation注解的方式(官方推荐)
  2. 一种是基于Xml的方式

(编程式的事务处理有些侵入性。通常我们的事务需求并没有要求在事务的边界上进行如此精确的控制,我们一般采用"声明式事务"。)

上面我们采用了基于注解的方式来配置事务。

6、配置数据库连接池、配置bean的依赖关系、配置事务处理器

applicationContext-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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="123456" />
<property name="initialSize" value="1" />
<property name="maxActive" value="20" />
</bean> </beans>

applicationContext-jdbc.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"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <import resource="applicationContext-dataSource.xml" /> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean> <!-- 首先定义抽象的abstractDao,其有一个jdbcTemplate属性,从而可以让继承的子类自动继承jdbcTemplate属性注入; -->
<bean id="abstractDao" abstract="true">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean> <bean id="userDao" class="edu.shao.springJdbc.dao.impl.UserDaoImpl"
parent="abstractDao" /> <bean id="userService" class="edu.shao.springJdbc.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean> <bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean> <tx:annotation-driven transaction-manager="txManager" />
</beans>

上面<tx:annotation-driven transaction-manager="txManager" /> 这句话的作用是注册事务处理器。

我们只需要在类上加上注解@Transactional,就可以指定这个类需要受Spring的事务管理。默认Spring为每个方法开启一个事务,如果方法发生运行期异常(RuntimeException),事务会进行回滚;如果发生一般的异常(Exception),事务不进行回滚。

7、测试

package edu.shao.springJdbc;

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import edu.shao.springJdbc.service.IUserService; public class SpringJdbcTest {
private static ApplicationContext ctx = null; @BeforeClass //表示在所以测试方法之前执行,且只执行一次。
public static void onlyOnce() {
ctx = new ClassPathXmlApplicationContext("db/applicationContext-jdbc.xml");
} @Test
public void testSave(){
IUserService service=ctx.getBean("userService",IUserService.class);
service.saveUser();
} @Test
public void testSaveThrowException() throws Exception{
IUserService service=ctx.getBean("userService",IUserService.class);
service.saveUserThrowException();
} @Test
public void testJDBCDaoQuery(){
IUserService service=ctx.getBean("userService",IUserService.class);
service.findUsers();
}
}

运行测试类,

第一个测试方法,后台输出异常:java.lang.RuntimeException,查看数据库发现数据没有插入,说明事务进行了回滚。

第二个测试方法,后台输出异常:java.lang.Exception ,查看数据库发现第一条数据插入,而第二条数据没有插入,说明事务没有进行了回滚。

说明了Spring的事务支持默认只对运行期异常(RuntimeException)进行回滚,如果执行sql操作的时候会发生sql异常,不属于运行期异常,那Spring是怎么进行事务回滚的呢 ?

Spring把SQLException等异常转化为了DataAccessException,后者是一种RuntimeException,所以只对RuntimeException异常进行回滚是很合理的。

其他注解方式:

  1. 修改Spring的默认配置,当发生RuntimeException时,可以不让它进行事务回滚 ,只需要加上一个@Transactional(noRollbackFor=RuntimeException.class)
  2. 配置对Exception进行回滚,在方法上添加@Transactional(rollbackFor=Exception.class)
  3. 对于一些查询工作,因为不需要配置事务支持,我们给其添加注解: @Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)。readOnly=true表示事务中不允许存在更新操作.

关于事务的传播属性有下面几种配置:

  • REQUIRED:业务方法需要在一个事务中运行,如果方法运行时,已经处于一个事务中,那么加入到该事务中。否则自己创建一个新的事务。(Spring默认的事务传播属性)
  • NOT_SUPPORTED:声明方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务被挂起,在方法调用结束后,原先的事务便会恢复执行
  • REQUIRESNEW:不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法运行时已经存在一个事务,则该事务会被挂起,新的事务被创建,直到方法执行结束,新事务才结束,原先的事务才恢复执行.
  • MANDATORY:指定业务方法只能在一个已经存在的事务中执行,业务方法不能自己发起事务,如果业务方法没有在事务的环境下调用,则容器会抛出异常
  • SUPPORTS:如果业务方法在事务中被调用,则成为事务中的一部分,如果没有在事务中调用,则在没有事务的环境下执行
  • NEVER:指定业务方法绝对不能在事务范围内运行,否则会抛出异常.
  • NESTED:如果业务方法运行时已经存在一个事务,则新建一个嵌套的事务,该事务可以有多个回滚点,如果没有事务,则按REQUIRED属性执行。 注意:业务方法内部事务的回滚不会对外部事务造成影响,但是外部事务的回滚会影响内部事务

总结:
事务是企业应用开发的重要组成部分,它使软件更加可靠。它们确保一种要么全有 要么全无的行为,防止数据不一致而导致的不可预测的错误发生。 事务同时也支持并发,防止并发应用线程在操作同一数据时互相影响。

以前我们写Jdbc代码的时候,可能需要自己手动去开启事务,然后方法执行结束之后再去提交事务,全部都嵌套在我们的业务代码之中,具有很强的侵入性....
使用Spring提供事务管理机制,我们只需要配置XML或使用Annotion进行注解就可以实现事务的管理和配置,减少了代码之间的耦合,配置也很方便,很大程度上提升了我们的开发效率。

参考:http://www.iteye.com/topic/480432

三种数据库访问——Spring JDBC的更多相关文章

  1. 三种数据库访问——原生JDBC

    原生的JDBC编程主要分一下几个步骤: (原生的JDBC编程指,仅应用java.sql包下的接口和数据库驱动类编程,而不借助任何框架) 1. 加载JDBC驱动程序: 2. 负责管理JDBC驱动程序的类 ...

  2. 三种数据库访问——Spring3.2 + Hibernate4.2

    前三篇随笔中介绍了 用原生的JDBC访问数据库.一种高效的数据库连接池druid.用Spring的JDBC框架访问数据库. 本文继续介绍第三种数据库访问的解决方案:Spring3.2 + Hibern ...

  3. qlserver、Mysql、Oracle三种数据库的优缺点总结

    这篇文章主要介绍了sqlserver.Mysql.Oracle三种数据库的优缺点总结,需要的朋友可以参考下   一.sqlserver优点:易用性.适合分布式组织的可伸缩性.用于决策支持的数据仓库功能 ...

  4. MySQL - 常见的三种数据库存储引擎

    原文:MySQL - 常见的三种数据库存储引擎 数据库存储引擎:是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧. ...

  5. MySQL、SqlServer、Oracle 三种数据库的优缺点

    MySQL.SqlServer.Oracle 三种数据库的优缺点 一.MySQL 优点: 缺点: 二.SqlServer 优点: 缺点: 三.Oracle 优点: 缺点: 一.MySQL 优点: 体积 ...

  6. MySQL、SqlServer、Oracle,这三种数据库的优缺点,你知道吗?

    盘点MySQL.SqlServer.Oracle 三种数据库优缺点 MySQL SqlServer Oracle 一.MySQL 优 点 体积小.速度快.总体拥有成本低,开源:支持多种操作系统:是开源 ...

  7. 浅淡Webservice、WSDL三种服务访问的方式(附案例)

    Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...

  8. MySQL、SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法

    在这里主要讲解一下MySQL.SQLServer2000(及SQLServer2005)和ORCALE三种数据库实现分页查询的方法. 可能会有人说这些网上都有,但我的主要目的是把这些知识通过我实际的应 ...

  9. Kubernetes的三种外部访问方式:NodePort、LoadBalancer和Ingress(转发)

    原文 http://cloud.51cto.com/art/201804/570386.htm Kubernetes的三种外部访问方式:NodePort.LoadBalancer和Ingress 最近 ...

随机推荐

  1. Asp.net WebForm 中无法引用App_Code文件夹下的类

    在VS2013中新建asp.net webform应用程序,手动添加"APP_Code"文件夹并新建类文件,发现这些类无法在APP_Code文件夹以外被引用. 解决办法: 选中类文 ...

  2. (一)Mahapps安装与使用

    一.Mahapps安装 1.NuGet程序包安装 右击项目-->选择 “管理Nuget程序包”-->搜索“MahApps.Metro” 2.包管理控制台 选择“工具”-->“NuGe ...

  3. Hello World! 我的程序员入坑之旅!

    先说下本文标题,各行各业都有自己的行规和一些内行人玩的梗什么的,这是我开始写技术博客的第一篇,所以它的标题毫无疑问只能是Hello World! 介绍一下我自己 我算是一个少见的科班出身的开发者了,1 ...

  4. Eclipse (JavaEE版)中修改web项目的访问路径

    访问路径,也就是指在浏览器中访问该web系统时的根路径,比如http://localhost:8080/xxxx/index.jsp  这里的xxxx. 也就是request.getContextPa ...

  5. CentOS 7 - 最小化安装以及引发的问题!

    一,操作系统和虚拟机 操作系统:CentOS 7 官方网站:https://www.centos.org 下载地址:https://www.centos.org/download/ 下载版本分三个:D ...

  6. Ultra-QuickSort (POJ 2299)树状数组+离散化

    题目链接 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm ...

  7. 数组内数据不使用for循环实现多个移动

    题目: 有序数组中加入一个新的数据,需保持数组有序,如何操作? 方式A :for循环将后续数组依次后移. 方式B :内存拷贝 代码: /******************************** ...

  8. 如何使不同时区的时间与京8区一致?(JS实现)

    如何使不同时区的时间与京8区一致?(JS实现) Update:2019/1/28 更简单的是使用这个函数(toDate): // 自定义日期格式如下(年月日都必须提供): // "2011- ...

  9. webpack初学者(1)

    最近在学习webpack的总结,不完善的希望各位提出宝贵的建议.本篇是以webpack3.0版本为基础的学习总结. 一.webpack的概念及作用 wepack是一个模块打包兼优化工具.往往一个项目中 ...

  10. 【LOJ 2542】【PKUWC2018】 随机游走(最值反演 + 树上期望dp)

    哇我太菜啦555555 不妨钦定我们需要访问的点集为$S$,在$S$已知的情况下,我们令$f(x) $表示从$x$走到点集$S$中任意一点的期望步数. 若$x∈S$,则显然$f(x)=0$,否则$f[ ...