Spring初识(四)(事务)

Spring中的事务

1.事务回顾

事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性:ACID

原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致. 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 持久性:一旦结束,数据就永久的保存到数据库.

如果不考虑隔离性: 脏读:一个事务读到另一个事务未提交数据 不可重复读:一个事务读到另一个事务已经提交数据(update)导致一个事务多次查询结果不一致 虚读:一个事务读到另一个事务已经提交数据(insert)导致一个事务多次查询结果不一致

事务的隔离级别: 未提交读:以上情况都有可能发生。 已提交读:避免脏读,但不可重复读,虚读是有可能发生。 可重复读:避免脏读,不可重复读,但是虚读有可能发生。 串行的:避免以上所有情况.

2.Spring中的事务

Spring中事务管理 分层开发:事务处在Service层.

Spring提供事务管理API

PlatformTransactionManager:平台事务管理器. ​

getTransaction(TransactionDefinition definition) ​

rollback(TransactionStatus status) ​

commit(TransactionStatus status) ​

TransactionDefinition:事务定义 ​

ISOLation_XXX:事务隔离级别. ​

PROPAGATION_XXX:事务的传播行为. ​

TransactionStatus:事务状态 ​ 是否有保存点 ​ 是否是一个新的事务 ​ 事务是否已经提交 ​ 关系:PlatformTransactionManager通过TransactionDefinition设置事务相关信息管理事务,管理事务过程中,产生一些事务状态,状态由TransactionStatus记录。

API详解: ​ PlatformTransactionManager:接口. ​ Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现

使用Spring JDBC或iBatis 进行持久化数据时使用(重点) ​ org.springframework.jdbc.datasource.DataSourceTransactionManager

使用Hibernate进行持久化数据时使用 ​ org.springframework.orm.hibernate.HibernateTransactionManager

使用JPA进行持久化时使用 ​

org.springframework.orm.jpa.JpaTransactionManager

当持久化机制是Jdo时使用 ​

org.springframework.jdo.JdoTransactionManager

使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用 ​ org.springframework.transaction.jta.JtaTransactionManager ​

TransactionDefinition: ​ ISOLATION_DEFAULT:默认级别.

Mysql --> repeatable_read | Oracle -->> read_commited

级别如下:

ISOLATION_READ_UNCOMMITTED ​

ISOLATION_READ_COMMITTED ​

ISOLATION_REPEATABLE_READ ​

ISOLATION_SERIALIZABLE

3.Sping中事务的传播行为

事务的传播行为:(不是JDBC事务管理,用来解决实际开发的问题.) 传播行为:解决业务层之间的调用的事务的关系.

PROPAGATION_REQUIRED: 支持当前事务,如果不存在 就新建一个

  • A,B 如果A有事务,B使用A的事务,如果A没有事务,B就开启一个新的事务.(A,B是在一个事务中。)

PROPAGATION_SUPPORTS: 支持当前事务,如果不存在,就不使用事务

  • A,B 如果A有事务,B使用A的事务,如果A没有事务,B就不使用事务.

PROPAGATION_MANDATORY: 支持当前事务,如果不存在,抛出异常

  • A,B 如果A有事务,B使用A的事务,如果A没有事务,抛出异常.

PROPAGATION_REQUIRES_NEW: 如果有事务存在,挂起当前事务,创建一个新的事务

  • A,B 如果A有事务,B将A的事务挂起,重新创建一个新的事务.(A,B不在一个事务中.事务互不影响.)

PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果有事务存在,挂起当前事务

  • A,B 非事务的方式运行,A有事务,就会挂起当前的事务.

PROPAGATION_NEVER: 以非事务方式运行,如果有事务存在,抛出异常 PROPAGATION_NESTED: 如果当前事务存在,则嵌套事务执行

  • 基于SavePoint技术.

    A,B A有事务,A执行之后,将A事务执行之后的内容保存到SavePoint.B事务有异常的话,用户需要自己设置事务提交还是回滚.

  • 常用:(重点) PROPAGATION_REQUIRED PROPAGATION_REQUIRES_NEW PROPAGATION_NESTED

4.Spring的事务管理

Spring的事务管理分成两类 编程式事务管理: ​ 手动编写代码完成事务管理.(了解) 声明式事务管理: 不需要手动编写代码,配置.

5.Spring的事务案例

