首页 > 其他分享 >Spring事务管理

Spring事务管理

时间:2023-01-08 20:00:37浏览次数:60  
标签:username 事务管理 money qzcsbj Spring import com public

一个转账的问题

创建表

 

添加数据

 

实体类

逆向生成实体类,并添加无参构造、带参构造、toString

package com.qzcsbj.bean;


public class Account {

  private long id;
  private String username;
  private double money;

  public Account() {
  }

  public Account(String username, double money) {
    this.username = username;
    this.money = money;
  }

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }


  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }


  public double getMoney() {
    return money;
  }

  public void setMoney(double money) {
    this.money = money;
  }

  @Override
  public String toString() {
    return "Account{" +
            "id=" + id +
            ", username='" + username + '\'' +
            ", money=" + money +
            '}';
  }
}

  

dao层(mapper层)

mapper接口

package com.qzcsbj.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qzcsbj.bean.Account;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
public interface AccountMapper extends BaseMapper<Account> {
}

 

service层

service接口

package com.qzcsbj.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.qzcsbj.bean.Account;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
public interface AccountService extends IService<Account> {
    // 这里只是演示,假设username是唯一的
    public int transferMoney(String fromName,String toName,double money);
}

  

service实现类

package com.qzcsbj.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qzcsbj.bean.Account;
import com.qzcsbj.mapper.AccountMapper;
import com.qzcsbj.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Autowired
    AccountMapper accountMapper;

    public int transferMoney(String fromName, String toName, double money) {
        // 账户A给账户B转钱,这里只是演示,假设账户A的钱≥要转出的钱
        // 账户A
        Account accountA = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", fromName));
        accountA.setMoney(accountA.getMoney()-money);
        int countA = accountMapper.updateById(accountA);
        // int countA = accountMapper.update(accountA,new QueryWrapper<Account>().eq("username",fromName));

        // 账户B
        Account accountB = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", toName));
        accountB.setMoney(accountB.getMoney()+money);
        int countB = accountMapper.updateById(accountB);
        // int countB = accountMapper.update(accountB,new QueryWrapper<Account>().eq("username",toName));

        return countA+countB;
    }
}

  

测试类

package com.qzcsbj.test;

import com.qzcsbj.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@RunWith(SpringJUnit4ClassRunner.class)  // 表示Spring和JUnit整合测试
@ContextConfiguration("classpath:applicationContext.xml")
public class TestTransaction {
    @Autowired
    AccountService accountService;

    @Test
    public void testTransferMoney(){
        String fromName = "jack";
        String toName = "tom";
        double money = 10.0;
        int i = accountService.transferMoney(fromName, toName, money);
        if (i==2){
            System.out.println(fromName + "给"+ toName + "转账" + money + "元成功");
        } else {
            System.out.println(fromName + "给"+ toName + "转账" + money + "元成功");
        }
    }
}

  

结果

 

表中数据正确

 

模拟异常

修改service实现类,模拟异常

package com.qzcsbj.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qzcsbj.bean.Account;
import com.qzcsbj.mapper.AccountMapper;
import com.qzcsbj.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {

    @Autowired
    AccountMapper accountMapper;

    public int transferMioney(String fromName, String toName, double money) {
        // 账户A给账户B转钱,这里只是演示,假设账户A的钱≥要转出的钱
        // 账户A
        Account accountA = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", fromName));
        accountA.setMoney(accountA.getMoney()-money);
        int countA = accountMapper.updateById(accountA);
        // int countA = accountMapper.update(accountA,new QueryWrapper<Account>().eq("username",fromName));

        // 模拟异常
        int i=1/0;

        // 账户B
        Account accountB = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", toName));
        accountB.setMoney(accountB.getMoney()+money);
        int countB = accountMapper.updateById(accountB);
        // int countB = accountMapper.update(accountB,new QueryWrapper<Account>().eq("username",toName));

        return countA+countB;
    }
}

 

测试结果

 

运行出异常了,jack的账户扣了钱(mybatis底层是jdbc,默认自动提交事务,所以转出这个操作会成功入库),tom的账户没变

 

所以,transferMoney这个业务方法需要在事务中运行才可以。

 

事务(Transaction)介绍

事务是一系列的动作,是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态。

在企业级应用程序开发中,事务管理是必不可少的技术,用来确保数据的完整性和一致性。

 

Spring事务管理的核心接口

org.springframework.transaction下三个接口是Spring中事务的顶级接口:平台事务管理器、事务定义接口、事务状态接口

 

 

mybatis底层是jdbc,所以需要配置DataSourceTransactionManager这个类

 

 

配置声明式事务:基于aop的xml配置

dao层,修改UserMapper,添加两个方法

package com.qzcsbj.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qzcsbj.bean.User;

import java.util.List;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
public interface UserMapper extends BaseMapper<User> {
    public List<User> getUsers();
    public int addUser(User user);
}

 

添加映射文件

