美文网首页
四、为业务层添加声明式事务

四、为业务层添加声明式事务

作者: lifeline张 | 来源:发表于2018-08-31 11:42 被阅读0次

一、本课目标

  • 掌握SpringXML方式实现声明式事务处理
  • 掌握使用注解实现声明式事务处理

二、为业务层添加声明式事务

2.1配置声明式事务

问题:如何在添加用户的业务流程中控制事务?

分析:

  • 可以采用MyBatis控制事务
  • 事务应该在业务逻辑层控制
  • 硬编码方式,代码繁琐,且破坏分层,代码不易维护

解决方法:

  • 可以采用AOP的方式实现
  • Spring提供了声明式事务支持

声明式事务关注的核心问题是:对哪些方法,采用声明样的事务策略?
对哪些方法其实就是切入点,采取哪些事务策略其实就是增强。
配置步骤:
1、导入tx和aop命名空间
2、定义事务管理器Bean,为其注入数据源Bean
3、通过<tx:advice>配置事务增强,绑定事务管理器并针对不同方法定义事务规则
4、配置切面,将事务增强与方法切入点结合

配置文件:

<?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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    
    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms?
                        useUnicode=true&amp;characterEncoding=utf-8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="41312019"></property>
    </bean>
    <!-- 配置SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:cn/smbms/dao/**/*.xml</value>
            </list>
        </property>
    </bean>

    <!-- 配置SqlSessionTemplate -->
    <!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
    </bean> -->
    
    <!-- <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="cn.smbms.dao.user.UserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean> -->
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.smbms.dao"></property>
    </bean>
    <!-- Service -->
    <context:component-scan base-package="cn.smbms.service.user"></context:component-scan>
    <!-- 事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 事务增强 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="find*" timeout="1000"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置切面 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn.smbms.service..*.*(..))" id="myPoint"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint"/>
    </aop:config>
</beans>

