젠장, 왜 내 비즈니스가 적용되지 않습니까?

머리말

누구나 평상시에 일에 대해 써야 합니다. 예전에 일을 쓰다가 몇 가지 함정에 빠졌는데 효과가 없었습니다. 나중에는 다양한 일에 실패하는 시나리오를 확인하고 검토했습니다. 두려울 수 없습니다. 따라서 먼저 트랜잭션에 대한 지식을 복습합시다. 트랜잭션은 작업의 가장 작은 작업 단위를 의미합니다. 하나의 분할할 수 없는 단위 작업으로서 모두 성공하거나 모두 실패합니다. 트랜잭션에는 네 가지 특성( ACID)이 있습니다.

  • 원자성( Atomicity): 트랜잭션에 포함된 작업은 모두 성공하거나 모두 실패하고 롤백되며 절반 성공과 절반 실패의 중간 상태가 없습니다. 예를 들어 A, B처음 에 500위안 이 있으면 돈 AB이체 하면 돈 이 적으면 더 많은 돈 가져야 합니다 . 돈을 잃을 수 없고 돈을 받지 못하면 돈이 됩니다 . 사라지고 원자성에 맞지 않습니다.100A100B100AB
  • 일관성( Consistency): 일관성이란 트랜잭션이 실행되기 전후에 전체 상태의 일관성을 유지하는 것을 말합니다. 예를 들어 , 처음에 위안 이 A있고 합계가 위안입니다. 이것이 이전 상태입니다. 이체 경우 다음 last is , yes , two 합산하더라도 이 전체 상태는 보장되어야 합니다.B5001000AB100A400B6001000
  • 격리( Isolation): 처음 두 기능은 동일한 트랜잭션에 대한 반면 격리는 다른 트랜잭션을 나타냅니다. 여러 트랜잭션이 동일한 데이터에 대해 동시에 작동하는 경우 서로 다른 트랜잭션과 동시성 간의 영향을 격리해야 합니다. 실행된 트랜잭션은 서로 간섭합니다.
  • 지속성( Durability): 트랜잭션이 커밋되면 데이터베이스에 대한 수정 사항이 영구적으로 유지되며 데이터베이스가 실패하더라도 발생한 수정 사항이 존재해야 합니다.

트랜잭션의 몇 가지 특성은 데이터베이스 트랜잭션에만 국한되지 않습니다. 넓은 의미에서 트랜잭션은 작동 메커니즘이자 동시성 제어의 기본 단위입니다. 작업 결과를 보장하고 분산 트랜잭션 등도 포함하지만 일반적으로 우리는 구체적으로 언급하지 않으면 데이터베이스와 관련된 것인데, 우리가 일반적으로 이야기하는 트랜잭션은 기본적으로 데이터베이스를 기반으로 완료되기 때문입니다.

트랜잭션은 데이터베이스만을 위한 것이 아닙니다. 이 개념을 대기열 서비스나 외부 시스템 상태와 같은 다른 구성 요소로 확장할 수 있습니다. 따라서 "일련의 데이터 조작 명령문은 시스템을 일관된 상태로 유지하기 위해 완료되거나 완전히 실패해야 합니다."

테스트 환경

우리는 이미 몇 가지 데모 프로젝트를 배포했고 docker를 사용하여 환경을 빠르게 구축했습니다.이 기사도 이전 환경을 기반으로 합니다.

  • JDK 1.8
  • 메이븐 3.6
  • 도커
  • MySQL

정상적인 트랜잭션 롤백의 예

일반적인 트랜잭션 예제에는 두 개의 인터페이스가 포함되어 있는데, 하나는 모든 사용자의 데이터를 가져오는 것이고 다른 하나는 update사용자 데이터를 업데이트하는 것인데, 이는 실제로 각 사용자의 나이입니다 +1. 예외, 최종 결과를 보십시오.

@Service("userService")
public class UserServiceImpl implements UserService {

    @Resource
    UserMapper userMapper;

    @Autowired
    RedisUtil redisUtil;

    @Override
    public List<User> getAllUsers() {
        List<User> users = userMapper.getAllUsers();
        return users;
    }

    @Override
    @Transactional
    public void updateUserAge() {
        userMapper.updateUserAge(1);
        int i= 1/0;
        userMapper.updateUserAge(2);
    }
}

데이터베이스 작업:

<?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.aphysia.springdocker.mapper.UserMapper">
    <select id="getAllUsers" resultType="com.aphysia.springdocker.model.User">
        SELECT * FROM user
    </select>

    <update id="updateUserAge" parameterType="java.lang.Integer">
        update user set age=age+1 where id =#{id}
    </update>
</mapper>

먼저 http://localhost:8081/getUserList모든 사용자를 확보하고 다음을 확인하십시오.

이미지-20211124233731699

업데이트 인터페이스를 호출할 때 페이지에서 오류가 발생합니다.

이미지-20211124233938596

콘솔은 또한 0으로 나누기를 의미하는 예외를 throw합니다. 예외:

java.lang.ArithmeticException: / by zero
	at com.aphysia.springdocker.service.impl.UserServiceImpl.updateUserAge(UserServiceImpl.java:35) ~[classes/:na]
	at com.aphysia.springdocker.service.impl.UserServiceImpl$$FastClassBySpringCGLIB$$c8cc4526.invoke(<generated>) ~[classes/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.12.jar:5.3.12]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.12.jar:5.3.12]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.12.jar:5.3.12]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.12.jar:5.3.12]
	at com.aphysia.springdocker.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$25070cf0.updateUserAge(<generated>) ~[classes/:na]

그런 다음 다시 요청 http://localhost:8081/getUserList하고 두 데이터 모두 데이터 11가 변경되지 않았음을 나타냅니다. 첫 번째 작업이 완료된 후 예외가 발생하고 롤백이 성공합니다.

[{"id":1,"name":"李四","age":11},{"id":2,"name":"王五","age":11}]

트랜잭션이 비정상적으로 롤백되는 경우는 언제입니까? 그리고 자세히 들어주세요.

실험

1. 잘못된 엔진 설정

지원되는 데이터 엔진 을 보는 데 사용할Mysql 수 있는 데이터베이스 엔진의 개념이 실제로 있다는 것을 알고 있습니다.show enginesMysql

이미지-20211124234913121

Transactions해당 컬럼, 즉 트랜잭션 지원만 InnoDB, 즉 트랜잭션만 지원하는 열 을 볼 수 InnoDB있으므로 엔진이 다른 트랜잭션으로 설정되어 있으면 무효가 됩니다.

기본 데이터베이스 엔진을 사용 show variables like 'default_storage_engine'하여 기본값이 InnoDB다음 과 같은지 확인할 수 있습니다.

mysql> show variables like 'default_storage_engine';
+------------------------+--------+
| Variable_name          | Value  |
+------------------------+--------+
| default_storage_engine | InnoDB |
+------------------------+--------+

그런 다음 우리가 시연한 데이터 테이블도 사용되는지 확인하고 InnoDB실제로 사용되는 것을 볼 수 있습니다.InnoDB

이미지-20211124235353205

테이블의 엔진을 이와 MyISAM같이 변경하면 어떻게 될까요? 여기서 우리는 데이터 테이블의 데이터 엔진만 수정합니다.

mysql> ALTER TABLE user ENGINE=MyISAM;
Query OK, 2 rows affected (0.06 sec)
Records: 2  Duplicates: 0  Warnings: 0

그런 다음 다시 update놀랍지 않게도 여전히 오류가 발생합니다. 이는 다르지 않은 것 같습니다.

이미지-20211125000554928

그러나 모든 데이터가 확보되면 첫 번째 데이터 업데이트는 성공하고 두 번째 데이터는 성공적으로 업데이트되지 않아 트랜잭션이 적용되지 않음을 나타냅니다.

[{"id":1,"name":"李四","age":12},{"id":2,"name":"王五","age":11}]

결론: InnoDB트랜잭션을 적용하려면 엔진으로 설정해야 합니다.

2. 메서드는 비공개일 수 없습니다.

