spring_day04

spring_day04

事务的概念回顾

事务的概念:

一个包含多个操作的逻辑单元,要么同时成功,要么同时失败。

事务的四大特性:

  • 原子性

  • 一致性

  • 持久性

  • 隔离性

事务并发带来的读问题:

  • 脏读

  • 不可重复读

  • 虚读/幻读

隔离级别:

  • read uncommitted
  • read committed
  • repeatable read
  • serializeable

事务管理的核心对象

事务管理器对象:PlatformTransactionManager

事务管理器的顶层接口,定义了事务操作最基础的提交,回滚等方法。在使用时,需要根据不同的dao层技术选择不同的实现类,如果dao层的实现技术为jdbc或者mybatis,则应选择DataSourceTransactionManager

事务定义对象:TransactionDefinition

用于封装事务相关属性

  • 隔离级别
  • 是否只读
  • 传播行为
  • 超时时间

事务状态对象:TransactionStatus

用于封装事务的状态

使用什么样的事务管理器,设置哪些属性,最终决定事务的状态。

编程式事务

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    private DataSource dataSource;
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void transfer(String outName, String inName, Double money) {
        // 准备事务对象(开启事务)
        DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
        TransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus status = manager.getTransaction(definition);
        try {
            accountDao.outMoney(outName, money);
            int i = 1 / 0;
            accountDao.inMoney(inName, money);
            // 提交事务
            manager.commit(status);
        } catch (Exception e) {
            e.printStackTrace();
            // 回滚事务
            manager.rollback(status);
        }
    }
}

AOP改造编程式事务

1)业务层只保留核心业务功能

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(String outName, String inName, Double money) {
        accountDao.outMoney(outName, money);
        int i = 1 / 0;
        accountDao.inMoney(inName, money);
    }
}
  1. 编写事务通知类,通过环绕通知方法实现事务管理
public class TxAdvice {
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Object tx(ProceedingJoinPoint pjp) {
        // 准备事务管理相关对象
        DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
        TransactionDefinition definition = new DefaultTransactionDefinition();
        TransactionStatus status = manager.getTransaction(definition);
        try {
            // 调用切入点方法
            Object rtValue = pjp.proceed();
            // 提交事务
            manager.commit(status);
            return rtValue;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            // 回滚事务
            manager.rollback(status);
            return null;
        }
    }
}

3)完成aop的配置

<!--配置通知对象-->
<bean id="txAdvice" class="com.itheima.advice.TxAdvice">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置aop-->
<aop:config>
    <!--公共切入点表达式-->
    <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
    <!--配置切面-->
    <aop:aspect ref="txAdvice">
        <aop:around method="tx" pointcut-ref="pt1"/>
    </aop:aspect>
</aop:config>

基于xml的声明式事务

声明式事务的概念

所谓的声明式事务指的是通过配置的方式实现事务管理。由于事务管理的代码是模板写法,几乎固定。所以spring内部对事务管理进行了再一次封装。

  1. 引入aspectjweaver坐标

​ 由于声明式事务的底层原理还是aop,所以需要引入aspectjweaver坐标

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

2)在spring核心配置文件中完成配置

  • 配置事务管理器

  • 配置事务通知

    事务管理器想要能进行事务管理需要设置事务的相关属性

  • 配置aop

    在进行aop配置时,不需要配置aop:aspect切面,而是配置aop:advisor顾问,顾问知道如何管理事务,只用将通知和需要管理的业务方法告知它即可。

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:property-placeholder location="classpath:*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="com.itheima.domain"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.dao"/>
    </bean>

    <!--配置事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--配置事务的通知:给事务管理器对象指定相关的属性-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
            <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
        </tx:attributes>
    </tx:advice>

    <!--配置aop-->
    <aop:config>
        <!--抽取公共切入点表达式-->
        <aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
    </aop:config>

</beans>

tx:method相关属性介绍

属性名 解释
method 需要被事务管理的方法名,支持通配符
read-only 是否只读,默认值为false,
查询方法可以配置为true,提升connection底层效率
isolation 事务的隔离级别,默认为DEFAULT,跟数据库的默认隔离级别一致
timeout 事务的超时时间,默认为-1
rollback-for 进行回滚的异常类型,写法为异常的全类名
多个值之间通过,分割
no-rollback-for 不进行回滚的异常类型,写法为异常的全类名
多个值之间通过,分割
propagation 事务的传播行为

事务传播行为

传播行为的概念

事务传播行为(Propagation Behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 所以讨论传播行为的前提是有业务方法的相互嵌套。 例如methodA事务方法调用methodB方法时,传播行为就是用来控制,methodB的事务该如何进行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jhBkWElZ-1608041302511)(.\传播行为.png)]

基于xml结合注解的声明式事务

1)在xml配置文件中删除tx:advice和事务的aop相关配置

2)配置事务的注解驱动

<!--配置事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--开启事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
  1. 在需要被事务管理的业务层接口,或者业务层实现类上,或者接口和类的方法上添加@Transactional注解
@Transactional
public interface AccountService {

    /**
     * 转账操作
     * @param outName     出账用户名
     * @param inName      入账用户名
     * @param money       转账金额
     */
    public void transfer(String outName, String inName, Double money);
}

通常情况注解设置在业务层接口上,接口上的为通用配置,对于特殊的方法,可以在方法上添加特殊的配置,也就是方法上的@Transactional注解优先级高于接口和类上的,可以在该注解中配置事务的属性,配置格式如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUrC1VvR-1608041302515)(C:\Users\65710\Desktop\spring_day04\注解属性.png)]

纯注解驱动

实现纯注解驱动,核心是通过EnableTransactionManagement注解替换<tx:annotation-driven/>标签

1)编写Spring核心配置类SpringConfig

@Configuration
@ComponentScan("com.itheima")
@EnableTransactionManagement // 事务的注解驱动
@Import(MybatisConfig.class)
public class SpringConfig {
    // 配置事务管理器
    @Bean("transactionManager")
    public PlatformTransactionManager getTransactionManager(DataSource dataSource){
        DataSourceTransactionManager manager = new DataSourceTransactionManager();
        manager.setDataSource(dataSource);
        return manager;
    }
}

2)编写Jdbc配置类JdbcConfig

@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean("dataSource")
    public DataSource getDataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        return dataSource;
    }
}
  1. 编写Mybatis配置类MybatisConfig
@Configuration
@Import(JdbcConfig.class)
public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setTypeAliasesPackage("com.itheima.domain");
        return factoryBean;
    }

    @Bean
    public MapperScannerConfigurer getMapperScannerConfigurer(){
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.itheima.dao");
        return configurer;
    }
}

taSource);
factoryBean.setTypeAliasesPackage(“com.itheima.domain”);
return factoryBean;
}

@Bean
public MapperScannerConfigurer getMapperScannerConfigurer(){
    MapperScannerConfigurer configurer = new MapperScannerConfigurer();
    configurer.setBasePackage("com.itheima.dao");
    return configurer;
}

}


上一篇:day04 python——列表


下一篇:Java 复习整理day04