userMapper.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.smbms.dao.user.UserMapper">
    <!-- 当数据库中的字段信息与对象的属性不一致时需要通过resultMap来映射 -->
    <resultMap type="User" id="userList">
        <result property="id" column="id" />
        <result property="userCode" column="userCode" />
        <result property="userName" column="userName" />
        <result property="phone" column="phone" />
        <result property="birthday" column="birthday" />
        <result property="gender" column="gender" />
        <result property="userRole" column="userRole" />
        <result property="userRoleName" column="roleName" />
    </resultMap>
    <!-- 查询用户列表(参数:对象入参) -->
    <select id="getUserList" resultMap="userList" parameterType="User">
        select u.*,r.roleName from smbms_user u,smbms_role r
        where u.userName like CONCAT ('%',#{userName},'%')
        and u.userRole = #{userRole} and u.userRole = r.id
    </select>
    <insert id="add" parameterType="User">
        insert into smbms_user (userCode,userName,userPassword,gender,
                birthday,phone,address,userRole,createdBy,creationDate) 
        values (#{userCode},#{userName},#{userPassword},#{gender},
                #{birthday},#{phone},#{address},#{userRole},
                #{createdBy},#{creationDate})
    </insert>
</mapper>

userMapper.java

package cn.smbms.dao.user;

import java.util.List;

import cn.smbms.pojo.User;

public interface UserMapper {
    /**
     * 查询用户列表(参数:对象入参)
     * 
     * @return
     */
    public List<User> getUserList(User user);
    
    public int add(User user);
}

测试类:

@Test
    public void testAdd() {
         ApplicationContext ctx = new ClassPathXmlApplicationContext(
                    "applicationContext.xml");
            UserService userService = (UserService) ctx.getBean("userService");
            
            User user = new User();
            user.setUserCode("test001");
            user.setUserName("测试用户001");
            user.setUserPassword("1234567");
            Date birthday = null;
            try {
                birthday = new SimpleDateFormat("yyyy-MM-dd").parse("1984-12-12");
            } catch (ParseException e) {
                e.printStackTrace();
            }
            user.setBirthday(birthday);
            user.setCreationDate(new Date());
            user.setAddress("地址测试");
            user.setGender(1);
            user.setPhone("13688783697");
            user.setUserRole(1);
            user.setCreatedBy(1);
            user.setCreationDate(new Date());

            boolean result = userService.addNewUser(user);

            logger.debug("testAdd result : " + result);
    }

运行结果:


image.png

这样单次的数据库的操作很难看出事务的作用,只有在一个业务逻辑里面同时多次去读取数据库的时候我们才可以发现具体在某一个数据操作发生了错误,具体的事务是怎么来控制的,我们才能够看出具体的事务控制的区别。基于这个考虑,添加一个addUsers()方法。

在service文件中增加方法:

package cn.smbms.service.user;

import java.util.List;

import cn.smbms.pojo.User;

public interface UserService {
    public List<User> findUsersWithConditions(User user);
    
    public boolean addNewUser(User user);
    
    public void addUsers(List<User> users);
}
package cn.smbms.service.user;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import cn.smbms.dao.user.UserMapper;
import cn.smbms.pojo.User;

@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier("userMapper")
    private UserMapper userMapper;

    @Override
    public List<User> findUsersWithConditions(User user) {
        try {
            return userMapper.getUserList(user);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw e;
        }
    }

    public UserMapper getUserMapper() {
        return userMapper;
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public boolean addNewUser(User user) {
        boolean result = false;
        if (userMapper.add(user) == 1) {
            result = true;
        }
        return result;
    }

    @Override
    public void addUsers(List<User> users) {
        for (int i = 0; i < users.size(); i++) {
            userMapper.add(users.get(i));
            throw new RuntimeException("test error");
        }
    }

}

测试类:

@Test
    public void testAdd() {
         ApplicationContext ctx = new ClassPathXmlApplicationContext(
                    "applicationContext.xml");
            UserService userService = (UserService) ctx.getBean("userService");
            
            User user = new User();
            user.setUserCode("test001");
            user.setUserName("测试用户001");
            user.setUserPassword("1234567");
            Date birthday = null;
            try {
                birthday = new SimpleDateFormat("yyyy-MM-dd").parse("1984-12-12");
            } catch (ParseException e) {
                e.printStackTrace();
            }
            user.setBirthday(birthday);
            user.setCreationDate(new Date());
            user.setAddress("地址测试");
            user.setGender(1);
            user.setPhone("13688783697");
            user.setUserRole(1);
            user.setCreatedBy(1);
            user.setCreationDate(new Date());

     //       boolean result = userService.addNewUser(user);
            List<User> users = new ArrayList<User>();
            users.add(user);
            users.add(user);
            userService.addUsers(users);
         //   logger.debug("testAdd result : " + result);
    }

此时的结果是数据库中不会添加进去任何数据,因为在添加第一条数据之后,抛出了自定义的异常,所以所有的数据都被回滚了。要么数据全部添加进去,要么全都添加不进去。

事务属性:


image.png
image.png

2.2使用注解实现事务处理

image.png

配置文件:

<?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:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    
    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms?
                        useUnicode=true&amp;characterEncoding=utf-8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="41312019"></property>
    </bean>
    <!-- 配置SqlSessionFactoryBean -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="mapperLocations">
            <list>
                <value>classpath:cn/smbms/dao/**/*.xml</value>
            </list>
        </property>
    </bean>
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.smbms.dao"></property>
    </bean>
    <!-- Service -->
    <context:component-scan base-package="cn.smbms.service.user"></context:component-scan>
    <!-- 事务管理器 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:annotation-driven transaction-manager="txManager"/>
    
    <!-- 事务增强 -->
    <!-- <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="find*" timeout="1000"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice> -->
    <!-- 配置切面 -->
    <!-- <aop:config>
        <aop:pointcut expression="execution(* cn.smbms.service..*.*(..))" id="myPoint"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint"/>
    </aop:config> -->
</beans>

serviceImpl:

package cn.smbms.service.user;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import cn.smbms.dao.user.UserMapper;
import cn.smbms.pojo.User;

@Transactional(propagation=Propagation.REQUIRED)
@Service("userService")
public class UserServiceImpl implements UserService {
    @Autowired
    @Qualifier("userMapper")
    private UserMapper userMapper;

    @Override
    public List<User> findUsersWithConditions(User user) {
        try {
            return userMapper.getUserList(user);
        } catch (RuntimeException e) {
            e.printStackTrace();
            throw e;
        }
    }

    public UserMapper getUserMapper() {
        return userMapper;
    }

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public boolean addNewUser(User user) {
        boolean result = false;
        if (userMapper.add(user) == 1) {
            result = true;
        }
        return result;
    }

    @Override
    public void addUsers(List<User> users) {
        for (int i = 0; i < users.size(); i++) {
            userMapper.add(users.get(i));
            throw new RuntimeException("test error");
        }
    }

}

这样同样也可以实现事务管理。
使用注解的时候事务属性如下:


image.png
image.png

三、小结

image.png

相关文章

  • 四、为业务层添加声明式事务

    一、本课目标 掌握SpringXML方式实现声明式事务处理 掌握使用注解实现声明式事务处理 二、为业务层添加声明式...

  • Spring高级源码笔记:什么是Spring中的声明式事务?作用

    编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务 声明式事务:通过xml或者注解配置的...

  • 6、事务注解Transactional

    大家都知道可以用@Transactional来注解业务层来实现声明式事务 @service@Transaction...

  • 办公系统

    报销流程 数据库设计 项目创建 包及全局配置 dao配置 biz配置 声明式事务需要配置在业务层 dao层封装为事...

  • spring事务(二) 声明式事务

    spring事务(二) 声明式事务 知识导读 声明式事务是对编程式事务的包装 声明式事务通过使用AOP来实现,注册...

  • Spring的事务传播行为

    前言 Spring同时支持编程事务策略和声明式事务策略,通常都推荐采用声明式事务策略。使用声明式事务策略的优势十分...

  • Spring的事务机制解析一

    一Spring事务的种类 1.声明式事务 2.编程式事务 二Spring事务的具体描述 (一)声明式事务 1.声明...

  • Spring事务——使用TransactionProxyFact

    Spring同时支持编程式事务策略和声明式事务策略,大部分时候,我们都推荐采用声明式事务策略。使用声明式事务策略的...

  • 事务的两种形式

    事务的分类 编程式事务:是指在代码中手动的管理事务,缺点:代码侵入性太强 声明式事务:基于AOP面向切面,将业务与...

  • @Transactional事务不生效的几种解决方案

    Spring事务管理方式 编码式事务管理:将事务控制代码编写在业务代码之中。 声明式事务管理:基于AOP(面向切面...

网友评论

      本文标题:四、为业务层添加声明式事务

      本文链接:https://www.haomeiwen.com/subject/fwdswftx.html