Spring5 学习笔记4

事务概念

1、什么是事务
(1)事务是数据库操作的最基本的单元,逻辑上是一组操作,要么都成功,如果有一个失败则所有操作都失败
2、事务四个特性(ACID)
(1)原子性

  • 一次事务提交,事务里的一组操作,要么全部提交成功,要么全部失败回滚,不能一部分成功,一部分失败
    (2)一致性
  • 事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
    (3)隔离性
  • 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
    (4)持久性
  • 即一个事务一旦提交,它对数据库中数据的改变应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

事务操作环境

1、定义一个演示场景
比如银行转账,张三从自己的账户上转账100元到李四的账户上,此时数据库需要进行变化,张三的账户需要减100,李四的账户应该加100元。
2、创建一个测试表
在这里插入图片描述
3、注入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"
       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">
    <!-- 注入组件扫描类 -->
    <context:component-scan base-package="com.company.base.spring9"/>
    <!-- 引入外部配置文件 -->
    <context:property-placeholder location="classpath:orm.properties"/>
    <!-- 注入数据源信息 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!-- 注入jdbcTemplate的操作bean -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

4、创建service和dao
service层定义一个转账的方法,而dao定义一个账户加钱和一个账户减钱的方法

@Service
public class BandCardService {

    @Autowired
    private BandCardDao bandCardDao;

    // 账户操作

    /**
     * 
     * @param fromName money转出的账户
     * @param targetName money转入的账户
     * @param money 转入的金额
     */
    public void operAccount(String fromName, String targetName, int money) {
        bandCardDao.reduceAccount(fromName, money);
        bandCardDao.addAccount(targetName, money);
    }
}

public interface BandCardDao {

    void addAccount(String name, int money);
    void reduceAccount(String name, int money);
}

@Repository
public class BandCardDaoImpl implements BandCardDao{
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void addAccount(String name, int money) {
        String sql = "update band_card set money = money + ? where name = ?";
        jdbcTemplate.update(sql, new Object[]{money, name});
    }

    @Override
    public void reduceAccount(String name, int money) {
        String sql = "update band_card set money = money - ? where name = ?";
        jdbcTemplate.update(sql, new Object[]{money, name});
    }
}

5、进行测试

public class TestBandCard {
    
    @Test
    public void test() {
        ApplicationContext context = 
                new ClassPathXmlApplicationContext("tx.xml");
        BandCardService service = context.getBean("bandCardService", BandCardService.class);
        service.operAccount("zhangsan", "lisi", 100);
    }
}

在这里插入图片描述

事务场景引入

(1)一组操作,有的执行成功,有的执行失败了
在这里插入图片描述

	public void operAccount(String fromName, String targetName, int money) {
        bandCardDao.reduceAccount(fromName, money);
        int i = 10/0;
        bandCardDao.addAccount(targetName, money);
    }

执行结果:抛出异常,zhangsan的账户减钱成功了,而lisi的账户由于异常抛出而未执行。
在这里插入图片描述
在这里插入图片描述

(2)事务执行的流程
在这里插入图片描述

Spring事务管理

1、事务一般添加到JavaEE三层结构里面的Service业务逻辑层
2、Spring进行事务管理操作
(1)编程式事务管理

	<!-- 创建事务管理器 -->
	<bean id="txMangaer" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
	public void operAccount(String fromName, String targetName, int money) {
        // 1、开启事务
        //默认事务定义,例如隔离级别,传播行为等
        TransactionDefinition tf = new DefaultTransactionDefinition() ;
        //开启事务transaction
        TransactionStatus transaction = txManager.getTransaction(tf);
        try {

            // 2、进行业务操作
            bandCardDao.reduceAccount(fromName, money);
            // 模拟异常
            int i = 10/0;
            bandCardDao.addAccount(targetName, money);
            // 3、没有遇到异常,正常提交事务
            txManager.commit(transaction);
            System.out.println("没有遇到异常,正常提交事务");
        } catch (Exception e) {
            // 4、遇到异常,事务进行回滚
            txManager.rollback(transaction);
            System.out.println("遇到异常,事务回滚");
        }
    }

进行测试
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

(2)声明式事务管理
3、声明式事务管理
(1)基于注解方式
(2)基于xml配置文件的方式
4、在Spring进行声明式事务管理,底层使用AOP原理
5、Spring事务管理API
(1)提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
在这里插入图片描述

基于注解方式

1、引入事务管理器

	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

2、引入tx命名空间和开启注解
在这里插入图片描述

 <!-- 开启事务注解,指定事务管理器 -->
    <tx:annotation-driven transaction-manager="txManager"/>

3、@Transactional注解的作用
(1)添加到类上,为这个类类中所有的方法添加事务
(2)添加到方法上,为这个方法添加事务
在这里插入图片描述

声明式事务管理参数配置

在这里插入图片描述