模拟两个人转账的问题,当我们没有加入事务操作的时候,假如转出操作执行了,转出操作和转入操作之间抛出了异常,大家都知道,程序遇到异常的时候会中止,所以出现的结果就是这个人转出钱了,另一个人没收到钱,这个就出现了很大的bug,因此我们引入了事务的操作

5.1手动编码的方式完成事务管理(了解)

缺点:代码量增加,代码有侵入性.

   (1)创建数据库和money表

(2)创建db.properties文件

db.driverClassName = com.mysql.cj.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/javacoffee?serverTimezone=Asia/Shanghai&characterEncoding=UTF8
db.username=root
db.password=1234

(3)导入依赖,这里我就直接都复制出来了,包括下文所需的包

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.10</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>

(4)创建mapper 和 实现类 并定义转出钱和增加钱的方法

package com.coffee.mapper;

public interface Money {

    public void setMoney(String money,Integer id);

    public void getMoney(String money,Integer id);

}
package com.coffee.mapper.impl;

import com.coffee.mapper.Money;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class MoneyImpl extends JdbcDaoSupport implements Money {


    @Override
    public void setMoney(String money,Integer id) {
        String sql = "update money set money = money - ? where id = ?";
        getJdbcTemplate().update(sql,money,id);

    }

    @Override
    public void getMoney(String money,Integer id) {
        String sql = "update money set money = money + ? where id = ?";
        getJdbcTemplate().update(sql,money,id);
    }
}

(5)创建service 以及它的实现类

package com.coffee.service;

import java.sql.SQLException;

public interface MoneyService {
    public void money(String money,Integer id1,Integer id2) throws SQLException;
}

在实现类里面,使用了事务模板对象

package com.coffee.service.impl;

import com.coffee.mapper.impl.MoneyImpl;
import com.coffee.service.MoneyService;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;


public class MoneyServiceImpl implements MoneyService {

    private MoneyImpl moneyImpl;
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    public void setMoneyImpl(MoneyImpl moneyImpl) {
        this.moneyImpl = moneyImpl;
    }

    @Override
    public void money(String money, Integer id1, Integer id2) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                moneyImpl.setMoney(money,id1);
//                这里可以写一个异常来看看事务回滚是否执行了
//                int sb= 1/0;
                moneyImpl.getMoney(money,id2);
            }
        });
    }
}

(6)在applicationContext.xml文件里完成依赖注入

<?xml version="1.0" encoding="UTF-8"?>
        <beans xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans">

<!--        扫描文件,获取文件中存储的数据库属性-->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--        注入四个属性,获取道数据库连接对象datasource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.password}"></property>
                <property name="url" value="${db.url}"></property>
                <property name="driverClassName" value="${db.driverClassName}"></property>
        </bean>
<!--        事务核心管理器,封装的所有的事务操作,十分重要-->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        事务模板对象-->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
                <property name="transactionManager" ref="dataSourceTransactionManager"></property>
        </bean>
<!--        mapper层,使用了JdbcTemplate来进行修改的操作,因此需要传入datasource-->
        <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        service层,将mapper层中获取的对象进行依赖注入,以及事务模板对象-->
        <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl">
                <property name="moneyImpl" ref="moneyImpl"></property>
                <property name="transactionTemplate" ref="transactionTemplate"></property>
        </bean>
</beans>

(7)测试类

import com.coffee.service.MoneyService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.sql.SQLException;

public class MyTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext classPathXmlApplicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        MoneyService money2 = (MoneyService)classPathXmlApplicationContext.getBean("moneyService");
        try {
//三个参数分别为 转账的钱,转出的账户的id,转入的账户的id
            money2.money("100",1,2);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

两个人本来都有1000块钱,账户id为1的给账户id为2的转100块钱

结果如下

 当转出之后立马出现了异常,就会执行事务回滚,可以在两个方法执行中间假如一个异常

 执行后控制台输出语句:

 数据库为:

 可以看到两个人的账户余额并没有改变

5.7Xml配置(aop)的方式完成事务管理

修改AccountServiceImpl

package com.coffee.service.impl;

import com.coffee.mapper.impl.MoneyImpl;
import com.coffee.service.MoneyService;

public class MoneyServiceImpl implements MoneyService {

    private MoneyImpl moneyImpl;


    public void setMoneyImpl(MoneyImpl moneyImpl) {
        this.moneyImpl = moneyImpl;
    }

    @Override
    public void money(String money, Integer id1, Integer id2) {
          {
                moneyImpl.setMoney(money,id1);
//                这里可以写一个异常来看看事务回滚是否执行了
                int sb= 1/0;
                moneyImpl.getMoney(money,id2);
            }
    }
}

修改applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
        <beans xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans">

<!--        扫描文件,获取文件中存储的数据库属性-->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--        注入四个属性,获取道数据库连接对象datasource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.password}"></property>
                <property name="url" value="${db.url}"></property>
                <property name="driverClassName" value="${db.driverClassName}"></property>
        </bean>
