前面 java Spring5通过声明式事务(注解方式)完成一个简单的事务操作讲了注解方式实现的声明式事务 java Spring5 xml配置文件方式实现声明式事务讲了xml方式来实现事务
但他们其实都挺依赖xml配置的 这次 我们就来做一个 完全注解开发
首先 我们做一下准备工作
要有一个mysql数据库
下面有一个test数据库 数据库下面有一个user表
表参考数据如下图
然后创建一个java项目 引入如下依赖Jar包
然后 在项目src目录下创建一个包 叫 config 在config包下创建一个类 叫 TxConfig
参考代码如下
package config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;@Configuration //声明这是一个配置类
@ComponentScan(basePackages = {"senvice","dao"}) //开启src下的senvice包和dao的注解扫描
@EnableTransactionManagement //开启事务
public class TxConfig {//创建数据库连接池@Beanpublic DruidDataSource getDruidDataSource() {DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); //设置驱动 直接照抄就好druidDataSource.setUrl("jdbc:mysql:///test"); //数据库路径druidDataSource.setUsername("root"); //数据库用户名druidDataSource.setPassword("root"); //数据库密码return druidDataSource;}//创建jdbcTemplate对象@Beanpublic JdbcTemplate getJdbcTemplate() {JdbcTemplate jdbcTemplate = new JdbcTemplate();jdbcTemplate.setDataSource(getDruidDataSource());return jdbcTemplate;}//创建事务管理器对象@Beanpublic DataSourceTransactionManager getDataSourceTransactionManager() {DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();dataSourceTransactionManager.setDataSource(getDruidDataSource());return dataSourceTransactionManager;}
}
这样 就完全取代了我们之前在xml中写的内容 但相对的 有些东西有时可能要改
其中getDruidDataSource中 setUrl 链接的数据库 有时可能会换 我这里现在连的是本地jdbc:mysql中的test数据库
setUsername和setPassword的话 如果你不是经常改密码 基本也不需要动
ComponentScan注解的话 就经常要动 表示对那些表开启注解扫描 如果你不开注解扫描 写注解也是没用的
接下来 搭一个三层架构
然后 在src下创建一个包 叫 dao
dao目录下创建一个接口 叫 transfAccoDao 参考代码如下
package dao;public interface transfAccoDao {//修改用户余额void updateMoney(String money,int userId);//查询用户余额String CheckTheBalance(int userId);
}
这里创建了两个抽象方法 一个接收一个字符串类型的参数(用户余额) 一个数字类型的参数(用户id) 作用是根据用户id修改他的金额
然后第二个方法 返回一个字符串的值 接收一个数字类型的用户id参数 根据id去查用户的金额
可能有人会奇怪为什么要用字符串处理金额
因为数组类型有很烦的大小限制 用字符串就不用管了
然后 在dao层下创建一个类 叫 transfAccoDaoImpl 参考代码如下
package dao;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class transfAccoDaoImpl implements transfAccoDao {@Autowiredprivate JdbcTemplate jdbcTemplate;@Overridepublic void updateMoney(String money,int userId){System.out.println(money);System.out.println(userId);String sql = "UPDATE user SET user_deposit = ? WHERE user_id = ?;";int hangm = jdbcTemplate.update(sql,money,userId);}@Overridepublic String CheckTheBalance(int userId) {String sql = "select user_deposit from user WHERE user_id = ?;";String money = jdbcTemplate.queryForObject(sql,String.class,userId);return money;}
}
可以看到 它实现了刚才创建的transfAccoDao 接口 并重写了他的两个抽象方法 通过jdbcTemplate下的方法以sql为行为 进行了对应的数据库操作
然后在src下创建一个包 叫 senvice
下面创建一个类 叫 transfAccoSenvice 参考代码如下
package senvice;import dao.transfAccoDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
@Transactional
public class transfAccoSenvice {@Autowiredprivate transfAccoDao TransfAccoDao;//转账方法public void transferAccounts(int sponsorId, int recipientId, double money){Boolean paym = pay(money,sponsorId);if(paym){int i = 10/0;income(money,recipientId);System.out.println("交易完成");}}//支出方法public Boolean pay(double money,int userId) {String vacancies = CheckTheBalance(userId);double vacancie = Double.parseDouble(vacancies);if(money > vacancie) {System.out.println("余额不足");return false;}double settleAccounts = (vacancie - money);TransfAccoDao.updateMoney(String.valueOf(settleAccounts),userId);return true;}//收入方法public Boolean income(double money,int userId) {String vacancies = CheckTheBalance(userId);double vacancie = Double.parseDouble(vacancies);double settleAccounts = (vacancie + money);TransfAccoDao.updateMoney(String.valueOf(settleAccounts),userId);return true;}//查询指定用户余额public String CheckTheBalance(int userId){return TransfAccoDao.CheckTheBalance(userId);}
}
这里面 我们写了一个 CheckTheBalance通过调用dao层的方法 用id查询用户余额
一个收入 一个支出的方法 都是调用了查余额然后转成小数点类型 做完计算 再转回字符串 然后 调用dao层修改余额的方法实现的
转账方法 我们会扣一个用户余额 加 另一个用户余额 但他执行完扣 就一定会报错 因为 拿0做除数的方法必定会异常 异常之后程序就会停止
来到数据库 我们查看两个用户的余额
首先 我们转账那个方法 是先扣 然后再加 而 我们要做的逻辑是 李四转钱给张三 而 李四的第一段扣钱sql会执行成功 然后 就会走到拿0做除数的位置 报异常 这样 给张三加钱的操作就没有执行
而李四已经扣了 但张三没加 这就是有问题的 所以我们事务再异常时就会进行回滚 这样 李四的余额也会被还原成开始的值
然后我们在src下创建测试类
import config.TxConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import senvice.transfAccoSenvice;public class text {public static void main(String args[]) {ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);transfAccoSenvice transfAccoSenvice = context.getBean("transfAccoSenvice", transfAccoSenvice.class);transfAccoSenvice.transferAccounts(2,1,200.00);}
}
这里 我们就通过AnnotationConfigApplicationContext读取到了我们开始写的TxConfig配置类
然后 调用了transfAccoSenvice的转账方法 id为2 的是李四 id为1为张三 金额200 李四转钱张三
运行结果如下
很明显 第一段sql执行成功了 返回了 李四余额被减去的数值 10800.0
然后 我们去数据库 刷新表 然后重新打开
可以看到 因为数据回滚 我们的李四数据也被还原了
然后 我们到senvice下的transfAccoSenvice类中 将transferAccounts方法中的 int i = 10/0;干掉
这样 我们的代码就不会异常了
再次运行测试类 运行结果如下
可以看到 我们的代码就没有异常了 然后到数据库刷新表并重新打开
很明显 这次执行成功了 张三由三千六变成了三千八 李四由一万一 变成了 一万零八百
下一篇:【操作系统】Linux