1、propagation:事务传播行为
是指当一个事务方法被另外一个事务方法调用时,这个事务方法如何进行
(1)REQUIRED(需要事务):如果有事务在运行,当前的方法的就在这个事务内运行,如果当前没有事务,则启动一个新的事务,并在自己的事务内运行
(2)REQUIRED_NEW(需要新的事务):无论当前是否有事务,都会启动一个新的事务,并在自己的事务内运行。并且如果当前有事务,会将当前事务挂起。
(3)SUPPORTS(支持事务,非强制性):如果有事务,则在事务中运行,如果没有事务,则以非事务的方式运行
(4)NOT_SUPPORTS(不支持事务,有则挂起):不支持事务,如果有事务,则将当前事务挂起,以非事务的方式运行
(5)MANDATORY(强制性的,必须要有事务,没有就异常):当前的方法必须在事务中运行,如果没有事务则会抛出异常
(6)NEVER(绝不,不能有事务,有则异常):不允许在事务中运行,如果当前有事务,则抛出异常
(7)NESTED(嵌套的,有则变嵌套,无则新建):如果有事务在运行,则当前方法以嵌套事务的方式运行,否则就启动一个新的事务,并在自己的事务内运行
2、ioslation:事务隔离级别
(1)隔离性
如果事务之间不进行隔离的话,那么事务并发时,会导致事务之间会互相影响。
(2)隔离性导致的问题

  • 脏读:一个未提交的事务读取到另一个未提交事务中的数据
  • 不可重复读:一个未提交的事务读取到另一个已提交事务的数据
  • 幻读:由于修改会被限制,所以不会产生不可重复读问题,但是会由于插入或删除导致产生幻读
    (3)隔离级别
  • 1、Read Uncommitted (读未提交)
    顾名思义,就是可以读到未提交的内容。因此,在这种隔离级别下,查询是不会加锁的,也由于查询的不加锁,所以这种隔离级别的一致性是最差的,可能会产生“脏读”、“不可重复读”、“幻读”。如无特殊情况,基本是不会使用这种隔离级别的。
  • 2、READ Commintted (读已提交)
    读提交,顾名思义,就是只能读到已经提交了的内容。这是各种系统中最常用的一种隔离级别,也是SQL Server和Oracle的默认隔离级别,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。这里多说点:那为什么“读提交”同“读未提交”一样,都没有查询加锁,但是却能够避免脏读呢?
    这就要说道另一个机制“快照(snapshot)”,而这种既能保证一致性又不加锁的读也被称为“快照读(Snapshot Read)”
    假设没有“快照读”,那么当一个更新的事务没有提交时,另一个对更新数据进行查询的事务会因为无法查询而被阻塞,这种情况下,并发能力就相当的差。而“快照读”就可以完成高并发的查询,不过,“读提交”只能避免“脏读”,并不能避免“不可重复读”和“幻读”。
  • 3、REPEATABLE READ(可重复读)
    可重复读,顾名思义,就是专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是MySql的默认隔离级别。
    在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了,而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。
  • 4、Serializable (串行化):
    这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。
    这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。
    3、timeout:超时时间
  • 1、事务需要在一定时间内进行提交,如果不提交就进行回滚
  • 2、默认值是-1,设置时间以秒为单位进行计算
    4、readOnly:是否只读
  • 1、读:查询操作,写:添加、修改、删除操作
  • 2、readOnly默认为false,表示可以进行增删改查的操作
  • 3、设置readOnly是true后,只能进行查询操作
    5、rollbackFor:回滚
  • 设置出现哪些异常需要回滚
    6、noRollbackFor:不回滚
  • 设置出现哪些异常不需要回滚

XML声明式事务管理

1、在spring配置文件中进行配置
第一步:配置事务管理器
第二步:配置通知
第三步:配置切入点和切面

<?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 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">
    <!-- 注入组件扫描类 -->
    <context:component-scan base-package="com.company.base.spring9"/>
    <!-- 引入外部配置文件 -->
    <context:property-placeholder location="classpath:orm.properties"/>
    <!-- 注入数据源信息 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!-- 注入jdbcTemplate的操作bean -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 注入事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置通知 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- 指定哪种规则的方法上面添加事务 -->
            <tx:method name="operAccount" isolation="REPEATABLE_READ"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置切面和切入点 -->
    <aop:config>
        <!-- 配置切入点 -->
        <aop:pointcut id="tx" expression="execution(* com.company.base.spring9.service.BandCardService.*(..))"/>
        <!-- 配置通知 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="tx"/>
    </aop:config>
</beans>

完全注解实现声明式管理

/**
 * @ClassName SpringConfigTx
 * @Description TOOD
 * @Autor
 * @Date
 * @Version 1.0
 **/
@Configuration
@ComponentScan(basePackages = {"com.company.base.spring10"})
@EnableTransactionManagement // 开启注解
@PropertySource(value = "classpath:orm.properties") // 引入数据源配置信息文件
public class SpringConfigTx {
    @Value("${jdbc.driverClassName}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 注入数据源dataSource
     * @return
     */
    @Bean
    public DruidDataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    /**
     * 注入jdbcTemplate
     * @param dataSource 会自动从IOC容器中找到我们自定义的dataSource
     * @return
     */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    /**
     * 注入事务管理器
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值