← 返回首页
Spring基础教程(十八)
发表时间:2020-05-17 19:03:09
讲解声明式事务管理

Spring支持两种方式的事务管理:

本节我们介绍Annotation的声明式事务管理。

实例:

项目结构图如下:

接上一节案例,在此基础是演示使用@Transactional注解实现声明式事务管理。

实现步骤:

1.在applicationContext.xml 配置事务管理器和注册事务管理驱动,开启注解式事务管理。

?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 扫描注解类 -->
    <context:component-scan base-package="com"/>
    <!-- 确定 AOP注解生效 -->
    <aop:aspectj-autoproxy/>
    <!-- 读取配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>

        <property name="initialPoolSize" value="${initPoolSize}"></property>
        <property name="maxPoolSize" value="${maxPoolSize}"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 事务管理器,依赖于数据源 -->
    <bean id="txManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 注册事务管理驱动 -->
    <tx:annotation-driven transaction-manager="txManager"/>
</beans>

2.修改BusinessDao,让其中涉及到新增,修改,删除的操作,都默认抛出异常。

package com.dao;

import java.util.List;

public interface BusinessDao<T> {

    public void add(T obj) throws Exception;
    public void query(String identify);
    public void delete(String identify) throws Exception;
    public void update(T obj) throws Exception;
    public List<T> queryAll();
    public void addBatch(List<T> objs) throws Exception;
}

3.修改StudentsDaoImpl,让其中涉及到新增,修改,删除的操作,都抛出异常。

package com.dao.impl;

import com.dao.StudentsDao;
import com.entity.Students;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Repository
public class StudentsDaoImpl implements StudentsDao<Students> {

    @Resource
    private JdbcTemplate jdbcTemplate;

    public void add(Students s) throws Exception {
        System.out.println("新增学生:" + s);
        String sql = "INSERT INTO students VALUES(?, ?, ?, ?)";
        jdbcTemplate.update(sql, s.getSid(), s.getSname(), s.getGender(), s.getMajor());
    }

    public void query(String identify) {
        System.out.println("查询学生编号:" + identify);
        String sql = "SELECT * FROM students WHERE sid = ?";
        RowMapper<Students> rowMapper = new BeanPropertyRowMapper<>(Students.class);
        // 在将数据装入对象时需要调用set方法。
        Students s = jdbcTemplate.queryForObject(sql, rowMapper, identify);
        System.out.println(s);
    }


    public void delete(String identify) throws Exception {
        System.out.println("删除学生编号:" + identify);
        String sql = "DELETE from students WHERE sid = ?";
        jdbcTemplate.update(sql, identify);
    }

    public void update(Students s) throws Exception {
        System.out.println("修改学生:" + s);
        String sql = "UPDATE students SET sname = ?, gender=?,major=? WHERE sid = ?";
        jdbcTemplate.update(sql, s.getSname(), s.getGender(), s.getMajor(), s.getSid());
    }

    @Override
    public List<Students> queryAll() {
        String sql = "SELECT * from students";
        RowMapper<Students> rowMapper = new BeanPropertyRowMapper<>(Students.class);
        List<Students> userList = jdbcTemplate.query(sql, rowMapper);
        return userList;
    }

    @Override
    public void addBatch(List<Students> objs) throws Exception{

        String sql = "INSERT INTO students VALUES(?, ?, ?, ?)";
        List<Object[]> batchArgs = new ArrayList<>();
        for (Students s : objs) {
            batchArgs.add(new Object[]{s.getSid(), s.getSname(), s.getGender(), s.getMajor()});
        }
        jdbcTemplate.batchUpdate(sql, batchArgs);
    }
}

4.新建service包,分别创建StudentsService和实现类StudentsServiceImpl

注意:@Transactional一般都加在service层而不加在dao层,因为一个Service完成一个服务,但是可能会调用很多个DAO层的功能,如果Transaction放在DAO层的话,做完一个DAO,就会提交一次事务,永久修改数据库,后面在调用另外一个DAO,但是throws Exception,对于整个的Service来说,应该是要完全回滚的,但是只能回滚到当前的DAO。

