Spring(八)事务

事务

1、事务

(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账(后面案例基于此场景)

  • 张三 转账 100 元 给李四
  • 张三 少 100,李四 多 100

2、事务四个特性(ACID)

(1)原子性:原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。
(2)一致性:一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
(3)隔离性:隔离性是指并发的事务是相互隔离的。即一个事务内部的操作及正在操作的数据必须封锁起来,不被企图进行修改的事务看到 。
(4)持久性:持久性是指在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。 即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中。

3、Spring 事务管理介绍

1、事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2、在 Spring 进行事务管理操作
(1)编程式事务管理(用代码来表现事务,不推荐使用)
(2)声明式事务管理(使用)
3、声明式事务管理
(1)基于注解方式(推荐使用)
(2)基于 xml 配置文件方式(不推荐使用)
4、在 Spring 进行声明式事务管理,底层使用 AOP 原理
5、Spring 事务管理 API
提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
在这里插入图片描述

4、基于xml配置文件和注解声明式事务管理(个人推荐)

1)根据数据库创建实体类
public class User {
    private int id;
    private String name;
    private BigDecimal money;
    public User() { }
    public User(int id, String name, BigDecimal money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }
	public int getId() {return id; }
    public void setId(int id) {this.id = id;}
    public String getName() {return name; }
    public void setName(String name) {this.name = name; }
    public BigDecimal getMoney() {return money;}
    public void setMoney(BigDecimal money) {this.money = money; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

2)创建dao类和service接口以及实现类,并添加相关的注解,和业务方法

UserDao

public interface UserDao {
    public void reduceMoney();
    public void addMoney();
}

UserService

public interface UserService {
    public void accountMoney();
}

UserDaoImpl

@Component//创建对象
public class UserDaoImpl implements UserDao{
    @Autowired//注入JdbcTemplate
    private JdbcTemplate jdbcTemplate;
    @Override
    public void reduceMoney() {
        String sql = "update t_user set money=money-? where name=?";
        jdbcTemplate.update(sql,100,"张三");
    }
    @Override
    public void addMoney() {
        String sql = "update t_user set money=money+? where name=?";
        jdbcTemplate.update(sql,100,"李四");
    }
}

UserServiceImpl

@Service//生成对象
@Transactional//添加事务
public class UserServiceImpl implements UserService {
    @Autowired//注入UserDao
    private UserDao userDao;

    @Override
    //转账的方法
    public void accountMoney() {
        //张三 少 100
        userDao.reduceMoney();

        //李四 多 100
        userDao.addMoney();
    }
}
3)xml配置文件配置

(1)、在 spring 配置文件引入名称空间context、 tx
(2)、配置数据库连接池
(3)、生成JdbcTemplate 对象
(4)、开启组件扫描
(5)、创建事务管理器
(6)、开启事务注解

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql:///test" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </bean>
    <!-- JdbcTemplate 对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入 dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 组件扫描 -->
    <context:component-scan base-package="com.xiaoqiu"></context:component-scan>
    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"></tx:annotation-driven>
</beans>
4)测试类
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
        UserService userService = context.getBean("userServiceImpl", UserServiceImpl.class);
        userService.accountMoney();
    }
}

运行前数据库结果:
在这里插入图片描述
运行后数据库结果:
在这里插入图片描述

5)手动增加异常测试
@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Override
    //转账的方法
    public void accountMoney() {
        //张三 少 100
        userDao.reduceMoney();

        //手动造成异常
        int i = 10 / 0;

        //李四 多 100
        userDao.addMoney();
    }
}

测试前结果:
在这里插入图片描述
测试中报错了
在这里插入图片描述
此时查看数据库结果:数值没有改变,说明事务可以使用
在这里插入图片描述

5、声明式事务管理参数配置

1)在 service 类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数

propagation
ioslation
timeout
readOnly
rollbackFor
noRollbackFor

2)propagation:事务传播行为

多事务方法之间的调用(有事务的方法调用没事务的方法、没事务的方法调用有事务的方法、有事务方法之间的调用,事务方法是怎么处理的就叫事务的传播)
Spring框架事务传播行为有7种:
在这里插入图片描述

3)ioslation:事务隔离级别

(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
(2)有三个读问题:脏读、不可重复读、虚(幻)读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据
(4)不可重复读:一个未提交事务读取到另一提交事务修改数据
(5)幻读:一个未提交事务读取到另一提交事务添加数据
(6)解决:通过设置事务隔离级别,解决读问题
在这里插入图片描述

4)timeout:超时时间

(1)事务需要在一定时间内进行提交,如果不提交就进行回滚
(2)默认值是 -1 ,设置时间以秒单位进行计算

5)readOnly:是否只读

(1)读:查询操作,写:添加修改删除操作
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作
(3)设置 readOnly 值是 true,设置成 true 之后,只能查询

6)rollbackFor:回滚

设置出现哪些异常进行事务回滚

7)noRollbackFor:不回滚

设置出现哪些异常不进行事务回滚

6、完全基于注解声明式事务管理

