AOP 应用 ——事务

Spring 编程式事务和声明式事务的区别

编程式事务处理:所谓编程式事务指的是通过编码方式实现事务,允许用户在代码中精确定义事务的边界。即类似于JDBC编程实现事务管理。管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

声明式事务处理:管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

事务的七种传播行为

PROPAGATION_REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
备注:常用的两个事务传播属性是14,即PROPAGATIONREQUIRED PROPAGATIONREQUIRES_NEW

五种隔离级别

级别说明
ISOLATION_DEFAULT这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
ISOLATIONREADUNCOMMITTED这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。 这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATIONREADCOMMITTED保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
ISOLATIONREPEATABLEREAD这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。 除了防止脏读,不可重复读外,还避免了幻像读。
关键词:
幻读(虚读)
事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录; 
通俗的说,幻读就是指在一个事务内读取了别的事务插入的数据,导致前后读取不一致(insert)

不可重复读取
事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录;
在一个事务内读取表中的某一行数据,多次读取结果不同.一个事务读取到了另一个事务提交后的数据.

脏读
事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。
通俗的说,脏读就是指一个事务读取了一个未提交事务的数据

XML方式

配置数据源

数据源依赖

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>
    <!-- 通过xml方式配置Spring声明式事务 配置start -->

    <context:component-scan base-package="com.lanou3g.spring.transaction" />

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="teacherDao" class="com.lanou3g.spring.transaction.dao.TeacherDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>
<!-- 第一步:配置数据源 -->
	<!--引入外部jdbc.properties配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties" />
    <!--引用外部jdbc.properties配置文件里的配置-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="${jdbc.url}" />
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="username" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
        <property name="connectionProperties">
            <props>
                <prop key="characterEncoding">${jdbc.characterEncoding}</prop>
            </props>
        </property>
    </bean>

初始化事务管理器

  <!-- 第二步:初始化事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

配置事务AOP通知

<!-- 第三步:配置事务AOP通知 -->

 <tx:advice id="txAdvice" transaction-manager="txManager">
     <tx:attributes>
     <!--只读 ,回滚 -->
         <tx:method name="insert*" rollback-for="ArithmeticException" />
         <tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
     </tx:attributes>
 </tx:advice>

定义AOP配置(将上面的通知和表达式组装到一起)

<!-- 第四步:定义AOP配置(将上面的通知和表达式组装到一起) -->
   <aop:config>
     <aop:pointcut id="all_dao_method" expression="execution(* com.lanou3g.spring.transaction.dao.*.*(..))" />
     <aop:advisor advice-ref="txAdvice" pointcut-ref="all_dao_method" />
 </aop:config> 

注解

数据源


/**
 * 配置数据源
 */
@PropertySource("classpath:jdbc.properties")
@Component
public class MyDataSource extends DriverManagerDataSource{
    public MyDataSource(@Value("${jdbc.driver}") String driver, @Value("${jdbc.url}") String url, @Value("${jdbc.user}") String userName, @Value("${jdbc.password}") String password, @Value("${jdbc.characterEncoding}") String characterEncoding) {
        super.setDriverClassName(driver);
        super.setUrl(url);
        super.setUsername(userName);
        super.setPassword(password);
        Properties conProperties = new Properties();
        conProperties.setProperty("characterEncoding", characterEncoding);
        super.setConnectionProperties(conProperties);
    }
}
// 此注解和@Component作用一样, 只是含有特定的语义(一般用来标注dao层的类)
@Setter
@Getter
@Repository
public class TeacherDaoImpl {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 查询teacher表所有数据
     * @return
     */
    // 通过@Transactional 开启事务、设置事务属性
    @Transactional(readOnly = true)
    public List<Teacher> queryAll() {
       //List<Teacher> teachers = jdbcTemplate.query("select * from teacher", new BeanPropertyRowMapper<Teacher>(Teacher.class));
       //return teachers;

        int rows = jdbcTemplate.update("delete from teacher;");
        System.out.println("影响了" + rows + "行.");
        return null;
    }

    /**
     * 插入teacher
     * @param teacher
     * @return 返回影响行数
     */
    // 通过@Transactional 开启事务、设置事务属性
    @Transactional(rollbackFor = {ArithmeticException.class})
    public int insertTeacher(Teacher teacher) {
        // 通过伪代码演示如果我们手写事务控制代码的套路
        // setAutoCommit(false);
        // beginTransaction
        // try{
        int result = jdbcTemplate.update("insert into teacher (tname) values (?)", teacher.getTname());
//        int retVal = 9 / 0;
        // commit;
        // } catch (Exception e) {
        // rollback;
        // }
        //
        return result;
    }
}

/**
 * 纯注解方式入口类
 */
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {

    /**
     * 定义JdbcTemplate对象,Spring给我们封装了所有的JDBC操作
     * @param dataSource
     * @return
     */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    /**
     * 定义事务管理器bean
     * @param dataSource
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppByTransactionAnnotation.class);
        testQuery(ctx);
//        testTransaction(ctx);
    }

    static void testQuery(ApplicationContext ctx) {
        TeacherDaoImpl teacherDao = ctx.getBean(TeacherDaoImpl.class);
        List<Teacher> teacherList = teacherDao.queryAll();
        for(Teacher teacher : teacherList) {
            System.out.println("id: " + teacher.getId()+", tname: " + teacher.getTname());
        }
    }

    static void testTransaction(ApplicationContext ctx) {
        TeacherDaoImpl teacherDao = ctx.getBean(TeacherDaoImpl.class);
        int rowEffect = teacherDao.insertTeacher(new Teacher("周伯通"));
        System.out.println("影响了" + rowEffect + "行.");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值