트랜잭션은 반드시 메소드여야 하며, public메소드에서 사용 private하면 자동으로 트랜잭션이 실패하지만 , 에서 IDEA작성하는 한 오류가 보고됩니다. Methods annotated with '@Transactional' must be overrideable즉, 트랜잭션의 주석에 의해 추가된 메소드 방법은 다시 private쓸 수 없으므로 오류가 보고됩니다.

이미지-20211125083648166

동일한 수정된 메서드는 주석이 추가된 경우 다시 쓰기를 원하지 final않기 때문에 오류도 보고합니다 .final

이미지-20211126084347611

Spring주로 Bean방사선으로 얻은 주석 정보를 사용하고 동적 프록시 기술 을 사용하여 전체 트랜잭션을 캡슐화합니다.이론적으로 는 메서드를 호출하는 데 문제가 없다고 AOP생각 합니다. 메서드 수준에서 사용할 수 있지만 팀 에서 방법은 개발자의 의지로 생각 하고 쉽게 혼동을 일으킬 수 있는 노출을 꺼리는 인터페이스의 캡슐화를 파괴할 필요가 없습니다.privatemethod.setAccessible(true);Springprivate

Protected방법이 가능한가요? 캔트!

다음으로 달성하기 위해 인터페이스를 사용할 수 없기 때문에 코드 구조를 마술처럼 변경합니다 Portected.인터페이스를 사용하면 protected메서드를 사용할 수 없으며 오류가 직접보고되며 다음에서 사용해야합니다. 같은 패키지. controller합계 를 service같은 패키지에 넣습니다.

이미지-20211125090358299

테스트 후 트랜잭션이 적용되지 않았으며 결과는 여전히 하나의 업데이트이고 다른 하나는 업데이트되지 않은 것으로 나타났습니다.

[{"id":1,"name":"李四","age":12},{"id":2,"name":"王五","age":11}]

결론: , , 메소드가 public아닌 메소드 에서 사용해야 합니다 . 그렇지 않으면 적용되지 않습니다.privatefinalstatic

3. 예외는 런타임 예외여야 합니다.

Springboot예외를 관리할 때 런타임 예외( RuntimeException및 해당 하위 클래스)만 롤백됩니다.예를 들어 이전 i=1/0;에 작성한 것처럼 런타임 예외가 생성됩니다.

또한 rollbackOn(ex)메소드가 예외인지 여부를 판단 RuntimeException하는 소스 코드를 볼 수 있습니다 Error.

	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

예외는 주로 다음 유형으로 나뉩니다.

모든 예외는 Throwable, Error그러나 오류 메시지입니다. 일반적으로 프로그램에서 이러한 파일이 없음, 메모리 오버플로, IO갑작스러운 오류와 같이 제어할 수 없는 오류가 발생합니다. 반면 Exception, 를 제외한 RuntimeException나머지 CheckException는 모두 처리할 수 있는 예외이며 Java프로그램은 이 예외가 작성될 때 이 예외를 처리해야 합니다. 그렇지 않으면 컴파일이 통과되지 않습니다.

아래 그림에서 볼 수 있습니다. 몇 가지 일반적인 IO 예외를 CheckedException나열했습니다 . 이 메서드를 찾지 못했습니다. 이 클래스를 찾지 못했지만 몇 가지 일반적인 예외 가 있습니다.IOExceptionNoSuchMethodExceptionClassNotFoundExceptionRunTimeException

  • 범위를 벗어난 배열 예외:IndexOutOfBoundsException
  • 유형 변환 예외:ClassCastException
  • 널 포인터 예외:NullPointerException

트랜잭션의 기본 롤백은 런타임 예외입니다. 즉 RunTimeException, 다음 코드와 같이 다른 예외가 발생하면 롤백할 수 없으며 트랜잭션은 실패합니다.

    @Transactional
     public void updateUserAge() throws Exception{
        userMapper.updateUserAge(1);
        try{
            int i = 1/0;
        }catch (Exception ex){
            throw new IOException("IO异常");
        }
        userMapper.updateUserAge(2);
    }