package com.service;

import com.entity.Students;
import java.util.List;

public interface StudentsService {

    public void add(Students s) throws Exception;
    public void query(String identify);
    public void delete(String identify) throws Exception;
    public void update(Students s) throws Exception;
    public List<Students> queryAll();
    public void addBatch(List<Students> stus) throws Exception;
}

在StudentsServiceImpl ,涉及到添加、修改、删除的方法上都添加@Transactional(rollbackFor = Exception.class)注解。

package com.service.impl;

import com.dao.StudentsDao;
import com.entity.Students;
import com.service.StudentsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.List;

@Service
public class StudentsServiceImpl implements StudentsService {

    @Resource
    private StudentsDao studentsDaoImpl;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(Students s) throws Exception {
        studentsDaoImpl.add(s);
    }

    @Override
    public void query(String identify) {
       studentsDaoImpl.query(identify);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(String identify) throws Exception {
      studentsDaoImpl.delete(identify);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(Students s) throws Exception {
       studentsDaoImpl.update(s);
    }

    @Override
    public List<Students> queryAll() {
        return studentsDaoImpl.queryAll();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addBatch(List<Students> stus) throws Exception {

        studentsDaoImpl.addBatch(stus);

    }
}

5.创建StudentsServiceTest测试类,使用Junit单元测试。

package com.service;

import com.dao.StudentsDao;
import com.entity.Students;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class StudentsServiceTest {

    @Resource
    private ApplicationContext context;

    @Test
    public void testStudentsDao(){
        try {
            JdbcTemplate jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
            String sql = "CREATE TABLE `students`  (\n" +
                    "  `sid` varchar(8) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,\n" +
                    "  `sname` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,\n" +
                    "  `gender` varchar(2) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,\n" +
                    "  `major` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,\n" +
                    "  PRIMARY KEY (`sid`) USING BTREE\n" +
                    ") ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;";
            jdbcTemplate.execute(sql);

            StudentsService studentsServiceImpl = (StudentsService) context.getBean("studentsServiceImpl");
            List<Students> studentsList = new ArrayList<Students>();
            studentsList.add(new Students("S002", "郭靖", "男", "九阴真经"));
            studentsList.add(new Students("S003", "黄蓉", "女", "打狗棒法"));
            //性别字段内容超出字段存储范围,执行失败,会抛出异常。  
            studentsList.add(new Students("S004", "段誉", "性别男性", "六脉神剑"));
            studentsServiceImpl.addBatch(studentsList);
            List<Students> students = studentsServiceImpl.queryAll();
            students.forEach(System.out::println);
        }catch(Exception ex){
            ex.printStackTrace();
        }

    }
}

这里我们通过执行一个批处理来测试事务能否回滚,在添加第三条记录,性别字段内容超出范围,因此事务应该回滚,但是执行单元测试结果如下:

环绕内的前置通知
权限的校验...
环绕内的异常抛出通知
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO students VALUES(?, ?, ?, ?)]; Data truncation: Data too long for column 'gender' at row 1; nested exception is java.sql.BatchUpdateException: Data truncation: Data too long for column 'gender' at row 1
    at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:104)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:647)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:936)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:990)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:981)
    at com.dao.impl.StudentsDaoImpl.addBatch(StudentsDaoImpl.java:63)
    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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
    at com.aspect.BusinessAspect.around(BusinessAspect.java:44)
    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.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy39.addBatch(Unknown Source)
    at com.service.impl.StudentsServiceImpl.addBatch(StudentsServiceImpl.java:49)
    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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy41.addBatch(Unknown Source)
    at com.service.StudentsServiceTest.testStudentsDao(StudentsServiceTest.java:41)
    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:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.sql.BatchUpdateException: Data truncation: Data too long for column 'gender' at row 1
    at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1819)
    at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1281)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:2544)
    at org.springframework.jdbc.core.JdbcTemplate.lambda$batchUpdate$2(JdbcTemplate.java:950)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617)
    ... 78 more
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'gender' at row 1
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3833)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3771)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2535)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1911)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2145)
    at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1777)
    ... 82 more
