从零到一实现Spring事务

从零到一实现Spring事务

  • 目标:
  • 实现:
    • 一、前期准备
      • 1、加入依赖
      • 2、定义spring.xml
      • 3、自定义注解类
      • 4、定义Dao操作数据库类
      • 5、定义服务层接口
      • 6、定义服务层的实现类
      • 7、定义测试类
      • 8、运行结果
    • 二、解决问题
      • 1、定义编程事务类
      • 2、定义切片类
      • 3、测试
  • 总结
    • 步骤
    • 流程图
    • 重点及易错点
      • 1、异常情况处理
      • 2、小结

目标:

  自定义ChenTransaction注解,实现方法级别事务管理。正常则事务提交,异常则事务回滚。

实现:

  思路:使用注解判断方法是否开启了事务,如果开启了事务则使用spring aop,在方法执行前开启事务,方法正常执行完毕提交事务。如果出现异常,则回滚事务。

一、前期准备

  模拟正常的业务代码,演示未实现事务会出现的问题。

1、加入依赖

  本项目spring事务基于Spring Aop技术实现,所以需要引入spring AOP相关jar包。事务需要关联数据库,同时引入mysql驱动包进行连接。源码如下:

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>handwritingprojectartifactId>
        <groupId>com.njustgroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>springTransactionartifactId>

    <dependencies>
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>3.0.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>3.0.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-aopartifactId>
            <version>3.0.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-ormartifactId>
            <version>3.0.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjrtartifactId>
            <version>1.6.1version>
        dependency>
        <dependency>
            <groupId>aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.5.3version>
        dependency>
        <dependency>
            <groupId>cglibgroupId>
            <artifactId>cglibartifactId>
            <version>2.1_2version>
        dependency>

        
        <dependency>
            <groupId>com.mchangegroupId>
            <artifactId>c3p0artifactId>
            <version>0.9.5.2version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.37version>
        dependency>
    dependencies>
project>

2、定义spring.xml

  在spring.xml中,主要配置自动扫描包的范围、开启自动切片代理、数据库连接池、引入JdbcTemplate来对数据库进行操作、配置事务等信息。源码如下:

spring.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	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
         http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/aop
         http://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.njust">context:component-scan>
	
	<aop:aspectj-autoproxy>aop:aspectj-autoproxy> 

	
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver">property>
		<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_learning">property>
		<property name="user" value="root">property>
		<property name="password" value="root">property>
	bean>

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

	
	<bean id="dataSourceTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource">property>
	bean>
	
	
beans>


3、自定义注解类

  新建ChenTransaction类,定义注解所修饰的对象范围为ElementType.METHOD,即用于描述方法。同时定义注解不仅被保存到class文件中,而且jvm加载class文件之后,仍然存在,使得spring通过java反射机制可以获取到该类。源码如下:

ChenTransaction.java

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 事务注解 设置传播行为
//@Target说明了Annotation所修饰的对象范围 METHOD:用于描述方法
@Target({ ElementType.METHOD })
//RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Retention(RetentionPolicy.RUNTIME)
public @interface ChenTransaction {

}

4、定义Dao操作数据库类

  根据测试先行原则,我们先完成业务代码结构,定义UserDao类,主要实现向数据库插入一条数据的功能。源码如下:

UserDao.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;

	public void add(String name, Integer age) {
		String sql = "INSERT INTO t_users(NAME, age) VALUES(?,?);";
		int updateResult = jdbcTemplate.update(sql, name, age);
		System.out.println("updateResult:" + updateResult);
	}

}

5、定义服务层接口

  定义UserService 接口,主要有add()del()两个简单的方法。源码如下:

UserService .java

//user 服务层
public interface UserService {

	public void add();

	public void del();
}

6、定义服务层的实现类

  定义UserServiceImpl实现UserService接口,通过spring注入UserDao,实现user用户添加操作。其中在add()方法上加入自定义注解@ChenTransaction实现事务管理。代码中用int i = 1 / 0;模拟业务操作过程中出现的异常,以测试自定义事务管理是否能够正常工作。

UserServiceImpl.java


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.njust.dao.UserDao;
import com.njust.service.LogService;
import com.njust.service.UserService;

//user 服务层
@Service
public class UserServiceImpl implements UserService {
	@Autowired
	private UserDao userDao;
	@Autowired
	private LogService logService;

	@ChenTransaction
    public void add() {
        userDao.add("test001", 20);
        int i = 1 / 0;
        System.out.println("################");
        userDao.add("test002", 21);
    }

	// 方法执行完毕之后,才会提交事务

	public void del() {
		System.out.println("del");
	}

}

7、定义测试类

  定义Test001 类,通过spring容器获取UserService 对象,对其进行add()添加操作,测试整个业务逻辑是否正常。

Test001 .java

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.njust.service.UserService;

public class Test001 {

	public static void main(String[] args) {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
		UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
		userService.add();
	}

}

8、运行结果

  运行测试类,由于代码中有int i = 1 / 0;,所以会出现异常,下面的控制台输出也验证了这一点。按照事务原子性的原则,当方法出现异常时,test001不会插入数据库,但是查看数据库我们发现,里面有条test001的数据,没有实现回滚,这和我们的预期不符。接下来我们将解决这个问题。

Console

三月 31, 2020 7:34:10 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6267c3bb: startup date [Tue Mar 31 19:34:10 CST 2020]; root of context hierarchy
三月 31, 2020 7:34:11 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring.xml]
三月 31, 2020 7:34:11 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@337d0578: defining beans [aopChenTransaction,logDao,userDao,logServiceImpl,userServiceImpl,transactionUtils,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.aop.config.internalAutoProxyCreator,dataSource,jdbcTemplate,dataSourceTransactionManager]; root of factory hierarchy
三月 31, 2020 7:34:11 下午 com.mchange.v2.log.MLog 
信息: MLog clients using java 1.4+ standard logging.
三月 31, 2020 7:34:12 下午 com.mchange.v2.c3p0.C3P0Registry 
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
三月 31, 2020 7:34:13 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1b60ft0a9df5oit1lx41ct|2f490758, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1b60ft0a9df5oit1lx41ct|2f490758, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/spring_learning, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
Exception in thread "main" java.lang.NullPointerException
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:816)
	at com.njust.transaction.TransactionUtils.rollback(TransactionUtils.java:37)
	at com.njust.aop.AopChenTransaction.afterThrowing(AopChenTransaction.java:31)
	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:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:603)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:59)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
	at com.sun.proxy.$Proxy17.add(Unknown Source)
	at com.njust.Test001.main(Test001.java:12)
updateResult:1


Process finished with exit code 1

数据库信息
在这里插入图片描述

二、解决问题

  使用注解判断方法是否开启了事务,如果开启了事务则使用spring aop,在方法执行前开启事务,方法正常执行完毕提交事务。如果出现异常,则回滚事务。

1、定义编程事务类

  定义TransactionUtils编程事务类,实现事务开启、事务提交、事务回滚的功能。注意TransactionUtils要设置成多例,防止出现线程安全问题。

TransactionUtils.java

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

// 编程事务(需要手动begin 手动回滚 手都提交)
@Component
@Scope("prototype") // 每个事务都是新的实例 目的解决线程安全问题 多例子
public class TransactionUtils {

	// 全局接受事务状态
	private TransactionStatus transactionStatus;
	// 获取事务源
	@Autowired
	private DataSourceTransactionManager dataSourceTransactionManager;

	// 开启事务
	public TransactionStatus begin() {
		System.out.println("开启事务");
		transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
		return transactionStatus;
	}

	// 提交事务
	public void commit(TransactionStatus transaction) {
		System.out.println("提交事务");
		dataSourceTransactionManager.commit(transaction);
	}

	// 回滚事务
	public void rollback() {
		System.out.println("回滚事务...");
		dataSourceTransactionManager.rollback(transactionStatus);
	}

}


2、定义切片类

  定义AopChenTransaction类,对service包中所有方法定义环绕通知和异常通知。在环绕通知中,首先判断该方法是否加上ChenTransaction 注解,如果有则开启事务,否则直接跳过事务流程。然后执行目标代理对象方法。最后如果开启了事务则提交事务,否则直接返回。当目标代理对象方法出现异常时,则调用afterThrowing进行事务回滚。

AopChenTransaction.java


import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import com.njust.annotation.ChenTransaction;
import com.njust.transaction.TransactionUtils;

//  自定义事务注解具体实现
@Aspect
@Component
public class AopChenTransaction {
	// 一个事务实例子 针对一个事务
	@Autowired
	private TransactionUtils transactionUtils;

	// 使用异常通知进行 回滚事务
	@AfterThrowing("execution(* com.njust.service.*.*.*(..))")
	public void afterThrowing() {
		// 获取当前事务进行回滚
		// TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		transactionUtils.rollback();
	}

	// 环绕通知 在方法之前和之后处理事情
	@Around("execution(* com.njust.service.*.*.*(..))")
	public void around(ProceedingJoinPoint pjp) throws Throwable {

		// 1.获取该方法上是否加上注解
		ChenTransaction ChenTransaction = getMethodChenTransaction(pjp);
		TransactionStatus transactionStatus = begin(ChenTransaction);
		// 2.调用目标代理对象方法
		pjp.proceed();
		// 3.判断该方法上是否就上注解
		commit(transactionStatus);
	}