4. 잘못된 구성으로 인해 발생

  1. @Transactional트랜잭션을 시작하려면 메서드를 사용해야 합니다.
  2. A다중 데이터 소스 또는 다중 트랜잭션 관리자를 구성할 때 데이터베이스가 운영되면 사용할 수 없는 트랜잭션에 주의 하십시오 B.이 문제는 매우 순진하지만 잘못된 사용으로 문제를 찾기 어려운 경우가 있습니다.
  3. 에 있는 경우 구성 파일 과 동일하지만 더 이상 필요하지 않은 트랜잭션을 열 Spring도록 구성해야 하는 경우 주석에 주석이 포함되어 있으면 자동으로 삽입됩니다.@EnableTransactionManagementxml*<tx:annotation-driven/>*SpringbootspringbootSpringBootApplication@EnableAutoConfiguration

@EnableAutoConfiguration자동으로 주입되는 것은 무엇입니까? jetbrains://idea/navigate/reference?project=springDocker&path=~/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.6/spring-boot-autoconfigure-2.5.6.jar!/META-INF/spring.factories다음 아래

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
...
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
...

TransactionAutoConfiguration트랜잭션 자동 구성 클래스인 하나가 구성되어 있습니다 .

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
		DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
  ...
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnBean(TransactionManager.class)
	@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
	public static class EnableTransactionManagementConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableTransactionManagement(proxyTargetClass = false)   // 这里开启了事务
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
		public static class JdkDynamicAutoProxyConfiguration {

		}
    ...

	}

}

메서드에 사용 되는 @Transactional것 외에도 클래스에도 사용할 수 있으므로 public이 클래스의 모든 메서드가 트랜잭션을 구성함을 나타냅니다.

5. 트랜잭션 메소드는 동일한 클래스에서 호출할 수 없습니다.

트랜잭션 관리를 수행하고자 하는 메소드는 현재 클래스가 아닌 다른 클래스에서만 호출할 수 있으며, 그렇지 않으면 무효가 된다.이러한 목적을 달성하기 위해 동일한 클래스에 많은 트랜잭션 메소드 및 기타 메소드가 있는 경우 이번에는 후임자가 동일한 클래스에 트랜잭션 메소드를 작성할 때 계층화가 더 명확하고 혼동을 피하기 위해 트랜잭션 클래스를 추출하는 것이 필요합니다.

트랜잭션 실패의 예:

예를 들어 service트랜잭션 방법을 다음 과 같이 변경합니다.

    public void testTransaction(){
        updateUserAge();
    }

    @Transactional
     public void updateUserAge(){
        userMapper.updateUserAge(1);
        int i = 1/0;
        userMapper.updateUserAge(2);
    }

controller안에서 트랜잭션 어노테이션이 없는 메소드가 호출되고, 그 다음 트랜잭션 메소드가 간접적으로 호출됩니다.

    @RequestMapping("/update")
    @ResponseBody
    public int update() throws Exception{
        userService.testTransaction();
        return 1;
    }

호출 후 트랜잭션이 유효하지 않은 것으로 확인되었습니다. 하나는 업데이트되고 다른 하나는 업데이트되지 않습니다.

[{"id":1,"name":"李四","age":12},{"id":2,"name":"王五","age":11}]

왜 그런가요?

Spring메소드는 aspect로 감싸고 외부 호출 메소드만 가로채고 내부 메소드는 가로채지 않는다.

소스 코드를 보십시오: 사실, 우리가 transaction 메소드를 호출할 때, 우리가 DynamicAdvisedInterceptor입력 할 메소드는 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)()다음과 같습니다:

이미지-20211128125711187

내부 AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice()에서 호출되며 다음은 호출을 받기 위한 호출 체인입니다. @Transactional주석 이 없는 메서드 userService.testTransaction()의 경우 프록시 호출 체인을 전혀 얻을 수 없으며 원래 클래스의 메서드가 계속 호출됩니다.

spring메서드를 프록시 aop하려면 식별자를 사용하여 프록시해야 할 메서드 또는 클래스를 식별해야 합니다. 이는 포인트컷 spring으로 정의 @Transactional되며 이 식별자를 정의하면 프록시됩니다.

