JavaEE学习日志(九十二): spring事务控制

JavaEE学习日志持续更新----> 必看!JavaEE学习路线(文章总汇)

Spring

spring事务控制的api

  • org.springframework.orm.hibernate5.HibernateTransactionManager: 在hibernate环境下使用
  • org.springframework.jdbc.datasource.DataSourceTransactionManager: 在jdbcTemplate,mybatis(ibatis)环境下使用

事务的特性

  • 原子性:不可分割
  • 隔离性:事务之间的隔离级别
  • 一致性:要么全部完成,要么全部不完成
  • 持久性:一旦提交,持久化到数据库中

事务的隔离级别

  • 读未提交:read uncommited
    产生的问题:脏读、幻读、不可重复读
  • 读已提交:read commited
    产生的问题:幻读,不可重复读
    解决的问题:脏读
  • 重复读:repeatable read
    产生的问题:幻读
    解决的问题:脏读,不可重复读
  • 串行化(序列化):serializable
    解决的问题:所有问题,隔离级别最高,效率最低

详细看jdbc中的事务部分

数据库支持的隔离级别
mysql:全支持,默认是repeatable read
oracle:read commited、serializable、read only(只读) 默认是read commited

事务的传播

  • REQUIRED:必要的,如果没有事务,则新建事务;如果有事务,则加入事务中。spring中指定为默认值
    增删改

  • SUPPORTS:支持的,如果没有事务,非事务执行;如果有事务,加入事务
    查询

了解其他事务传播
MANDATORY:可以使用当前事务,如果没有事务,则抛出异常
REQUERS_NEW:新建一个事务,如果存在事务,则挂起事务
NOT_SUPPORTED:必须非事务执行,如果有事务则挂起
NEVER:非事务执行,如果存在事务,抛出异常
NESTED:有事务,嵌套执行;没有事务,执行REQUIRED

是否为只读的事务

  • 如果是查询,则为只读的事务,readOnly=true
  • 如果是增删改,则为非只读的事务,readOnly=false

spring声明式xml事务管理(推荐)

引入spring的事务管理jar包

<!--spring的事务管理jar包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

一、创建事务管理器对象

<!--创建事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源属性:事务存在连接中,连接存在连接池中-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>

二、事务的增强:过滤方法是否需要拦截
有四个属性

  • isolation:隔离级别,一般选择默认
  • propagation:传播的行为
  • read-only:是否为只读的事务,增删改选择非只读;查询选择只读
  • timeout:超时时间,单位为s,-1时为永不超时

注意*:通配符配置,如find*,只要以find开头即可

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--方法的过滤-->
        <tx:attributes>
            <!--指定需要拦截的方法
                isolation:隔离级别,一般选择默认
                propagation:传播的行为
                read-only:是否为只读的事务,增删改选择非只读;查询选择只读
                timeout:超时时间,单位为s,-1时为永不超时

                *:通配符配置,如find*,只要以find开头即可
            -->
            <!--增删改-->
            <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="del*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>
            <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false" timeout="-1"/>

            <!--查询-->
            <tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="query*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>
            <tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true" timeout="-1"/>

        </tx:attributes>
    </tx:advice>

可以将默认的属性删除掉

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--方法的过滤-->
        <tx:attributes>
            <!--指定需要拦截的方法
                isolation:隔离级别,一般选择默认
                propagation:传播的行为
                read-only:是否为只读的事务,增删改选择非只读;查询选择只读
                timeout:超时时间,单位为s,-1时为永不超时

                *:通配符配置,如find*,只要以find开头即可
            -->
            <!--增删改-->
            <tx:method name="insert*" />
            <tx:method name="add*" />
            <tx:method name="update*" />
            <tx:method name="del*" />
            <tx:method name="delete*"/>
            <tx:method name="save*" />

            <!--查询-->
            <tx:method name="find*"  propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*"  propagation="SUPPORTS" read-only="true" />
            <tx:method name="query*"  propagation="SUPPORTS" read-only="true" />
            <tx:method name="get*"  propagation="SUPPORTS" read-only="true" />

        </tx:attributes>
    </tx:advice>

再简化:可以将查询或增删改的其中一部分,用*来代替

<tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--方法的过滤-->
        <tx:attributes>
            <!--查询-->
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="select*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="query*" propagation="SUPPORTS" read-only="true"></tx:method>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"></tx:method>

            <!--其他配置事务,增删改-->
            <tx:method name="*"></tx:method>
        </tx:attributes>
    </tx:advice>

三、配置切面

  • advice-ref:通知关联对象
  • pointcut:切入点
<aop:config>
        <!--切面配置
            advice-ref:通知关联对象
            pointcut:切入点
        -->
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service..*.*(..))"></aop:advisor>
    </aop:config>

四、测试
业务层

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    AccountDao accountDao;
    @Override
    public void transfer(String fromName, String toName, Float money) {
        //查询转账的账户
        Account fromAccount = accountDao.findByName(fromName);
        //查询接收的账户
        Account toAccount = accountDao.findByName(toName);
        //开始转账
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //持久化到数据库
        accountDao.update(fromAccount);
        System.out.println(0/0);
        accountDao.update(toAccount);
    }
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testJdbcTemplate {
    @Autowired
    AccountService accountService;
    @Test
    public void test(){
        accountService.transfer("aaa","ccc",500f);

    }

}

注解的事务管理

引入依赖和上面完全相同
一、创建事务管理器对象

<!--创建事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源属性:事务存在连接中,连接存在连接池中-->
        <property name="dataSource" ref="springDataSource"></property>
    </bean>

二、开启事务的注解管理和aop的自动代理

<!--开启事务的注解管理-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    <!--开启aop的注解,自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

三、标记注解

1、在业务层的类上标记注解@Transactional

会出现一个问题:类中所有的方法都会使用事务管理

2、删除在类上的注解在方法上标记注解@Transactional:只有该方法按照事务执行

四、配置属性

@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false, timeout = -1)
    public void transfer(String fromName, String toName, Float money) {
        //查询转账的账户
        Account fromAccount = accountDao.findByName(fromName);
        //查询接收的账户
        Account toAccount = accountDao.findByName(toName);
        //开始转账
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //持久化到数据库
        accountDao.update(fromAccount);
        System.out.println(0/0);
        accountDao.update(toAccount);
    }

也可以省略

@Transactional()
    public void transfer(String fromName, String toName, Float money) {
        //查询转账的账户
        Account fromAccount = accountDao.findByName(fromName);
        //查询接收的账户
        Account toAccount = accountDao.findByName(toName);
        //开始转账
        fromAccount.setMoney(fromAccount.getMoney()-money);
        toAccount.setMoney(toAccount.getMoney()+money);
        //持久化到数据库
        accountDao.update(fromAccount);
        System.out.println(0/0);
        accountDao.update(toAccount);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章