一、在JAVAEE中,事务一般用于Service层(业务逻辑层)
二、在Spring中实现事务管理
(1)编程式事务管理:在方法中利用try-catch捕获异常并处理。
(2)声明式事务管理:通过配置实现。
三、声明式事务管理(基于AOP)
(1)基于注解方式实现。
(2)基于配置文件实现。
(3)Spring提供一个接口,代表事务管理器,针对不同DAO层框架提供不同的实现类。
(4)事务常用参数
- propagation:事务的传播行为。多个方法进行调用时,如果某些方法配置了事务,Spring怎样实现对事务进行管理。当值为REQUIRED时,所有配置了事务的方法共用一个事务。当值为REQUIRES_NEW时,所有配置了事务的方法都会创建一个事务。
- isolation:事务的隔离等级。默认为数据库的隔离级别。
- timeout:事务的超时时间。
- readOnly:事务是否只允许只读操作。
- rollbackFor:当出现指定异常时才回滚。
- noRollbackFor:当出现指定异常时不需要回滚。
四、注解实现(@Transactional)
(1)基于Spring整合Mybatis的环境实现(具体环境见文章Spring整合Mybatis)
(2)添加更新Account中的money功能
Mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wsh.mapper.AccountMapper">
<select id="selectAccountList" resultType="Account">
select * from account
</select>
<update id="updateAccount">
update account set money = money + #{arg1} where id = #{arg0}
</update>
</mapper>
DAO接口
@Mapper
public interface AccountMapper {
public List<Account> selectAccountList();
public void updateAccount(Long id, Integer money);
}
Service层接口及实现类
public interface AccountService {
public List<Account> selectAccountList();
public void updateAccountTest();
}
@Service
public class AccountServiceImpl implements AccountService{
@Autowired
private AccountMapper accountMapper;
public List<Account> selectAccountList() {
return accountMapper.selectAccountList();
}
@Transactional
public void updateAccountTest() {
accountMapper.updateAccount(1L, -100);
int i = 10 / 0;
accountMapper.updateAccount(2L, 100);
}
}
(3)JAVA程序
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
AccountService accountService = context.getBean("accountServiceImpl", AccountServiceImpl.class);
accountService.updateAccountTest();
}
执行前的表数据
1 张三 100
2 李四 100
输出
java.lang.ArithmeticException: / by zero
at com.wsh.service.AccountServiceImpl.updateAccountTest(AccountServiceImpl.java:31)
at com.wsh.service.AccountServiceImpl$$FastClassBySpringCGLIB$$2d6e7a30.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at com.wsh.service.AccountServiceImpl$$EnhancerBySpringCGLIB$$df80911d.updateAccountTest(<generated>)
at MyTest.test(MyTest.java:23)
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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
执行后的表数据
1 张三 100
2 李四 100
四、配置文件实现
(1)添加Maven依赖(AOP)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
(2)编写配置文件
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
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
http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:myProperty.properties"></context:property-placeholder>
<!--数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${my.driver}"></property>
<property name="url" value="${my.url}"></property>
<property name="username" value="${my.username}"></property>
<property name="password" value="${my.password}"></property>
</bean>
<!--配置Spring的事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"></property>
</bean>
<!--创建SqlSessionFactory对象-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
<!--Spring扫描Dao层下带有注解的接口并为其创建代理对象-->
<mybatis:scan base-package="com.wsh.mapper"></mybatis:scan>
<!--开启Spring的组件扫描-->
<context:component-scan base-package="com.wsh"></context:component-scan>
<!--配置通知-->
<tx:advice id="transactionAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<!--对哪个方法启用事务,可以在该标签配置事务的属性-->
<tx:method name="updateAccountTest"></tx:method>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="myPointcut" expression="execution(* com.wsh.service.AccountServiceImpl.*(..))"/>
<aop:advisor advice-ref="transactionAdvice" pointcut-ref="myPointcut"></aop:advisor>
</aop:config>
</beans>
(3)JAVA程序
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
AccountService accountService = context.getBean("accountServiceImpl", AccountServiceImpl.class);
accountService.updateAccountTest();
}
注:不需要在方法上用@Transactional注解开始事务
执行前的表数据
1 张三 100
2 李四 100
输出
java.lang.ArithmeticException: / by zero
at com.wsh.service.AccountServiceImpl.updateAccountTest(AccountServiceImpl.java:30)
at com.wsh.service.AccountServiceImpl$$FastClassBySpringCGLIB$$2d6e7a30.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at com.wsh.service.AccountServiceImpl$$EnhancerBySpringCGLIB$$44b03522.updateAccountTest(<generated>)
at MyTest.test(MyTest.java:23)
执行后的表数据
1 张三 100
2 李四 100