目录
Spring JDBC基础概念
Spring声明式事务
事务传播方式
Spring JDBC基础概念
Spring JDBC 封装了原生的JDBC API,使得处理关系型数据库更加简单。Spring JDBC的核心是JdbcTemplate,里面封装了大量数据库CRUD的操作。使用Spring JDBC有如下步骤:
1、pom增加依赖
<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.20.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.22.RELEASE</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version></dependency>
spring-context是spring的核心,负责容器中对象实例创建;spring-jdbc是对原生jdbc的封装;logback-classic主要为了数据库操作时辅助信息的日志输出。
2、IOC容器实例化数据源对象dataSource和JdbcTemplate对象
<?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:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="com.text"/><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx:3306/test"></property><property name="username" value="test"></property><property name="password" value="test"></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>
</beans>
3、在各个DAO实现类中注入JdbcTemplate对象,通过JdbcTemplate的API实现数据库CRUD
JdbcTemplate的API重要的API:
- jdbcTemplate.query* :各种查询方法
- jdbcTemplate.update:各种新增、修改、删除等方法
示例:学生信息查询、新增
package com.text.dao.impl;import com.text.dao.StudentDao;
import com.text.entity.Student;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;import javax.annotation.Resource;@Repository
public class StudentDaoImpl implements StudentDao {@Resourceprivate JdbcTemplate jdbcTemplate;@Overridepublic Student getById(String id) throws Exception {String sql = "select * from student where id = ?";Student student = jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<>(Student.class));return student;}@Overridepublic void insert(Student student) throws Exception {String sql = "insert into student(id,name,age) values (?,?,?)";jdbcTemplate.update(sql,new Object[]{student.getId(),student.getName(),student.getAge()});}
}
Spring声明式事务
上文的案例中,没有添加事务支持,如果在Service层封装了Dao层并且做了批量操作的动作,在批量操作时系统出现异常,会出现部分数据提交到数据库的情况,不满足数据同时提交、同时回滚的情况,参考代码如下:
1、Service服务类
package com.text.service;import com.text.entity.Student;public interface StudentService {void batchSave() throws Exception;Student searchById(String id) throws Exception;
}
package com.text.service.impl;import com.text.dao.StudentDao;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class StudentServiceImpl implements StudentService {@Resourceprivate StudentDao studentDao;@Overridepublic void batchSave() throws Exception {for(int i=10;i<=20;i++) {if(i==15) {throw new RuntimeException("系统出现异常");}Student student = new Student(i+"","张三"+i,20);this.studentDao.insert(student);}}@Overridepublic Student searchById(String id) throws Exception {return this.studentDao.getById(id);}
}
2、测试类
package com.text;import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;public class Application {public static void main(String[] args) throws Exception {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");StudentService studentService = context.getBean("studentServiceImpl", StudentService.class);Student student = studentService.searchById("1");System.out.println(student);studentService.batchSave();}
}
3、运行结果分析:
从程序运行结果可以看出,程序循环插入10条数据时,程序每次都获取新的数据库连接,程序执行到第15条时,系统发生异常,前面10-14条数据提交到了数据库,不满足10条数据同时提交或同时回滚,通过配置事务管理器可以解决此问题。
Spring声明式事务是针对编程式事务而言的,编程式事务就是在程序内部手工的编写Commit和Rollback方法进行事务的提交和回滚,编写相对麻烦,本文重点讨论基于注解声明式事务。
Spring声明式事务,实现的原理就是AOP的环绕通知,在程序全部执行正常后,自动提交事务,在程序出现异常时,自动回滚事务。实现基于注解Spring声明式事务,经过如下步骤:
1、配置事务管理器及开启注解形式的声明式事务
<?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:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:component-scan base-package="com.text"/><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property><property name="url" value="jdbc:mysql://xxx.xxx.xxx.xxx:3306/test"></property><property name="username" value="test"></property><property name="password" value="test"></property></bean><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean><!--事务管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!-- 启用注解形式声明式事务 --><tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2、在需要事务的服务类上添加注解@Transactional
package com.text.service.impl;import com.text.dao.StudentDao;
import com.text.entity.Student;
import com.text.service.StudentService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service
public class StudentServiceImpl implements StudentService {@Resourceprivate StudentDao studentDao;@Transactional //添加事务支持@Overridepublic void batchSave() throws Exception {for(int i=10;i<=20;i++) {if(i==15) {throw new RuntimeException("系统出现异常");}Student student = new Student(i+"","张三"+i,20);this.studentDao.insert(student);}}@Overridepublic Student searchById(String id) throws Exception {return this.studentDao.getById(id);}
}
3、测试类(和上面一致),运行后结果:
从运行结果看出,由于service的批量插入方法上添加了事务支持,批量新增时,用的是同一个数据库连接,系统发生异常后,数据没有出现部分提交的情况。
事务传播方式
Spring事务的默认的事务传播特性是Required,即当前环境没有事务,则创建一个事务,如果已经在一个事务中,则加入这个事务中。几种事务传播特性如下:
- required 如果有事务正在运行,当前方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行;
- requires_new 总是主动开启事务;如果存在外层事务,就将外层事务挂起
- supports 如果不存在外层事务,就不开启事务;否则使用外层事务
- not_supported 当前的方法不应该运行在事务中,如果当前有运行的事务,将它挂起
- mandatory 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
- never 当前的方法不应该运行在事务中,如果运行在事务中就抛出异常
- nested 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行
示例:requires_new 总是主动开启事务;如果存在外层事务,就将外层事务挂起
A方法调用了B和C,其中A,B和C采用的是requires_new 传播特性,则程序内部事务如下示意图: