itsource

B가 잘못되면 A를 롤백합니다. 스프링 부트, jdbct 템플릿

mycopycode 2023. 6. 11. 10:38
반응형

B가 잘못되면 A를 롤백합니다. 스프링 부트, jdbct 템플릿

저는 'databaseChanges'라는 방법을 가지고 있는데, 이 방법은 A, B 두 가지 연산을 반복적으로 호출합니다.'A'가 먼저, 'B'가 마지막입니다.'A'와 'B'는 내 영구 스토리지인 Oracle Database 11g에서 생성, 업데이트 삭제 기능이 될 수 있습니다.

예를 들어,

'A'는 사용자 테이블의 레코드를 업데이트합니다. 여기서 ID는 = 1입니다.

B는 테이블 취미에 레코드를 삽입합니다.

시나리오: databaseChanges 메서드가 호출되고 'A'가 작동하여 레코드를 업데이트합니다.'B'가 작동하여 레코드를 삽입하려고 하면 어떤 일이 발생하고 예외가 발생하며 예외가 databaseChanges 메서드에 버블링됩니다.

예상: 'A'와 'B'는 아무것도 변경하지 않았습니다.'A'가 한 업데이트는 롤백됩니다.'B'는 아무것도 변하지 않았어요, 음...예외가 있었습니다.

실제: 'A' 업데이트가 롤백되지 않은 것 같습니다.'B'는 아무것도 변하지 않았어요, 음...예외가 있었습니다.


일부 코드

연결이 되면 다음과 같은 작업을 수행할 것입니다.

private void databaseChanges(Connection conn) {
   try {
          conn.setAutoCommit(false);
          A(); //update.
          B(); //insert
          conn.commit();
   } catch (Exception e) { 
        try {
              conn.rollback();
        } catch (Exception ei) {
                    //logs...
        }
   } finally {
          conn.setAutoCommit(true);
   }
}

문제:연결이 없습니다(질문과 함께 게시되는 태그 참조).

노력했습니다.

@Service
public class SomeService implements ISomeService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate npjt;

    @Transactional
    private void databaseChanges() throws Exception {   
        A(); //update.
        B(); //insert
    }
}

내 AppConfig 클래스:

import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;

@Configuration
public class AppConfig {    
    @Autowired
    private DataSource dataSource;

    @Bean
    public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
        return new NamedParameterJdbcTemplate(dataSource);
    }   
}

'A'가 업데이트합니다.'B'에서 예외가 발생했습니다.'A'에서 업데이트한 내용은 롤백되지 않습니다.

제가 읽은 바로는, 제가 @Transactional을 올바르게 사용하지 않는 것으로 알고 있습니다.저는 여러 블로그 게시물을 읽고 시도해 보았지만 제 문제를 해결하기 위해 성공하지 못했습니다.

좋은 의견이라도 있나?


편집

databaseChanges() 메서드를 호출하는 메서드가 있습니다.

public void changes() throws Exception {
    someLogicBefore();
    databaseChanges();
    someLogicAfter();
}

어떤 방법이 @Transactional로 주석을 달아야 하는지,

변경()?데이터베이스 변경()?

@Transactional로 감싸서 하며, 는 봄의 주석프포작동며하써그, ▁with▁annot▁which▁in▁proxy▁worksation▁methods다▁in▁by▁wrapping▁spring니▁in로 주석을 달았습니다.@Transactional거래에서개인 메서드는 상속될 없기 때문에 개인 메서드에서 주석이 작동하지 않습니다(예에서와 같이). => 이러한 메서드는 래핑될 수 없습니다(aspectj와 선언적 트랜잭션을 사용하는 경우에는 그렇지 않습니다. 그렇다면 아래의 프록시 관련 주의 사항이 적용되지 않습니다.

다음은 방법에 대한 기본적인 설명입니다.@Transactional춘계의 요술

작성한 내용:

class A {
    @Transactional
    public void method() {
    }
}

하지만 이것은 콩을 주사할 때 실제로 얻을 수 있는 것입니다.

class ProxiedA extends A {
   private final A a;

   public ProxiedA(A a) {
       this.a = a;
   }

   @Override
   public void method() {
       try {
           // open transaction ...
           a.method();
           // commit transaction
       } catch (RuntimeException e) {
           // rollback transaction
       } catch (Exception e) {
           // commit transaction
       }
   }
} 

이것은 제한이 있습니다.사할수없다니습과 일하지 .@PostConstruct메서드는 개체가 프록시되기 전에 호출되기 때문입니다.또한 모두 올바르게 구성한 경우에도 트랜잭션은 기본적으로 확인되지 않은 예외에서만 롤백됩니다.사용하다@Transactional(rollbackFor={CustomCheckedException.class})선택한 예외에 대한 롤백이 필요한 경우

자주 발생하는 또 다른 경고는 다음과 같습니다.

@Transactional에서 "외부에서합니다.b()트랜잭션에 포함되지 않습니다.

class X {
   public void a() {
      b();
   }

   @Transactional
   public void b() {
   }
}

또한그은 때문입니다.@Transactional개체를 프록시하는 방식으로 작동합니다.위의 는 다음과 .a()부를 것입니다X.b() 프록시". "스프링 프록시" 메서드가 아닙니다.b()그래서 거래는 없을 것입니다.으로 해결방전야합니다해화로으법다합으로 전화해야 합니다.b()다른 콩에서.

해결 수 없는 경우( method non-private call 다음주사은의메발같항이생제여하해과안된방호사결법을경출수우나)b()콩에서)를.TransactionTemplate선언적 트랜잭션 대신:

