Spring声明式事务,注解方式

相比编程式事务,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要配置,太麻烦了,有兴趣自行研究

发表评论