(六)Spring-事务式声明


(六)Spring-事务式声明

一、回顾事务

1.1 事务特性:

  • ACID
  • 把一组业务当成一个业务来做,要么都成功,要么都失败!
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题。不能马虎!
  • 确保完整性和一致性!

1.2 事务ACID原则:

  • 原子性 A
  • 一致性 C
  • 隔离性 I
    • 多个业务可能操作同一个资源,如一条记录,防止数据损坏即确保完整性和一致性。
  • 持久性 D

二、spring中的事务管理

2.1 声明式事务:AOP

1 编写service层业务逻辑,包含事务

  • service层实现,通过容器注入属性mapper

  • 在业务逻辑中使用mapper即dao层

  • 编写事务,doTx()方法先添加后修改user,中间制造错误,如下:

package com.happy.service.user.impl;

import com.happy.mapper.user.UserMapper;
import com.happy.pojo.user.User;
import com.happy.service.user.UserService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier("userMapper")
    UserMapper userMapper;

    @Override
    public List getUserList() {
        List userList = userMapper.getUserList();
        return userList;


    }

    @Override
    public User getUserById(int id) {
        User user = userMapper.getUserById(id);
        return user;
    }

    @Override
    public int addUser(User user) {
        int result = userMapper.addUser(user);
        return result;
    }

    @Override
    public int deleteUser(User user) {
        int result = userMapper.deleteUser(user);
        return result;
    }

    @Override
    public int updateUser(User user) {
        int result = userMapper.updateUser(user);
        return result;
    }

    @Override
    public void doTx(){
        List userList = userMapper.getUserList();
        System.out.println(userList);

        System.out.println("==========执行一组业务,先增后删==============");
        User user = new User(521, "gao518", "1987518");
        /*1 增加*/
        System.out.println("1.增加用户");
        userMapper.addUser(user);
        /*制造报错*/
        int error=1/0;
        /*2 删除*/
        User user2 = new User(520, "gao518518", null);
        System.out.println("2.删除用户");
        userMapper.updateUser(user2);
        userList = userMapper.getUserList();
        System.out.println(userList);
    }
}

2 编写mapper接口和mapper.xml文件

package com.happy.mapper.user;

import com.happy.pojo.user.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface UserMapper {

    @Select("select * from user")
    List getUserList();

    User getUserById(@Param("id") int id);

    /*增*/
    int addUser(User user);

    /*删*/
    int deleteUser(User user);

    /*改*/
    int updateUser(User user);
}

<?xml version="1.0" encoding="UTF-8" ?>



    
    
    


    

    
        insert into user(`id`, `name`, `pwd`)
        values (#{id}, #{name}, #{pwd})
    

    
        
            delete from user where id=#{id};
        
    

    
        
            update user
            
                
                    name=#{name},
                
                
                    pwd=#{pwd},
                
            
            where id=#{id};
        
    

3 开启Spring的事务处理功能

在相关的spring xml中如spring-mybatis.xml,要开启 Spring 的事务处理功能,在 Spring 的配置文件中创建一个 DataSourceTransactionManager 对象:


  

4 配置切面(通知)和切点

  • 配置切面

    • 结合aop实现事务的织入
    • 配置事务的类即:定义事务切面和通知即事务增强方法
      • 经过实测,方法名必须和严格一致或使用通配符,如method name="do*" propagation="REQUIRED"
    • 配置事务的传播特性
  • 配置切点

    • 即配置通知的作用点
    • 作用点一般定义在service层,请注意
    • service层,作用在接口和实现类均可。即可以是com.happy.service.user.UserService.,也可以是com.happy.service.user.impl.UserServiceImpl.
<?xml version="1.0" encoding="UTF-8"?>


    

    

    
    
        
        
        
        
    


    
    
    
        
        
        
    

    
    
    
        
        
    


    
    
        
      
    

    
    
    
    
        
            
            
            
            
            
            
            
            
        
    

    
    
        
        
    

    

5 模拟controller调用service层方法,测试事务

  • 注意:必须从容器获取service层实例context.getBean("userService", UserService.class);,不能自己用构造器new
package com.happy.service.user;

import com.happy.pojo.user.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class UserServiceTest {


    @Test
    public void testGetUserList() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        List userList = userService.getUserList();
        for (User user : userList) {
            System.out.println(user);
        }
    }

    @Test
    public void testGetUserById() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        UserService userService = context.getBean("userService", UserService.class);
        User user = userService.getUserById(3);
        System.out.println(user);
    }

    @Test
    public void testTransaction() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        UserService userService = context.getBean("userService", UserService.class);

        //注意:必须是被容器管理的对象,执行相关方法,才能被AOP拦截。
        userService.doTx();
    }

}

2.2 编程式事务:需要在代码中,进行事务的管理。

public class UserService {
  private final PlatformTransactionManager transactionManager;
  public UserService(PlatformTransactionManager transactionManager) {
    this.transactionManager = transactionManager;
  }
  public void createUser() {
    TransactionStatus txStatus =
        transactionManager.getTransaction(new DefaultTransactionDefinition());
    try {
      userMapper.insertUser(user);
    } catch (Exception e) {
      transactionManager.rollback(txStatus);
      throw e;
    }
    transactionManager.commit(txStatus);
  }
}
  • 需要在原有业务逻辑上,通过手动代码实现事务,即try-catch+rollback

  • 要改变代码,现在一般不用了

2.3 小结

思考:

为什么需要事务

  • 如果不配置事务,可能存在数据提交不一致的情况。如银行转账绝对不允许。
  • 如果我们不在spring中去配置声明式事务,我们就需要在代码中手动配置事务
  • 事务在项目的开发中是否重要,涉及到数据的一致性和完整性问题,不容马虎。
  • 工作的时候,都建议配置上声明式事务,都是死的。