mybatisplus 操作另一个数据库的数据_一文搞懂 Spring 数据库事务操作!

文章来源:https://mp.weixin.qq.com/s/VlNe2blmVpajsO0txvgQWg

作者:阿文

今天我们一起了解下Spring的数据库事务操作。在操作数据库时,我们经常会使用到事务,为此Spring 提供了专门的用于处理事务的API方便开发者调用,那么本文就着重来讲解下Spring 对于事务的相关功能。

1.Spring 事务的核心接口

Spring 通过一个名为spring-tx-4.3.6-RELEASE 的JAR包来管理事务,在这个JAR包中的org.Springframework.transaction 包中包含了三个接口文件:

  • PlatformTramsactionManager 主要用于管理事务,包括获取事务的状态、提交事务和回滚事务;
  • TramsactionDefinition 该接口是事务定义的对象,包括了获取事务的名称、隔离级别、事务的传播行为、超时时间、事务是否只读等;
  • TramsactionStatus 该接口是事务的状态,描述了某一个时间点事务状态信息,包括刷新事务、获取是否存在保存点、是否是新事务、是否回滚、设置事务回滚。

2.实例讲解

接下来我们将通过实例的方式来讲解如何使用注解的方式来通过Spring 进行事务的处理,手续我们在maven的pom.xml 中增加事务的JAR包:

 org.springframework      spring-tx      4.3.6.RELEASE

我们首先准备一个数据库:

CREATE TABLE IF NOT EXISTS `user`(                `id` INT UNSIGNED AUTO_INCREMENT,                `username` VARCHAR(100) NOT NULL,                `password` VARCHAR(40) NOT NULL,                `jifen` int(10) NOT NULL,                PRIMARY KEY ( `id` ))ENGINE=InnoDB DEFAULT CHARSET=utf8;

然后向数据库中写入一些数据,包括了用户名、密码和积分,如下所示:

MariaDB [spring_db]> select * from user;+----+----------+----------+-------+| id | username | password | jifen |+----+----------+----------+-------+|  1 | zhangsan | 123      |  1000 ||  2 | lisi     | 1234     |  1000 ||  3 | wangwu   | 1234     |  1000 |+----+----------+----------+-------+3 rows in set (0.000 sec)

我们要做的事情就是把张三的积分转给李四。

我们需要创建一个 User 类,如下:

package com.SpringDemo;public class User {    private Integer id;    private String username;    private String password;    private Integer jifen;    public Integer getId() {        return id;    }    public void setId(Integer id) {        this.id = id;    }    public String getUsername() {        return username;    }    public void  setJifen(Integer jifen){        this.jifen = jifen;    }    public Integer getjifen() {        return jifen;    }    public void setUsername(String username) {        this.username = username;    }    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;    }    public String toString() {        return "User [id=" + id + ", username=" + username + ", password=" + password + "]";    }}

然后创建一个接口 UserDao:

