前言
一觉醒来下雨了,今天还是蛮冷的,坐标无锡,及时添衣服啊。话说最近喜欢上了看衬衫,对格子衬衫感兴趣了(据说格子衬衫是程序员的标配,是我越来越像程序员了么...)。早上从床上爬起来就开始看视频了,之所以看了一天才更新那么一点,是因为对每个代码都有上机实践。实践的过程可就太痛苦了,不报错还好,报错也没事,最怕的就是不报错的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>
标签:事务,String,Spring,class,money,public,name From: https://www.cnblogs.com/garyroyal/p/17096666.html