SSM4==通过XML配置spring 事务,通过纯注解配置spring 事务

        mysql的事务管理,是指在有关系的表(比如有外键)之间实现ACID,比如表A的金额列的外键是表B的金额列,那么如果A的金额减了300,B的金额也要减少300。如果A减300成功了,B失败了,mysql会自动回滚导致两个减都失败。

        可以看到上述两个表在数据库内部就是有联系的。那么如果表C和表D在数据库内是没有任何联系的,我也想让C减少300,D减少300必须同时成功和失败,怎么做呢?或者控制同一张表的不同行的金额一行增300另一行必须减300,怎么做呢?按三层架构开发的话,只能在业务层控制了。spring的事务管理实现了上述功能。

        JDBC的事务管理能实现上述功能,开启手动控制事务,编写代码执行,手动commit。步骤如下:

    1获取连接 Connection con = DriverManager.getConnection()
    2开启事务con.setAutoCommit(true/false);
    3执行CRUD
    4提交事务/回滚事务 con.commit() / con.rollback();
    5关闭连接 conn.close();

        spring的事务管理简化了JDBC的事务书写的2和4步骤,再搭配连接池简化了1和5步骤,再搭配mybatis简化了3步骤。使用注解开发配置事务的话(属于声明式事务),2和4这两个步骤直接简化成了两个注解和一个bean。在配置类上使用@EnableTransactionManagement告诉IOC容器我们使用了spring管理事务,在需要使用事务的接口或者方法上使用注解@Transactional声明里面的操作要么同时成功、要么同时失败。此时失败的原因不一定是数据库的原因,几个方法操作的表也不一定有外键这样的联系。先有数据源,再声明一个bean。最好不要在实现类上使用,不利于解耦。          

        spring采用的声明式事务(声明,意思就是使用注解。另外有种编程式事务,官方都不推荐就不学了)的粒度最小是到方法,意思就是能控制同时成功同时失败的最小的范围是在同一个方法内,如果是一个方法内部的某一个代码块那就失效了。根据代理、spring AOP的知识,背后的原理也能大概猜一下,应该就是在被@Transactiona注解的方法执行前和执行后加上一些操作而已。

Spring事务管理——使用XML配置声明式事务_Evankaka的专栏-CSDN博客_spring事务事务简介:事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性事务就是一系列的动作,它们被当作一个单独的工作单元。这些动作要么全部完成,要么全部不起作用事务的四个关键属性(ACID)① 原子性(atomicity):事务室一个原子操作,有一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用② 一致性(consistency):一旦所有事务动作完成,事务就被提交。https://blog.csdn.net/Evankaka/article/details/45478007?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~default-1.no_search_link        @Transactional默认有些异常不会回滚。ERROR系和runtime系的会回滚,其他的都不回滚,比如IOException,需要手动设置成@Transactional(rollbacker=(IOException.class)).

        事务管理员:

        事务协调员:

        事务的传播行为:实现日志@Transactional(propagation = Propagation.REQUIRES_NEW)