에이전트가 되는 시기는 언제인가요?

Spring우리는 통합 관리 bean를 가지고 있으며 프록시의 타이밍은 자연스럽게 bean생성 과정입니다.이 로고가 있는 클래스를 확인하기 위해 프록시 개체가 생성됩니다.

SpringTransactionAnnotationParser이 클래스에는 TransactionAttribute주석을 결정하는 데 사용되는 메서드가 있습니다.

	@Override
	@Nullable
	public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
		AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
				element, Transactional.class, false, false);
		if (attributes != null) {
			return parseTransactionAnnotation(attributes);
		}
		else {
			return null;
		}
  }

6. 멀티스레딩에서 트랜잭션 실패

멀티 스레딩에서 다음과 같이 트랜잭션을 사용한다고 가정하면 트랜잭션을 정상적으로 롤백할 수 없습니다.

    @Transactional
    public void updateUserAge() {
        new Thread(
                new Runnable() {
                    @Override
                    public void run() {
                        userMapper.updateUserAge(1);
                    }
                }
        ).start();
        int i = 1 / 0;
        userMapper.updateUserAge(2);
    }

다른 스레드는 SqlSession다른 연결과 동일한 다른 것을 사용하기 때문에 동일한 트랜잭션은 전혀 사용되지 않습니다.

2021-11-28 14:06:59.852 DEBUG 52764 --- [       Thread-2] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2021-11-28 14:06:59.930 DEBUG 52764 --- [       Thread-2] c.a.s.mapper.UserMapper.updateUserAge    : <==    Updates: 1
2021-11-28 14:06:59.931 DEBUG 52764 --- [       Thread-2] org.mybatis.spring.SqlSessionUtils       : Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2e956409]

7. 트랜잭션 중첩의 합리적인 사용에 주의