package com.SpringDemo;import java.util.List;public interface UserDao {    public int addUser(User user);    public int updateUser(User user);    public int deleteUser(int id);    //通过id查询用户    public User findUserById(int id);    //查询所有用户    public List findAllUser();    public void transfer(String outUser,String inUser,Integer jifen);}

在UserDao 接口中我们定义了一个transfer 的方法,它包含了三个参数分别是outUser、inUser、jifen。

接来下我们定义实现类 UserDAOImpl:

package com.SpringDemo;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.RowMapper;import org.springframework.transaction.annotation.Isolation;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;import java.util.List;public class UserDaoImpl implements UserDao {    private JdbcTemplate jdbcTemplate;    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {        this.jdbcTemplate = jdbcTemplate;    }    @Override    public int addUser(User user) {        String sql="insert into user(username,password) value(?,?)";        Object[] obj=new Object[]{                user.getUsername(),                user.getPassword()        };        int num=this.jdbcTemplate.update(sql,obj);        return num;    }    @Override    public int updateUser(User user) {        String sql="update user set username=?,password=? where id=?";        Object[] params=new Object[]{                user.getUsername(),                user.getPassword(),                user.getId()        };        int num=this.jdbcTemplate.update(sql,params);        return num;    }    @Override    public int deleteUser(int id) {        String sql="delete from user where id=?";        int num=this.jdbcTemplate.update(sql,id);        return num;    }    @Override    public User findUserById(int id) {        String sql="select * from user where id=?";        RowMapper rowMapper=new BeanPropertyRowMapper(User.class);        return this.jdbcTemplate.queryForObject(sql,rowMapper,id);    }    @Override    public List findAllUser() {        String sql="select * from user";        RowMapper rowMapper=new BeanPropertyRowMapper(User.class);        return this.jdbcTemplate.query(sql,rowMapper);    }    @Override    public void transfer(String outUser, String inUser, Integer jifen) {        // 赠送积分        this.jdbcTemplate.update("update  user set jifen=jifen+? where username=?",jifen,inUser);        // 模拟系统运行时的突发性问题        int i =1/0;        //赠送出积分        this.jdbcTemplate.update("update user set jifen=jifen-? where username=?",jifen,outUser);    }}

接下来我们定义一个applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>

Spring 的事务管理方式有2种,一种是传统的编程序事务管理,即通过代码来管理事务的开始、执行和异常以及回滚,一种是声明式管理,即通过配置文件的方式,原理是通过AOP技术实现,我们在实际开发过程中推荐使用声明式事务管理,效率会大大提升,因为只需要通过配置即可。

在该接口中我们我们重写transfer的方法,更新数据库将inUser 的积分进行增加,而对应的outUser 积分要进行减少,但是在这里我们要模拟系统运行的一些突然性问题。之后我们加了一个@Transactionl 注解,并设置了propagation、Isolation、readOnly 三个参数。

@Override  @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,    readOnly = false)    public void transfer(String outUser, String inUser, Integer jifen) {        // 赠送积分        this.jdbcTemplate.update("update  user set jifen=jifen+? where username=?",jifen,inUser);        // 模拟系统运行时的突发性问题        int i =1/0;        //赠送出积分        this.jdbcTemplate.update("update user set jifen=jifen-? where username=?",jifen,outUser);    }

注解 @Transactional 的参数含义如下:

ce43b4e17d65c2b370a73b2cf93b925e.png

@Transactional 除了 DEFAULT,还有其他属性,我们可以在Isolation 这个类中看到相对于的定位@Transactional 注解也可以添加到类级别上。当把@Transactional 注解放在类级别时,表示所有该类的公共方法都配置相同的事务属性信息。

401b620d840573d98a4fab42b0fc990c.png

Propagation 的属性如下:

eb8a241c9d91150fa9821b7ce930cdb8.png

此外使用@Transactional 必须保证是在public 级别的方法中使用,@Transactional 只能应用到 public 方法才有效,这是因为在使用 Spring AOP 代理时,Spring 在调用 TransactionInterceptor 在目标方法执行前后进行拦截之前,DynamicAdvisedInterceptor(CglibAopProxy 的内部类)的的 intercept 方法或 JdkDynamicAopProxy 的 invoke 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transactional 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。

接下来我们创建一个测试类来进行测试:

81f2bc3d87d9dd3e6be7df1cbecf9990.png

我们执行上述程序,可以发现报错了。程序报:

Exception in thread "main" java.lang.ArithmeticException: / by zero如图所示:

如图所示:

befbbe071a9ef5041c96e8c86d200733.png

此时,我们查看数据库中的数据没有发生任何变化:

MariaDB [spring_db]> select * from user;+----+----------+----------+-------+| id | username | password | jifen |+----+----------+----------+-------+|  1 | zhangsan | 123      |  1000 ||  2 | lisi     | 1234     |  1000 ||  3 | wangwu   | 1234     |  1000 |+----+----------+----------+-------+3 rows in set (0.000 sec)

而当我们把 int i =1/0; 注释掉再次执行就会发现程序执行没有报错了,并且数据发生了变化

MariaDB [spring_db]> select * from user;+----+----------+----------+-------+| id | username | password | jifen |+----+----------+----------+-------+|  1 | zhangsan | 123      |   900 ||  2 | lisi     | 1234     |  1100 ||  3 | wangwu   | 1234     |  1000 |+----+----------+----------+-------+3 rows in set (0.000 sec)

好了,以上就是关于Spring的事务管理介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值