<!--        事务核心管理器,封装的所有的事务操作,十分重要-->
        <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--配置事务通知-->
<!--         &lt;!&ndash; 企业中配置CRUD方法一般使用方法名+通配符*的形式配置通知,此时类中的方法名要和配置的方法名一致 &ndash;&gt;-->
<!--               &lt;!&ndash; 以方法为单位,指定方法应用什么事务属性-->
<!--                isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。-->
<!--                propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。-->
<!--                read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。-->
<!--                timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。-->
<!--                rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。-->
<!--                no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。-->
<!--             &ndash;&gt;-->
        <tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
                <tx:attributes>
                       <tx:method name="money" propagation="REQUIRED" isolation="REPEATABLE_READ" read-only="false"></tx:method>
                </tx:attributes>
        </tx:advice>


<!--        事务模板对象-->
        <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
                <property name="transactionManager" ref="dataSourceTransactionManager"></property>
        </bean>
<!--        mapper层,使用了JdbcTemplate来进行修改的操作,因此需要传入datasource-->
<!--       配置织入-->
        <aop:config>
<!--                配置切入点-->
                <aop:pointcut id="pc" expression="execution(* com.coffee.service.impl.MoneyServiceImpl.money(..))"/>
<!--                配置切面-->
                <!-- 配置切面 : 通知+切点 advice-ref:通知的名称 pointcut-ref:切点的名称 -->
                <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
        </aop:config>

        <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        service层,将mapper层中获取的对象进行依赖注入,以及事务模板对象-->
        <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl">
                <property name="moneyImpl" ref="moneyImpl"></property>
        </bean>
</beans>

5.8注解配置(aop)的方式完成事务管理

修改applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
        <beans xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd
         http://www.springframework.org/schema/aop
         http://www.springframework.org/schema/aop/spring-aop.xsd"
               xmlns:tx="http://www.springframework.org/schema/tx"
               xmlns:context="http://www.springframework.org/schema/context"
               xmlns:aop="http://www.springframework.org/schema/aop"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns="http://www.springframework.org/schema/beans">

<!--        扫描文件,获取文件中存储的数据库属性-->
        <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!--        注入四个属性,获取道数据库连接对象datasource-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="username" value="${db.username}"></property>
                <property name="password" value="${db.password}"></property>
                <property name="url" value="${db.url}"></property>
                <property name="driverClassName" value="${db.driverClassName}"></property>
        </bean>
<!--        事务核心管理器,封装的所有的事务操作,十分重要-->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        开启注解事务-->
        <tx:annotation-driven/>

        <bean id="moneyImpl" class="com.coffee.mapper.impl.MoneyImpl">
                <property name="dataSource" ref="dataSource"></property>
        </bean>
<!--        service层,将mapper层中获取的对象进行依赖注入,以及事务模板对象-->
        <bean id="moneyService" class="com.coffee.service.impl.MoneyServiceImpl">
                <property name="moneyImpl" ref="moneyImpl"></property>
        </bean>
</beans>

修改AccountServiceImpl

package com.coffee.service.impl;

import com.coffee.mapper.impl.MoneyImpl;
import com.coffee.service.MoneyService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public class MoneyServiceImpl implements MoneyService {

    private MoneyImpl moneyImpl;


    public void setMoneyImpl(MoneyImpl moneyImpl) {
        this.moneyImpl = moneyImpl;
    }

    @Override
    //注解可以直接在类上表示,但是如果该方法与类名上的配置不同,也可以单独在这个方法上配置注解
    @Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED,readOnly = false)
    public void money(String money, Integer id1, Integer id2) {
          {
                moneyImpl.setMoney(money,id1);
//                这里可以写一个异常来看看事务回滚是否执行了
                int sb= 1/0;
                moneyImpl.getMoney(money,id2);
            }
    }
}

总结:xml配置AOP和注解的方式比较简便,开发时候也经常用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇智波波奶茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值