目录
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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://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 {
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public 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;
}
@Override
public 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 {
@Resource
private StudentDao studentDao;
@Override
public 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);
}
}
@Override
public 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/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://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 {
@Resource
private StudentDao studentDao;
@Transactional //添加事务支持
@Override
public 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);
}
}
@Override
public 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 传播特性,则程序内部事务如下示意图: