当前位置: 首页 > article >正文

Spring入门篇7 --- spring事务

目录

spring事务的作用:保证在数据层或业务层的一系列数据库操作同成功或同失败。

1.案例:模拟银行账户间的转账业务

需求:A账户减钱,B账户加钱

分析:

①数据层提供基础操作,指定账户减钱、加钱

②业务层提供转账操作(transfer),调用减钱与加钱的操作

③提供2个账号和操作金额执行转账操作

④基于Spring整合MyBatis环境搭建上述操作

(1)创建数据库和配置文件

用户名:root

密码:123456

数据库名:spring_db

表名:tbl_account

id

name

money

1

Jack

1000

2

Rose

1000

/src/main/resources/jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.userName=root
jdbc.password=123456

(2)导入依赖和其它配置

pom.xml中导入的依赖有:spring-context、spring-jdbc、mybatis、mybatis-spring、druid、mysql-connector-java、spring-test、junit(spring-context、spring-jdbc、spring-test要版本一致,JDK版本最好要最新的)

/src/main/java/com/itheima/config/MybatisConfig.java

package com.itheima.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;

public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("com.itheima.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

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

/src/main/java/com/itheima/config/JdbcConfig.java

package com.itheima.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;

public class JdbcConfig {
    // 读取配置文件jdbc.properties的信息
    @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
    public DataSource dataSource() {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    // 配置事物管理器,mybatis使用的是jdbc事务
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

/src/main/java/com/itheima/config/SpringConfig.java

package com.itheima.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class, MybatisConfig.class})
// 开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}

(3)数据层和业务层代码

/src/main/java/com/itheima/dao/AccountDao.java

package com.itheima.dao;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;

public interface AccountDao {
    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name, @Param("money") Double money);

    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}

/src/main/java/com/itheima/service/AccountService.java

package com.itheima.service;

import org.springframework.transaction.annotation.Transactional;

public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    @Transactional
    public void transfer(String out,String in ,Double money) ;
}

/src/main/java/com/itheima/service/impl/AccountServiceImpl.java

package com.itheima.service.impl;

import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    public void transfer(String out,String in ,Double money) {
        accountDao.outMoney(out,money);
        // int i = 1/0;
        accountDao.inMoney(in,money);
    }
}

(4)测试

/src/test/java/com/itheima/service/AccountServiceTest.java

package com.itheima.service;

import com.itheima.config.SpringConfig;
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;
import java.io.IOException;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() throws IOException {
        accountService.transfer("Jack","Rose",100D);
    }
}

如果不用事务,在AccountServiceImpl.java的转账操作中,A账户减钱 ,B账户加钱,中间如果抛异常,减钱操作成功,加钱操作失败。这显然是不合适的。

2.Spring事务角色

事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法

事务协调员:加入事务方,在Spring中通常指代数据层方法,也可以是业务层方法。

3. 进阶版案例

需求:A账户减钱,B账户加钱,数据库记录转账日志(无论转账成功与否)。

分析:

①基于上方案例增加日志模块,实现在数据库中记录日志

②业务层转账操作:减钱、加钱、记录日志

③默认记录日志操作和转账操作同属一个事务,为了保证转账失败还能记录日志,需要设置记录日志的业务层接口上事务的传播行为REQUIRES_NEW(需要新事务)

在上述案例的基础上修改,其它代码不变

(1)建表tbl_log(id为主键,设置自动递增,可以为null)

id

info

createDate

0

(2)数据层和业务层代码

/src/test/java/com/itheima/dao/LogDao.java

package com.itheima.dao;

import org.apache.ibatis.annotations.Insert;

public interface LogDao {
    @Insert("insert into tbl_log(info,createDate) values(#{info},now())" )
    void log(String info);
}

/src/main/java/com/itheima/service/LogService.java

package com.itheima.service;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

public interface LogService {
    //propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    void log(String out, String in, Double money);
}

/src/main/java/com/itheima/service/impl/LogServiceImpl.java

package com.itheima.service.impl;

import com.itheima.dao.LogDao;
import com.itheima.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class LogServiceImp implements LogService {
    @Autowired
    private LogDao logDao;

    public void log(String out, String in, Double money) {
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}

(3)转账操作略作修改

/src/main/java/com/itheima/service/impl/AccountServiceImpl.java

package com.itheima.service.impl;

import com.itheima.dao.AccountDao;
import com.itheima.service.AccountService;
import com.itheima.service.LogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    // 该接口类只有一个实现类,这里自动装配的其实是实现类
    @Autowired
    private LogService logService;

    public void transfer(String out,String in ,Double money) {
        try {
            accountDao.outMoney(out,money);
            // int i = 1/0;
            accountDao.inMoney(in,money);
        } finally {
            logService.log(out, in, money);
        }
    }
}

(4)测试代码不变


http://www.kler.cn/a/4823.html

相关文章:

  • 【2024年华为OD机试】 (A卷,100分)- 租车骑绿岛(Java JS PythonC/C++)
  • 关于物联网的基础知识(二)——物联网体系结构分层
  • java_将数据存入elasticsearch进行高效搜索
  • HTTP 重定向状态码是什么意思?
  • 组提交_并行复制
  • FITC-PEG-SH,荧光素-聚乙二醇-巯基的用途:用于修饰氨基酸,蛋白质等
  • day9—编程题
  • 软件测试零基础好入门么
  • 电力行业等保定级评级依据是什么?分为几个等级?
  • 分布式锁实现方案
  • 【Note2】macvlan,sol
  • 【蓝桥杯-筑基篇】搜索
  • 微信小程序商城系统必须配置SSL证书吗?
  • 中国版ChatGPT在哪些方面具有快速发展的机会?
  • Ubuntu之NVIDIA GeForce显卡驱动安装
  • Redis 基本数据类型
  • 辉煌优配|猪肉概念股大幅拉升,巨星农牧涨停
  • Docker 安装 Redis
  • 2023年广东省网络安全竞赛——Linux 操作系统渗透解析(超级详细)
  • 基于WebSocket的网页聊天室
  • 【TPV】TPVFormer代码解析
  • python实战应用讲解-【numpy专题篇】常见问题解惑(十五)(附python示例代码)
  • 6 Nginx常用核心模块指令