	private TransactionStatus begin(ChenTransaction ChenTransaction) {
		if (ChenTransaction == null) {
			return null;
		}
		// 2.如果存在事务注解,开启事务
		return transactionUtils.begin();
	}

	private void commit(TransactionStatus transactionStatus) {
		if (transactionStatus != null) {
			// 5.如果存在注解,提交事务
			transactionUtils.commit(transactionStatus);
		}

	}

	// 获取方法上是否存在事务注解
	private ChenTransaction getMethodChenTransaction(ProceedingJoinPoint pjp)
			throws NoSuchMethodException, SecurityException {
		String methodName = pjp.getSignature().getName();
		// 获取目标对象
		Class<?> classTarget = pjp.getTarget().getClass();
		// 获取目标对象类型
		Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
		// 获取目标对象方法
		Method objMethod = classTarget.getMethod(methodName, par);
		ChenTransaction ChenTransaction = objMethod.getDeclaredAnnotation(ChenTransaction.class);
		return ChenTransaction;
	}

}


3、测试

  再次运行测试代码,我们发现同样出现异常,但是控制台显示了回滚,查看数据库我们发现确实没有插入test001,正确的实现了事务的功能。

Console

三月 31, 2020 8:01:33 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6267c3bb: startup date [Tue Mar 31 20:01:33 CST 2020]; root of context hierarchy
三月 31, 2020 8:01:33 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [spring.xml]
三月 31, 2020 8:01:33 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@337d0578: defining beans [aopChenTransaction,logDao,userDao,logServiceImpl,userServiceImpl,transactionUtils,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.aop.config.internalAutoProxyCreator,dataSource,jdbcTemplate,dataSourceTransactionManager]; root of factory hierarchy
三月 31, 2020 8:01:33 下午 com.mchange.v2.log.MLog 
信息: MLog clients using java 1.4+ standard logging.
三月 31, 2020 8:01:34 下午 com.mchange.v2.c3p0.C3P0Registry 
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
开启事务
三月 31, 2020 8:01:35 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource 
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> 1b60ft0a9dg4vp61yxqyq7|365c30cc, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1b60ft0a9dg4vp61yxqyq7|365c30cc, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/spring_learning, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {user=******, password=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
updateResult:1
回滚事务...
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.njust.service.impl.UserServiceImpl.add(UserServiceImpl.java:77)
	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:309)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
	at com.njust.aop.AopChenTransaction.around(AopChenTransaction.java:42)
	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:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
	at com.sun.proxy.$Proxy17.add(Unknown Source)
	at com.njust.Test001.main(Test001.java:12)

Process finished with exit code 1

数据库信息

总结

步骤

  自定义事务ChenTransaction的主要实现流程如下:

  • 首先我们自定义ChenTransaction注解类,使其修饰method方法,同时保证注解不仅被保存到class文件中,而且jvm加载class文件之后,仍然存在,以便被spring反射出实例对象。
  • 然后我们自定义一些业务逻辑方法,演示没有使用事务会出现的问题。
  • 然后封装编程事务类,实现事务开启、事务提交、事务回滚的功能。
  • 最后使用spring AOP环绕通知和异常通知实现给方法添加事务,正常运行提交事务,出现异常及时回滚。具体步骤分为:
    • 环绕通知:
      • 首先获取该方法上是否加上ChenTransaction注解,并开启事务。
      • 然后调用目标代理对象方法。
      • 最后提交事务。
    • 异常通知:
      • 直接回滚事务。

流程图

Created with Raphaël 2.2.0 开始 测试service方法 Spring AOP拦截 方法是否有 @ChenTransaction 注解? 开启事务 执行目标代理对象方法 目标代理对象方法 运行是否出现异常? 事务是否 为NULL? 回滚事务 结束 事务是否 为NULL? 提交事务 yes no yes no yes no yes no

重点及易错点

1、异常情况处理

  在业务代码中不要捕获异常,否则即使业务代码出现异常,但是没有抛出,框架会认为任务正常结束从而提交事务,而不是回滚事务。

2、小结

  大白话理解一下,事务主要使用Spring AOP在方法前开启事务,方法结束后提交事务,遇到异常则回滚事务。
  第一次写小轮子,有问题欢迎各位读者批评指正。

点个赞再走呗!欢迎留言哦!

你可能感兴趣的:(数据库,spring,java,mysql,aop)