우선 트랜잭션에 대한 전파 메커니즘이 있습니다.

  • REQUIRED(default): 현재 트랜잭션 사용 지원, 현재 트랜잭션이 존재하지 않는 경우 새 트랜잭션을 생성하고, 존재하는 경우 현재 트랜잭션을 직접 사용합니다.

  • SUPPORTS: 현재 트랜잭션의 사용을 지원하며, 현재 트랜잭션이 존재하지 않으면 트랜잭션을 사용하지 않습니다.

  • MANDATORY: 현재 트랜잭션의 사용을 지원합니다. 현재 트랜잭션이 존재하지 않으면 throw됩니다. 즉, 현재 트랜잭션 Exception에 있어야 합니다.

  • REQUIRES_NEW: 새 트랜잭션을 생성하고 현재 트랜잭션이 존재하는 경우 현재 트랜잭션을 일시 중단합니다.

  • NOT_SUPPORTED: 트랜잭션이 실행되지 않으며, 현재 트랜잭션이 존재하는 경우 현재 트랜잭션을 일시 중단합니다.

  • NEVER: 트랜잭션이 실행되지 않고 현재 트랜잭션이 있으면 throw됩니다 Exception.

  • NESTED: 중첩된 트랜잭션, 현재 트랜잭션이 존재하는 경우 중첩된 트랜잭션에서 실행됩니다. 현재 트랜잭션이 존재하지 않는 경우 동작은 `REQUIRED

확인할 사항이 많지 않습니다.

기본값은 REQUIRED트랜잭션에서 다른 트랜잭션을 호출하면 실제로 트랜잭션을 다시 생성하지 않고 현재 트랜잭션을 재사용한다는 것입니다. 그런 다음 다음과 같이 중첩된 트랜잭션을 작성하면:

@Service("userService")
public class UserServiceImpl {
    @Autowired
    UserServiceImpl2 userServiceImpl2;
  
    @Resource
    UserMapper userMapper;
  
  	@Transactional
    public void updateUserAge() {
        try {
            userMapper.updateUserAge(1);
            userServiceImpl2.updateUserAge();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

다음과 같은 다른 트랜잭션이 호출되었습니다.

@Service("userService2")
public class UserServiceImpl2 {

    @Resource
    UserMapper userMapper;

    @Transactional
    public void updateUserAge() {
        userMapper.updateUserAge(2);
        int i = 1 / 0;
    }
}

다음 오류가 발생합니다.

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

그러나 실제 트랜잭션은 정상적으로 롤백되었고 결과는 정확했다 이 문제의 원인은 내부 메소드에서 예외가 발생했고 동일한 트랜잭션이 사용되어 트랜잭션을 롤백해야 함을 나타내지만, 외부 레이어가 차단 catch되어 동일한 트랜잭션입니다. 하나는 롤백하라고 하고 다른 하나는 인식 catch되지 않도록 차단합니다. 모순 아닌가요? 따라서 오류는 다음과 같습니다. 이 트랜잭션은 표시되고 롤백되어야 하며 결국 롤백됩니다 .springExceptionspring

그것을 처리하는 방법?

    1. 외부 레이어는 적극적으로 오류를 발생시킵니다.throw new RuntimeException()
    1. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();활성 ID 로 롤백
    @Transactional
    public void updateUserAge() {
        try {
            userMapper.updateUserAge(1);
            userServiceImpl2.updateUserAge();
        }catch (Exception ex){
            ex.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

8. 롤백을 위해 외부 네트워크 요청에 의존하는 것을 고려해야 합니다.

때때로 우리는 우리 자신의 데이터베이스를 운영할 뿐만 아니라 데이터 동기화, 동기화 실패와 같은 외부 요청을 고려해야 하고 우리 자신의 상태를 롤백해야 할 필요가 있습니다.이 시나리오에서 우리는 네트워크 요청이 잘못될지 여부와 오류를 처리하는 방법. , 성공해야 하는 오류 코드입니다.

네트워크 시간이 초과되면 실제로는 성공하지만 실패로 판단하고 롤백하여 데이터 불일치가 발생할 수 있습니다. 이를 위해서는 수신자가 재시도를 지원해야 합니다.재시도 시 멱등성을 지원해야 하며 저장된 상태의 일관성이 여러 번 호출됩니다.전체 주요 프로세스는 매우 간단하지만 여전히 많은 세부 사항이 있습니다.

이미지-20211128153822791

요약하다

트랜잭션은 Spring복잡하게 싸여 있고 많은 것들이 깊은 소스 코드를 가질 수 있습니다.이를 사용할 때 호출이 정상적으로 롤백될 수 있는지 확인하기 위해 시뮬레이션 테스트에주의해야합니다.당연히 받아 들일 수 없습니다.사람은 실수를 할 수 있습니다. , 그리고 블랙박스 테스트는 이런 예외를 테스트하는 경우가 많다.데이터가 정상적으로 롤백되지 않으면 나중에 수동으로 처리해야 한다.시스템 간 동기화 문제를 고려할 때 불필요한 문제가 많이 발생한다. 데이터베이스를 수동으로 변경하는 프로세스를 수행해야 합니다.

이미지-20211128154248397

【작가의 간략한 소개】 :
공개 계정 [ Qin Huai Grocery Store ]의 저자 Qin Huai, 기술의 길은 한 번에 있지 않고 산은 높고 강은 길고 느리더라도 끝이 없습니다. . 개인 쓰기 방향: Java源码解析, JDBC, Mybatis, Spring, redis, 分布式, 등, 모든 기사를 신중하게 작성 剑指Offer, LeetCode헤드라인 파티를 좋아하지 않음, 종소리와 휘파람을 좋아하지 않음, 주로 일련의 기사를 작성합니다. 내가 쓰는 것을 보장할 수 없습니다. 완전히 정확하지만, 내가 작성한 모든 내용은 연습되었거나 정보를 검색한 것임을 보증합니다. 누락이나 오류가 있으면 저를 수정하십시오.

Sword Point는 모든 문제 솔루션 PDF를 제공합니다.

2020년에 나는 무엇을 썼을까?

오픈 소스 프로그래밍 노트

{{o.name}}
{{m.name}}

Supongo que te gusta

Origin my.oschina.net/u/5077784/blog/5381599
Recomendado
Clasificación