<?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="com.qzcsbj.mapper.UserMapper">

    <select id="getUsers" resultType="com.qzcsbj.bean.User">
          select * from user
    </select>

    <insert id="addUser">
        insert into user values(null,#{username},#{password},#{realName},#{sex},#{birthday},#{phone},#{utype},#{addtime},#{adduser})
    </insert>
</mapper>

  

service接口添加两个方法

package com.qzcsbj.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.qzcsbj.bean.User;

import java.util.List;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
public interface UserService extends IService<User> {
    public List<User> getUsers();
    public int addUser(User user);
}

  

修改service实现类

package com.qzcsbj.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qzcsbj.bean.User;
import com.qzcsbj.mapper.UserMapper;
import com.qzcsbj.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @公众号 : 全栈测试笔记
 * @博客 : www.cnblogs.com/uncleyong
 * @微信 : ren168632201
 * @描述 : <>
 */
@Service
public class UserServiceImlp extends ServiceImpl<UserMapper,User> implements UserService {
    @Autowired
    UserMapper userMapper;


    public List<User> getUsers() {
        return userMapper.getUsers();
    }

    public int addUser(User user) {
        return userMapper.addUser(user);
    }
}

 

配置applicationContext.xml(基于这里<https://www.cnblogs.com/uncleyong/p/17026215.html>的xml添加如下内容)

下面只配置了一个方法需要织入事务

    <tx:advice id="adviser" transaction-manager="transactionManager">
        <!--配置哪些方法需要配代理织入事务-->
        <tx:attributes>
            <tx:method name="transferMoney"/>
        </tx:attributes>
    </tx:advice>

  

配置AOP切入点

    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.qzcsbj.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="adviser" pointcut-ref="pointcut"/>
    </aop:config>

 

测试报错

 

数据库回滚了,说明事务起作用了

 

其它配置

 

propagation:事务的传播性,非必须,默认值REQUIRED
isolation:隔离级别,非必须,mysql填可重复读REPEATABLE_READ;如果不配置就是默认isolation="DEFAULT"
timeout="-1",事务超时时间,单位是秒,非必须;默认值是-1,使用数据库底层的超时时间,如果底层数据库事务系统没有设置超时值,那么就是none,表示没有超时限制,也就是不超时;
read-only:只读,非必须,默认值是false,查询设置true,非查询设置false
no-rollback-for:非必须,示不被触发进行回滚的 Exception(s)
rollback-for:非必须,表示将被触发进行回滚的 Exception(s)

 

配置声明式事务:基于aop的注解配置

applicationContext.xml配置(基于这里<https://www.cnblogs.com/uncleyong/p/17026215.html>的xml添加如下内容)

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

    <!--开启注解管理事务-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

 

可以在方法上加注解

    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout = -1,readOnly = false,rollbackFor ={Exception.class})
    public int transferMoney(String fromName, String toName, double money) {
        // 账户A给账户B转钱,这里只是演示,假设账户A的钱≥要转出的钱
        // 账户A
        Account accountA = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", fromName));
        accountA.setMoney(accountA.getMoney()-money);
        int countA = accountMapper.updateById(accountA);
        // int countA = accountMapper.update(accountA,new QueryWrapper<Account>().eq("username",fromName));

        // 模拟异常
        int i=1/0;

        // 账户B
        Account accountB = accountMapper.selectOne(new QueryWrapper<Account>().eq("username", toName));
        accountB.setMoney(accountB.getMoney()+money);
        int countB = accountMapper.updateById(accountB);
        // int countB = accountMapper.update(accountB,new QueryWrapper<Account>().eq("username",toName));

        return countA+countB;
    }

 

测试异常

 

自动回滚了

 

也可以在类上加注解,表示全局,而方法上加注解表示局部,如果全局和局部都有注解,那么方法上的注解优先。

  

【bak】

原文会持续更新,原文地址:https://www.cnblogs.com/uncleyong/p/17035170.html

 

标签:username,事务管理,money,qzcsbj,Spring,import,com,public
From: https://www.cnblogs.com/uncleyong/p/17035170.html

相关文章

  • SpringBoot——Swagger2的集成和使用
    前言现在都奉行前后端分离开发和微服务大行其道,分微服务及前后端分离后,前后端开发的沟通成本就增加了。所以一款强大的RESTfulAPI文档就至关重要了。而目前在后端领域,基本......
  • SpringBoot——Redis 的集成和简单使用
    前言在实际工作中,在存储一些非结构化或者缓存一些临时数据及热点数据时,一般上都会用上mongodb和redis进行这方面的需求。所以本文准备讲下缓存数据库Redis的集成,同时会介......
  • spring boot——请求与参数校验——Request共享数据(域对象)
    注意:只能在一次请求转发中使用。                           packageorg.example.controller;importorg.......
  • SpringBoot——异步开发之异步调用
    何为异步调用说异步调用前,我们说说它对应的同步调用。通常开发过程中,一般上我们都是同步调用,即:程序按定义的顺序依次执行的过程,每一行代码执行过程必须等待上一行代码执行......
  • SpringBoot——异步开发之异步请求
    何为异步请求在Servlet3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。如果一个请求需要进行IO操作,比如访问数据......
  • SpringBoot——定时任务的使用
    前言在JAVA开发领域,目前可以通过以下几种方式进行定时任务:Timer:jdk中自带的一个定时调度类,可以简单的实现按某一频度进行任务执行。提供的功能比较单一,无法实现复杂的调......
  • SpringBoot——日志管理
    SpringBoot在所有内部日志中使用CommonsLogging,但是默认配置也提供了对常用日志的支持,如:JavaUtilLogging,Log4J, Log4J2和Logback。每种Logger都可以通过配置使用控制台......
  • SpringBoot——多环境配置
    多环境配置在开发应用时,常用部署的应用是多个的,比如:开发、测试、联调、生产等不同的应用环境,这些应用环境都对应不同的配置项,比如swagger一般上在生产时是关闭的;不同环境数......
  • SpringBoot——常用注解介绍及简单使用
    常用注解@SpringBootApplication前几章,在系统启动类里面,都加入了此启动注解,此注解是个组合注解,包括了@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentSc......
  • SpringBoot——过滤器、监听器、拦截器
    前言在实际开发过程中,经常会碰见一些比如系统启动初始化信息、统计在线人数、在线用户数、过滤敏高词汇、访问权限控制(URL级别)等业务需求。这些对于业务来说一般上是无关......