Spring—事务

一觉醒来下雨了,今天还是蛮冷的,坐标无锡,及时添衣服啊。话说最近喜欢上了看衬衫,对格子衬衫感兴趣了(据说格子衬衫是程序员的标配,是我越来越像程序员了么...)。早上从床上爬起来就开始看视频了,之所以看了一天才更新那么一点,是因为对每个代码都有上机实践。实践的过程可就太痛苦了,不报错还好,报错也没事,最怕的就是不报错的bug,动辄花费几十分钟。。。好了废话不多说了,直接进入今天的主题,Spring的事务部分。

 


不知道大家有没有注意过银行转账的逻辑,很简单对吧。就是客户A向客户B转了C元钱,那么A对应的账户就需要减去C元,B对应的账户就要加上C元。使用Spring整合JDBC和Mybatis可以轻松完成这一业务逻辑。那么我先来演示一下如下过程。很简单的一张表哈,在tbl_account表中有两行记录,分别记录的Tom和Jerry两人的余额。在idea中使用配置文件的方式链接到数据库。

 

然后在主包下面的Config包下配置JdbcConfig的类。(节省篇幅,折叠一下啦)

 

复制代码
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
    private 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 transactionManager=new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}
View Code
复制代码

 

同样的,由于整合了Mybatis来对sql语句进行简化,也要在Config包下书写一个MybatisConfig类。

 

复制代码
public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        SqlSessionFactoryBean ssfb=new SqlSessionFactoryBean();
        ssfb.setTypeAliasesPackage("study.domain");
        ssfb.setDataSource(dataSource);
        return ssfb;
    }
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc=new MapperScannerConfigurer();
        msc.setBasePackage("study.dao");
        return msc;
    }
}
View Code
复制代码

 

在这两步都结束了之后,就开始书写Spring的配置文件的,非常简单哈。

 

1 @Configuration                        //注解声明其为配置文件
2 @ComponentScan("study")                   //扫描主包中的bean
3 @PropertySource("classpath:jdbc.properties")      //声明配置文件的来源
4 @Import({JdbcConfig.class, MybatisConfig.class})    //导入刚刚配置好的JdbcConfig和MybatisConfig
5 public class SpringConfig {
6 }

 

在主包下新建一个包(名字任取),用于放置数据类型,此处我用的是Account类型。

复制代码
public class Account {
    Integer id;
    String name;
    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 +
                '}';
    }
}
View Code
复制代码

 

在dao层来书写转出和转入的接口,用于后面service层的调用。

public interface AccountDao {
    @Update("update tbl_account set money = money + #{money} where name = #{name}")
    void inMoney(@Param("name") String name,@Param("money") Double money);      //@Param后面表示为该参数设置一个名字,后续就会将其传递到上面的sql语句中
    @Update("update tbl_account set money = money - #{money} where name = #{name}")
    void outMoney(@Param("name") String name,@Param("money") Double money);
}

最后来实现一下Service层即可,包括了service的接口和Impl。

public interface AccountService {
    public void transfer(String out,String in,Double money);
}
复制代码
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private LogService logService;
    @Override
    public void transfer(String out, String in, Double money) {
            accountDao.inMoney(in,money);
            accountDao.outMoney(out,money);
    }
}
复制代码

上述内容实现了之后,就写一个test来测试一下这个功能是否合格吧!(要整合JUnit,后面我会把需要的依赖的坐标都附上,供大家使用,当然版本不同,请甄别!)

复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= SpringConfig.class)
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;
    @Test
    public void testTransfer() throws IOException {
        accountService.transfer("Tom","Jerry",100D);
    }
}
复制代码

回到库中查看结果,可以发现,Tom和Jerry的余额都已经被修改,可以说这个功能做好了。。。。。吗?真的做好了吗?

 

 

在现实生活中,转账并非一瞬间的事情,人们的转账也并非一帆风顺,可能转账期间恰逢系统维护,那么转账的钱又将何去何从呢?我用一个简单的例子来验证一下,如果在刚刚的转账方法

处加入一个错误,例如int i=1/0,那么众所周知,程序会报错,还有呢?试着运行就可以发现,程序正常报错了,然而转账却出了问题,可以发现,Tom的资金并没有产生变化,而Jerry的长上却多出来了100块钱,对于Jerry来说是件好事,但对银行来说,就是破产的预告啦!由此可见,上面的方法还是有很大漏洞的!那么该如何解决这个问题呢?这时,就要引入今天的主角了—Spring的事务处理。

什么是Spring的事务?

事务就是逻辑上的一些组合,事务要么一起成功,要么一起失败(同成功,共失败)。比如刚刚的转账事务,在这个方法运行中,执行转账方法是一个事务,Tom扣钱是一个事务,Jerry加钱是一个事务,产生/0报错也是一个事务。那么当转账采用事务管理后,整个流程就合并成一个事务了,在此期间只要有一个出了问题,那么整个事务都会运行失败,里面的操作自然就会回滚,从而保持数据的安全。同样的,只有当所有的操作都成功了,那么整个事务才会成功。这就是Spring的事务管理。

那么如何开启事务呢?

很简单,只需要几步就能搞定。还记得上面的SpringConfig吗?

第一步:给要进行事务管理的方法对应的接口上配置注解@Transactional,降低藕合的同时,声明了这是一个事务。

第二步:在config包下的JdbcConfig中定义一个事务管理器,将其声明为一个Bean交给Spring管理

1     @Bean
2     public PlatformTransactionManager transactionManager(DataSource dataSource){
3         DataSourceTransactionManager transactionManager=new DataSourceTransactionManager();
4         transactionManager.setDataSource(dataSource);
5         return transactionManager;
6     }

第三步:告诉Spring,现在使用了事务管理,只需要在SpringConfig类上加上一个注解@EnableTransactionManagement就好了。

接着来运行一下看看,对于同样的/0异常,可以发现程序是正常报错的,在数据库中发现,Tom和Jerry的金额都没受到该异常的影响,数据并未提交,而是回滚了,可见事务管理十分成功!

事务相关配置

 

这些是事务还具有的方法,这边就不多阐述了,可以在api中查到对应的用法。

 


 

结束语

那么今天的学习分享就到这里了,下面附录一些常用的依赖坐标供大家参考。 话说各位读者,那么觉得我的内容还有什么可以优化的地方吗?字体什么看的舒服么?如有建议,必然虚心接受!

复制代码
<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.7</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.18</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.18</version>
        </dependency>
    </dependencies>
复制代码

 

 

 

posted @   GaryRoyal  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示