public class A {
    @Autowired
    TransactionTemplate transactionTemplate;

    public void method() {
        transactionTemplate.execute(status -> {
            A();
            B();
            return null;
        });
    }

...
} 

갱신하다

위의 정보를 사용하여 OP 업데이트된 질문에 답변합니다.

@Transactional: changes()? databaseChanges()로 주석을 달아야 하는 방법은 무엇입니까?

@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
    someLogicBefore();
    databaseChanges();
    someLogicAfter();
}

ㅠㅠchanges() outside불리는 에서가 아니라 " bone").afterPropertiesSet()또는@PostConstruct주석이 달린 방법).기본적으로 선택되지 않은 예외에 대해서만 스프링 롤백 트랜잭션을 수행합니다(rollbackForched exceptions 목록에서 더 구체적으로 설명).

조금도RuntimeException롤백을 트리거하지만 선택한 예외는 롤백하지 않습니다.

이것은 모든 Spring 트랜잭션 API에서 일반적인 동작입니다.기본적으로 다음과 같은 경우RuntimeException트랜잭션 코드 내에서 던져지면 트랜잭션이 롤백됩니다.된 예외 선된예즉(택, 그않은경우)가 아닌 RuntimeException가 던져지면 트랜잭션이 롤백되지 않습니다.

에는 안에 어떤 .databaseChanges기능.따라서 모든 예외를 파악하려면 다음을 추가하기만 하면 됩니다.rollbackFor = Exception.class

변경 사항은 서비스 클래스에 있어야 합니다. 코드는 다음과 같습니다.

@Service
public class SomeService implements ISomeService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate npjt;

    @Transactional(rollbackFor = Exception.class)
    private void databaseChanges() throws Exception {   
        A(); //update
        B(); //insert
    }
}

게다가 당신은 그것으로 좋은 것을 할 수 있기 때문에 당신이 항상 글을 써야 하는 것은 아닙니다.rollbackFor = Exception.class사용자 정의 주석을 직접 작성하면 다음과 같은 이점을 얻을 수 있습니다.

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor = Exception.class)
@Documented
public @interface CustomTransactional {  
}

최종 코드는 다음과 같습니다.

@Service
public class SomeService implements ISomeService {
    @Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;
    @Autowired
    private NamedParameterJdbcTemplate npjt;

    @CustomTransactional
    private void databaseChanges() throws Exception {   
        A(); //update
        B(); //insert
    }
}

당신이 제시하는 첫 번째 코드는 사용자 트랜잭션을 위한 것입니다. 즉, 애플리케이션이 트랜잭션 관리를 수행해야 합니다.일반적으로 컨테이너에서 이 작업을 처리하고 @Transactional 주석을 사용합니다.제 생각에 당신의 경우 문제는 개인적인 방법으로 주석을 다는 것입니다.주석을 클래스 수준으로 이동합니다.

@Transactional
public class MyFacade {

public void databaseChanges() throws Exception {   
    A(); //update.
    B(); //insert
}

그러면 제대로 롤백해야 합니다.자세한 내용은 Spring@Transactional 속성이 개인 메서드에서 작동합니까?

사용해 보십시오.

@TransactionManagement(TransactionManagementType.BEAN)
public class MyFacade {

@TransactionAttribute(TransactionAttribute.REQUIRES_NEW)
public void databaseChanges() throws Exception {   
    A(); //update.
    B(); //insert
}

있는 것처럼 은 당이놓있것보처것이은는럼는신입니다.TransactionManager의 은?TransactionManager데이터베이스 트랜잭션을 관리할 수 있습니다.트랜잭션에는 프로그래밍 방식과 선언 방식의 두 가지 유형이 있습니다.당신이 설명하고 있는 것은 주석을 통한 선언적 트랜잭션의 필요성입니다.

따라서 프로젝트에 필요한 것은 다음과 같습니다.

스프링 트랜잭션 종속성(예: Gradle 사용)

compile("org.springframework:spring-tx")

스프링 부팅 구성에서 트랜잭션 관리자 정의

이런 거.

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource)
{
    return new DataSourceTransactionManager(dataSource);
}

또다을추야합다니해가음을 해야 할 입니다.@EnableTransactionManagement주석(이것이 새로운 버전의 스프링 부트에서 무료인지 확실하지 않음).

@EnableTransactionManagement
public class AppConfig {
...
}

@Transactional 추가

에 여에다추니다합가을음을 할 수 .@Transactional에 하고자 하는 .

@Transactional
public void book(String... persons) {
    for (String person : persons) {
        log.info("Booking " + person + " in a seat...");
        jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
    }
};

방법은 공개적이어야 하며 비공개적이어서는 안 됩니다.당신은 다음을 고려하는 것이 좋을 것입니다.@Transactional공중전화로.databaseChanges().

다음 위치에 대한 고급 항목도 있습니다.@Transactional가봐야 하고 어떻게 행동해야 하는지, 그래서 먼저 무언가가 작동하고 조금 후에 이 분야를 탐색하는 것이 더 낫습니다.

이 모든 것이 준비되면(의존성 + transactionManager 구성 + 주석) 트랜잭션이 적절하게 작동해야 합니다.

레퍼런스

거래에 관한 봄 참조 문서

Spring Boot을 이용한 거래를 위한 Spring Guide - 이것은 당신이 가지고 놀 수 있는 샘플 코드가 있습니다.

언급URL : https://stackoverflow.com/questions/39096860/roll-back-a-if-b-goes-wrong-spring-boot-jdbctemplate

반응형