1)根据数据库创建实体类
public class User {
    private int id;
    private String name;
    private BigDecimal money;
    public User() { }
    public User(int id, String name, BigDecimal money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }
	public int getId() {return id; }
    public void setId(int id) {this.id = id;}
    public String getName() {return name; }
    public void setName(String name) {this.name = name; }
    public BigDecimal getMoney() {return money;}
    public void setMoney(BigDecimal money) {this.money = money; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

2)创建dao类和service接口以及实现类,并添加相关的注解,和业务方法

UserDao

public interface UserDao {
    public void reduceMoney();
    public void addMoney();
}

UserService

public interface UserService {
    public void accountMoney();
}

UserDaoImpl

@Component//创建对象
public class UserDaoImpl implements UserDao{
    @Autowired//注入JdbcTemplate
    private JdbcTemplate jdbcTemplate;
    @Override
    public void reduceMoney() {
        String sql = "update t_user set money=money-? where name=?";
        jdbcTemplate.update(sql,100,"张三");
    }
    @Override
    public void addMoney() {
        String sql = "update t_user set money=money+? where name=?";
        jdbcTemplate.update(sql,100,"李四");
    }
}

UserServiceImpl

@Service//生成对象
@Transactional//添加事务
public class UserServiceImpl implements UserService {
    @Autowired//注入UserDao
    private UserDao userDao;

    @Override
    //转账的方法
    public void accountMoney() {
        //张三 少 100
        userDao.reduceMoney();

        //李四 多 100
        userDao.addMoney();
    }
}
3)创建配置类,使用配置类替代 xml 配置文件

(1)、添加@Configuration作为配置类,替代 xml 配置文件
(2)、添加@ComponentScan代替启组件扫描配置
(3)、添加@EnableTransactionManagement 开启事务
(4)、创建数据库连接池
(5)、创建 JdbcTemplate 对象
(6)、创建事务管理器

@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.xiaoqiu"})//代替启组件扫描配置
@EnableTransactionManagement //开启事务
public class SpringConfig {
    //创建数据库连接池
    @Bean//告诉IOC你可以从下面的方法中拿到一个bean对象
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///test");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }
    //创建 JdbcTemplate 对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        //到 ioc 容器中根据类型找到 dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入 dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    //创建事务管理器
    @Bean
    public DataSourceTransactionManager
    getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
4)测试类
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userServiceImpl", UserServiceImpl.class);
        userService.accountMoney();
    }
}

7、完全基于xml配置文件声明式事务管理

1)根据数据库创建实体类
public class User {
    private int id;
    private String name;
    private BigDecimal money;
    public User() { }
    public User(int id, String name, BigDecimal money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }
	public int getId() {return id; }
    public void setId(int id) {this.id = id;}
    public String getName() {return name; }
    public void setName(String name) {this.name = name; }
    public BigDecimal getMoney() {return money;}
    public void setMoney(BigDecimal money) {this.money = money; }
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

2)创建dao类和service接口以及实现类,并添加业务方法

UserDao

public interface UserDao {
    public void reduceMoney();
    public void addMoney();
}

UserService

public interface UserService {
    public void accountMoney();
}

UserDaoImpl

public class UserDaoImpl implements UserDao{
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void reduceMoney() {
        String sql = "update t_user set money=money-? where name=?";
        jdbcTemplate.update(sql,100,"张三");
    }

    @Override
    public void addMoney() {
        String sql = "update t_user set money=money+? where name=?";
        jdbcTemplate.update(sql,100,"李四");
    }
}

UserServiceImpl

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    //转账的方法
    public void accountMoney() {
        //张三 少 100
        userDao.reduceMoney();

        //手动造成异常
        //int i = 10 / 0;

        //李四 多 100
        userDao.addMoney();
    }
}
3)配置xml文件

(1)、在 spring 配置文件引入名称空间tx、aop
(2)、配置数据库连接池
(3)、生成JdbcTemplate 对象
(4)、创建对象
(5)、配置通知
(6)、配置切入点和切面
(7)、创建事务管理器

<?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:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.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">
    <!-- 数据库连接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          destroy-method="close">
        <property name="url" value="jdbc:mysql:///test" />
        <property name="username" value="root" />
        <property name="password" value="123456" />
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    </bean>

    <!-- JdbcTemplate 对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入 dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--创建对象-->
    <bean id="userDaoImpl" class="com.xiaoqiu.UserDaoImpl" autowire="byType"></bean>
    <bean id="userServiceImpl" class="com.xiaoqiu.UserServiceImpl" autowire="byType"></bean>

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--配置通知-->
    <tx:advice id="advice">
        <!--配置事务参数-->
        <tx:attributes>
            <!--指定哪种规则的方法上面添加事务-->
            <!--方式一:name里写具体的方法,告诉aop这个方法加上事务,可选参数propagation里指明事务的属性-->
            <tx:method name="accountMoney" propagation="REQUIRED"/>
            <!--方式二:以account开头的都加上事务-->
            <!--<tx:method name="account*"/>-->
        </tx:attributes>
    </tx:advice>

    <!--配置切入点和切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="point" expression="execution(* com.xiaoqiu.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="advice" pointcut-ref="point"/>
    </aop:config>
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
4)测试类
public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
        UserService userService = context.getBean("userServiceImpl", UserServiceImpl.class);
        userService.accountMoney();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值