spring的事务管理纯注解实现的具体步骤:

        1、导入依赖包,因为spring是基于JDBC事务的,jdbc是jdk自带的,按理说只需要在spring-context基础上导入spring-jdbc就行了。但是没有数据源那事务就没有意义了,所以得有数据源,有了数据源再给配个Druid连接池,导入mysql-connector-java和druid依赖。为了简化CRUD操作,再导入mybatis,再导入spring提供的简化mybatis配置的mybatis-spring。为了简化测试,导入Junit依赖,为了简化Junit调用IOC容器中的bean来进行测试,导入spring-junit依赖。

        2、建立数据库、表,根据表建立实体类

        3、建立dao接口(使用注解配置SQL)目的是从数据库获取数据。编总配置类SpringConfig,打上@Configuration注解声明这是一个配置类,打上@ComponentScan("com.ldj")告诉spring哪些包中存在着要交给IOC容器管理的bean记得去扫描,打上@PropertySource("classpath:jdbc.properties")告诉spring数据库信息存在哪里,打上@Import({JdbcConfig.class,MybatisConfig.class})告诉spring还有两个子配置类记得去导入,打上@EnableTransactionManagement告诉spring我这个项目使用了事务记得扫描相关的注解。

        4、编写子配置类JdbcConfig和MybatisConfig,@Value获取properties文件中的值,@bean声明以下四个为IOC容器管理的bean,DruidDataSource和PlatformTransactionManager和SqlSessionFactoryBean和MapperScannerConfigurer。通过MapperScannerConfigurer会自动扫描dao将其代理类生成bean交给IOC管理,所以后续在service中我能够用@Autowired自动装配注入。

        5、编写service接口和实现类。在实现类上使用@Service声明为bean,不能在接口上声明。方便测试类后续调用,当然如果写了controller层,也方便controller层调用。在实现类中通过@Autowired自动装配注入dao

        6、在AccountService接口的方法上使用@Transactiona声明为事务,该方法内调用到的其他接口的方法,也可以同样的声明为事务,并且可以设置他们两者之间的协调关系。这就是事务的传播行为。

        7、编写测试类,注入service,调用方法测试。

==================================================================

项目结构:

package com.ldj.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.ldj")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}

        

        

package com.ldj.config;

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

import javax.sql.DataSource;


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
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager ptm = new DataSourceTransactionManager();
        ptm.setDataSource(dataSource);
        return ptm;
    }
}
package com.ldj.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.ldj.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.ldj.dao");//dao不需要通过打注解声明为bean,在这用编程式已经声明了
        return msc;
    }
}



package com.ldj.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Double money;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
package com.ldj.dao;

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

public interface AccountDao {

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

    @Update("update tb_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name, @Param("money") Double money);
}
package com.ldj.dao;

import org.apache.ibatis.annotations.Insert;

public interface LogDao {
    @Insert("insert into tb_log (info,createDate) values(#{info},now())")
    void log(String info);
}
package com.ldj.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.FileNotFoundException;
import java.io.IOException;

public interface AccountService {
    //rollback:设置当前事务参与回滚的异常,默认非运行时异常不参与回滚
//    @Transactional(rollbackFor = IOException.class)
    @Transactional
    public void transfer(String out,String in ,Double money) throws IOException;
}
package com.ldj.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);
}
package com.ldj.service.impl;

import com.ldj.dao.AccountDao;
import com.ldj.service.AccountService;
import com.ldj.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);
        }
    }

}
package com.ldj.service.impl;

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

@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;

    public void log(String out,String in,Double money ) {
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=1234
/*

Source Database       : spring_db

*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for tb_account
-- ----------------------------
DROP TABLE IF EXISTS `tb_account`;
CREATE TABLE `tb_account` (
  `id` int(255) NOT NULL AUTO_INCREMENT,
  `name` varchar(35) DEFAULT NULL,
  `money` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_account
-- ----------------------------
INSERT INTO `tb_account` VALUES ('1', '22', '33');
INSERT INTO `tb_account` VALUES ('2', '33', '55');
INSERT INTO `tb_account` VALUES ('3', '77', '88');
INSERT INTO `tb_account` VALUES ('7', '类', '999');
INSERT INTO `tb_account` VALUES ('8', 'Tom', '9650');
INSERT INTO `tb_account` VALUES ('9', 'Jerry', '10350');

-- ----------------------------
-- Table structure for tb_log
-- ----------------------------
DROP TABLE IF EXISTS `tb_log`;
CREATE TABLE `tb_log` (
  `info` varchar(50) DEFAULT NULL,
  `createDate` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_log
-- ----------------------------
INSERT INTO `tb_log` VALUES ('转账操作由Tom到Jerry,金额:50.0', '2021-11-08 19:38:36');
INSERT INTO `tb_log` VALUES ('转账操作由Tom到Jerry,金额:50.0', '2021-11-08 19:39:07');
INSERT INTO `tb_log` VALUES ('转账操作由Tom到Jerry,金额:50.0', '2021-11-08 20:38:31');
package com.ldj.service;

import com.ldj.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("Tom","Jerry",50D);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值