环绕内的最终通知
最终通知...
日志记录...
环绕内的前置通知
权限的校验...
环绕内的后置通知
环绕内的最终通知
最终通知...
日志记录...
Students{sid='S002', sname='郭靖', gender='男', major='九阴真经'}
Students{sid='S003', sname='黄蓉', gender='女', major='打狗棒法'}

我们发现尽管抛出了异常,但是前两条记录还是插入成功了,似乎事务并没有回滚。经过仔细分析,我们发现当执行StudentsDaoImpl 的addBatch()方法时,其实是被AOP注入了一个切面,而这个切面里面要执行一个环绕通知,在环绕通知里面自己已经捕获并处理的异常,这样spring的声明式事务就无法捕获到异常,实现回滚了。

如何解决呢?很简单,改写环绕通知方法代码如下:

//环绕通知;
    @Around(value="myPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Exception {
        Object obj=null;
        try {
            System.out.println("环绕内的前置通知");
            obj = joinPoint.proceed();
            System.out.println("环绕内的后置通知");
        } catch (Throwable e) {
            System.out.println("环绕内的异常抛出通知");
            e.printStackTrace();
            //这里一定要抛出新的异常,让spring的事物管理器自动捕获。实现回滚
            throw new Exception("环绕内捕获到异常,抛出异常!");
        } finally {
            System.out.println("环绕内的最终通知");
        }
        return obj;
    }

再次执行测试,结果如下:

环绕内的前置通知
权限的校验...
环绕内的异常抛出通知
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO students VALUES(?, ?, ?, ?)]; Data truncation: Data too long for column 'gender' at row 1; nested exception is java.sql.BatchUpdateException: Data truncation: Data too long for column 'gender' at row 1
    at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:104)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81)
    at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1443)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:633)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:647)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:936)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:990)
    at org.springframework.jdbc.core.JdbcTemplate.batchUpdate(JdbcTemplate.java:981)
    at com.dao.impl.StudentsDaoImpl.addBatch(StudentsDaoImpl.java:63)
    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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:88)
    at com.aspect.BusinessAspect.around(BusinessAspect.java:44)
    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.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy39.addBatch(Unknown Source)
    at com.service.impl.StudentsServiceImpl.addBatch(StudentsServiceImpl.java:49)
    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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy41.addBatch(Unknown Source)
    at com.service.StudentsServiceTest.testStudentsDao(StudentsServiceTest.java:41)
    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:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.sql.BatchUpdateException: Data truncation: Data too long for column 'gender' at row 1
    at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1819)
    at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1281)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:2544)
    at org.springframework.jdbc.core.JdbcTemplate.lambda$batchUpdate$2(JdbcTemplate.java:950)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:617)
    ... 78 more
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'gender' at row 1
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3833)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3771)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2535)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1911)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2145)
    at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1777)
    ... 82 more
环绕内的最终通知
最终通知...
异常抛出通知...环绕内捕获到异常,抛出异常!
java.lang.Exception: 环绕内捕获到异常,抛出异常!
    at com.aspect.BusinessAspect.around(BusinessAspect.java:50)
    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.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644)
    at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633)
    at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy39.addBatch(Unknown Source)
    at com.service.impl.StudentsServiceImpl.addBatch(StudentsServiceImpl.java:49)
    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.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)
    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.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
    at com.sun.proxy.$Proxy41.addBatch(Unknown Source)
    at com.service.StudentsServiceTest.testStudentsDao(StudentsServiceTest.java:41)
    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:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

我们发现三条记录,均未插入成功!说明回滚起作用了。

使用@Transactional注意事项

1.@Transactional只能添加在修饰符为public的方法上方。 2.@Transactional默认只能捕获到RuntimeException的异常。想捕获到所有异常,使用: @Transactional(rollbackFor={Exception.class}) 3.如果已经在service中进行了try catch 操作 由于已经被抓获异常 故事务也不会回滚。除非在catch代码使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 手工回滚。 4.数据库引擎要支持事务,如果是mysql,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的。 5.是否开启了对注解的解析 6.spring是否扫描到带注解的包,如下是扫描到org.test下面的包