相比编程式事务,Spring声明式事务用得更多,这里就不纠结了,事务都通过注解方式来做
要新增schema的相关配置,可以搜索https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/xsd-configuration.html
事务相关,可以搜索spring-tx
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> </beans>
完整的schema如下,dataSource,jdbc和之前编程式事务一样
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="com.lihuia.declaration"></context:component-scan> <context:property-placeholder location="classpath:config/db.properties"/> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> </bean> <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
这里就直接通过一个简单的转账来测试,表里内容如下
mysql root@localhost:account_center> select * from account_info; +----+--------+---------+ | id | name | account | +----+--------+---------+ | 1 | lihui | 100 | | 2 | lilei | 100 | | 3 | liming | 100 | +----+--------+---------+
首先是Service,lilei减去10,lihui增加10,但是这里手动新增了一个异常除以0
package com.lihuia.declaration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Copyright (C), 2018-2019 * FileName: AccountService * Author: lihui * Date: 2019/11/4 */ @Component public class AccountService { @Autowired private AccountDao accountDao; public void changeAccount() { accountDao.reduce("lilei", 10); int x = 1 / 0; accountDao.increase("lihui", 10); } }
然后是Dao数据库操作,新增和减少两个方法
package com.lihuia.declaration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; /** * Copyright (C), 2018-2019 * FileName: AccountDao * Author: lihui * Date: 2019/11/4 */ @Component public class AccountDao { @Autowired private JdbcTemplate jdbcTemplate; public void increase(String name, Integer account) { String sql = "update account_info set account=account+? where name=?"; jdbcTemplate.update(sql, account, name); } public void reduce(String name, Integer account) { String sql = "update account_info set account=account-? where name=?"; jdbcTemplate.update(sql, account, name); } }
最后是测试类
package com.lihuia.declaration; import static org.testng.Assert.*; import org.testng.annotations.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Copyright (C), 2018-2019 * FileName: AccountTest * Author: lihui * Date: 2019/11/4 */ public class AccountTest { @Test public void accountTest() { ApplicationContext context = new ClassPathXmlApplicationContext("config/declaration.xml"); AccountService accountService = (AccountService) context.getBean("accountService"); accountService.changeAccount(); } }
执行结果报了一个错
java.lang.ArithmeticException: / by zero at com.lihuia.declaration.AccountService.changeAccount(AccountService.java:21) at com.lihuia.declaration.AccountTest.accountTest(AccountTest.java:24) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124) at org.testng.internal.Invoker.invokeMethod(Invoker.java:583) at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719) at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989) at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109) at org.testng.TestRunner.privateRun(TestRunner.java:648) at org.testng.TestRunner.run(TestRunner.java:505) at org.testng.SuiteRunner.runTest(SuiteRunner.java:455) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415) at org.testng.SuiteRunner.run(SuiteRunner.java:364) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208) at org.testng.TestNG.runSuitesLocally(TestNG.java:1137) at org.testng.TestNG.runSuites(TestNG.java:1049) at org.testng.TestNG.run(TestNG.java:1017) at org.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:73) at org.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:123)
来看下数据库操作的结果,果然lilei减了,但是lihui没加
mysql root@localhost:account_center> select * from account_info; +----+--------+---------+ | id | name | account | +----+--------+---------+ | 1 | lihui | 100 | | 2 | lilei | 90 | | 3 | liming | 100 | +----+--------+---------+
这样我的操作流程部分成功,部分失败,就是一个典型的事务问题,通过一个事务,如果出现失败,就可以进行回滚
如果用XML方式,由于用到了AOP,配置比较麻烦,用注解方式就比较简易
大致有如下几步:
1、配置事务管理器DataSourceTransactionManager
2、配置里开启事务
3、Service层添加@Transactional注解
因此只需要两处添加配置,首先xml里
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
然后是Service类
package com.lihuia.declaration; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; /** * Copyright (C), 2018-2019 * FileName: AccountService * Author: lihui * Date: 2019/11/4 */ @Transactional @Component public class AccountService { @Autowired private AccountDao accountDao; public void changeAccount() { accountDao.reduce("lilei", 10); int x = 1 / 0; accountDao.increase("lihui", 10); } }
执行结果还是除以0报错,但是lilei的账户恢复到了转账执行前的状态,也就是回滚了
mysql root@localhost:account_center> select * from account_info; +----+--------+---------+ | id | name | account | +----+--------+---------+ | 1 | lihui | 100 | | 2 | lilei | 100 | | 3 | liming | 100 | +----+--------+---------+
假如一定要用XML配置来设置,由于声明式事务用了aop,因此需要schema新增
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> </beans>
还有一堆aop的advice要配置,太麻烦了,有兴趣自行研究