什么是事务?
对于spring jdbc我们如何进行事务控制呢?
通过实际的代码学习如何事务管理器完成实物的提交或者回滚
公司入职10名新员工,将员工批量的导入员工表中,唯一的要求就是一次全部导入成功,要么什么都不做。
创建service包 业务逻辑
批量导入方法
配置,完成注入
测试方法
植入service
调用
这里觉得程序写完了就大错特错了,因为对于这10个数据来说他们并不是在一个事务中完成的
为什么这么说打印下日志看一下
加入依赖
先删除增加的数据,重新执行
都是更新操作
从数据源获取jdbc数据连接
创建全新的连接
在新的连接中去执行sql语句
这一部分是完成了一条数据的新增部分
每执行一次循环都会重复的产生这个日志
这就意味着,在刚才循环的过程中,顶层并不是使用一个数据库连接而是创建了10个数据库链接,完成了十次插入的操作,同时也进行了10次提交。这十条数据在各自的事务中并不是一个整体,我们期望的正确结果是这10次insert操作应该是在1个数据库连接中重复的去执行insert
我们的需求是 10条必须全部插入成功如果出现问题就什么都不做
如何解决?那就是引入事务进行整体判断
使用程序代码是手动控制什么时候提交?什么时候回滚?
编程式事务需要配置一个核心的对象
事务管理器
基于数据源的管理器,需要绑定一个数据源
基于这个管理器就能完成对事物的整体提交和回滚
怎么用?
注入他
生成getset
此时就具备的事务管理功能
从这句以后所有数据的新增修改删除都会被直接放到事务区中由事务统一进行管理
事务状态包含了当前方法所执行事务的目前阶段
成功
出现错误要进行捕捉,进行回滚,将运行时异常原封不动的抛出去
运行
抛出异常意味着要在事务中进行回滚
部分数据不再出现
底层原理
第二次循环并没有创建新的数据库连接,而是去应用原有的唯一的数据库连接
第三次出现异常的时候 回滚
增加后所有的数据库连接都是在一个中完成的,写入的数据并不是直接进入表中而是放到了事务区中,之后再根据方法是否执行成功,来提交或者回滚
频繁地使用去完成数据的新增
完成10次后 一次性提交写入
编程式事务根据么个程序员的水平不同可能会出现遗忘事务控制的状况
代码演示, 打开s02基础工程
不修改原始代码的情况下配置声明式事务
引入依赖
事务管理器
事务通知配置
额外增加一个命名空间url地址
增加所对应描述文件xsd的地址
aop命名空间
、
aop的xsd地址
说明事务通知作用在那些类上要确定通知范围
测试运行
又有一个新问题现在只有一个bacthImport,但是未来service上有各种各样的方法,难道要在
定义成百上千个方法说明他是声明式事务吗其实不用
可以进行通配符映射batchImport可以写成
与之类似的
其它选项,当前面几项都不符合要求时其他的情况是否开启声明式事务?
默认使用
不适用,根据情况而定
答案
package com.imooc.spring.jdbc.dao;
import com.imooc.spring.jdbc.entity.Hotel;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class HotelDao {
private JdbcTemplate jdbcTemplate;
public Hotel findById(Integer OrderNo){
String sql="select* from hotel where OrderNo=?";
Hotel hotel = jdbcTemplate.queryForObject(sql, new Object[]{OrderNo}, new BeanPropertyRowMapper(Hotel.class));
return hotel;
}
public List findByCity(String city){
String sql="select * from hotel where city=?";
List list = jdbcTemplate.query(sql, new Object[]{city}, new BeanPropertyRowMapper(Hotel.class));
return list;
}
public int insert(Hotel hotel){
String sql="insert into hotel(orderNo,city,price,hotelName,arriveDate,leaveDate)values(?,?,?,?,?,?)";
int i = jdbcTemplate.update(sql, new Object[]{hotel.getOrderNo(), hotel.getCity(), hotel.getPrice(), hotel.getHotelName(), hotel.getArriveDate(), hotel.getLeaveDate()});
return i;
}
public int alter(Hotel hotel){
String sql="update hotel set city=?,price=?,hotelName=?,arriveDate=?,leaveDate=? where orderNO=? ";
int count = jdbcTemplate.update(sql, new Object[]{hotel.getCity(), hotel.getPrice(), hotel.getHotelName(),hotel.getArriveDate(), hotel.getLeaveDate(),hotel.getOrderNo()});
return count;
}
public int delete(Integer integer){
String sql="delete from hotel where orderNo=?";
int count = jdbcTemplate.update(sql, new Object[]{integer});
return count;
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
package com.imooc.spring.jdbc.entity;
import java.util.Date;
public class Hotel {
private Integer orderNo;
private String city;
private Float price;
private String hotelName;
private Date arriveDate;
private Date leaveDate;
@Override
public String toString() {
return "Hotel{" +
"orderNo=" + orderNo +
", city='" + city + '\'' +
", price=" + price +
", hotelName='" + hotelName + '\'' +
", arriveDate=" + arriveDate +
", leaveDate=" + leaveDate +
'}';
}
public Integer getOrderNo() {
return orderNo;
}
public void setOrderNo(Integer orderNo) {
this.orderNo = orderNo;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Float getPrice() {
return price;
}
public void setPrice(Float price) {
this.price = price;
}
public String getHotelName() {
return hotelName;
}
public void setHotelName(String hotelName) {
this.hotelName = hotelName;
}
public Date getArriveDate() {
return arriveDate;
}
public void setArriveDate(Date arriveDate) {
this.arriveDate = arriveDate;
}
public Date getLeaveDate() {
return leaveDate;
}
public void setLeaveDate(Date leaveDate) {
this.leaveDate = leaveDate;
}
}
package com.imooc.spring.jdbc.service;
import com.imooc.spring.jdbc.dao.HotelDao;
import com.imooc.spring.jdbc.entity.Hotel;
import java.util.Date;
public class HotelService {
private HotelDao hotelDao;
public void batchImport(){
for (int i=1;i<=5;i++){
if (i==3){
throw new RuntimeException();
}
Hotel hotel=new Hotel();
hotel.setOrderNo(100+i);
hotel.setCity("长沙");
hotel.setHotelName("酒店8"+i);
hotel.setArriveDate(new Date());
hotel.setLeaveDate(new Date());
hotel.setPrice(999.98f+i);
hotelDao.insert(hotel);
}
}
public HotelDao getHotelDao() {
return hotelDao;
}
public void setHotelDao(HotelDao hotelDao) {
this.hotelDao = hotelDao;
}
}
import com.imooc.spring.jdbc.dao.HotelDao;
import com.imooc.spring.jdbc.entity.Hotel;
import com.imooc.spring.jdbc.service.HotelService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext.xml"})
public class JdbcTemplateTestor {
@Resource
private HotelService hotelService;
@Resource
private HotelDao hotelDao;
@Test
public void testFindById(){
Hotel hotel = hotelDao.findById(10001);
System.out.println(hotel);
}
@Test
public void testFindByName(){
List 上海 = hotelDao.findByCity("上海");
System.out.println(上海);
}
@Test
public void testInsert(){
Hotel hotel=new Hotel();
hotel.setOrderNo(10099);
hotel.setCity("长沙");
hotel.setHotelName("酒店8");
hotel.setArriveDate(new Date());
hotel.setLeaveDate(new Date());
hotel.setPrice(999.98f);
int count = hotelDao.insert(hotel);
System.out.println(count);
}
@Test
public void testUpdate() throws ParseException {
String date1="2020-4-30";
String data2="2020-5-05";
SimpleDateFormat simpleDateFormat =new SimpleDateFormat("yyyy-MM-dd");
Hotel hotel = hotelDao.findById(10003);
hotel.setLeaveDate(simpleDateFormat.parse(date1));
hotel.setArriveDate(simpleDateFormat.parse(data2));
int count = hotelDao.alter(hotel);
System.out.println(count);
}
@Test
public void testDelete(){
int count = hotelDao.delete(10099);
System.out.println(count);
}
@Test
public void testBatchImport(){
hotelService.batchImport();
System.out.println("批量插入成功");
}
}
通过案例了解
propagation会为我们带来那些特性
还需要开发大量类似于batchimport的方法怎么办呢?
依赖于
新增一个方法
增加bean
测试
插入成功
作为当前代码是事物的嵌套使用
默认也是开启声明式事务的
当前的配置是有问题的
作为批量导入,这两个方法之间应该是互不影响的但是employeeservice却做不到这个职责
演示一下
数据没有插入成功
import1插入成功就应该插入,但实际结果却不是这样
startImportJob在运行时会创造一个事物,而内侧嵌套调用的importjob1和importjob2因为配置的也是required内测执行时发现外侧已经有现成的事务了,所以job1的事务就会加入到外侧事务当中,也就是说整体是在一个事务中完成数据的增删改操作
要是这两个方法相对于start方法独立该怎么做呢?
针对这两个方法在运行的过程中都会产生新的事务来自进行处理
更改后执行
数据就产生了
注解改造
设置扫描的基本包
针对系统底层公用类还是要自己增加bean来进行实例化
还要定义事务管理器
注解来完成对象实例化
完成对象的注入
测试一下
下面把关注点放在事务控制上,作为声明式事务如果利用注解非常的简单,全程只有一个注解
测试
事务被成功地回滚了
对应于不同的方法也可以单独设置
在这个查询方法上去应用声明式事务是无效的
进行单独设置采用非事务方式
针对batchservice来进行一下设置,import方法都是可以独立的在方法中运行
在方法上也可以设置默认值,不需要使用事务
测试,能否将前半部分成功的导入