거래 지원 가 최근 Couchbase SDK에 추가되었습니다. 시작 Spring 데이터 카우치베이스 5.0.0-M5에 트랜잭션 지원이 추가되었습니다. Spring 데이터 카우치베이스. 이 블로그에서는 Spring 데이터 사용법에 대해 설명합니다. 트랜잭션 어노테이션을 사용하여 카우치베이스 트랜잭션을 활용합니다.
그리고 스프링 데이터 카우치베이스 리포지토리에는 스프링 데이터 테스트 탭 트랜잭션을 실행합니다. 이는 여행 샘플 버킷을 생성합니다. (기본 인덱스가 있는 모든 버킷이면 충분합니다).
|
1 2 |
git clone git@github.com:spring-projects/spring-data-examples.git cd spring-data-examples/couchbase/transactions |
트랜잭션 관리를 지원하는 빈이 다음에 추가되었습니다. 추상 카우치베이스 구성. 내장 트랜잭션 인터셉터 클래스를 CouchbaseTransactionInterceptor로 재정의할 수 있도록 하려면 다음 줄이 application.properties file:
application.properties
|
1 |
spring.main.allow-bean-definition-overriding=true |
트랜잭션 구성은 재정의하여 수정할 수 있습니다. 구성환경() 메서드를 확장하는 클래스에서 추상 카우치베이스 구성. 여기서는 내구성 수준 에 없음 를 사용하여 단일 노드 클러스터에서 작업할 수 있습니다.
Config.java
|
1 2 3 4 5 6 7 8 9 |
@Configuration @EnableCouchbaseRepositories({"com.example.demo"}) @EnableTransactionManagement public class Config extends AbstractCouchbaseConfiguration { … @Override public void configureEnvironment(ClusterEnvironment.Builder builder){ builder.transactionsConfig(TransactionsConfig.durabilityLevel(DurabilityLevel.NONE)); } |
이를 사용할 클래스에서 평소와 같이 템플릿을 정의하고 서비스 객체에 대한 참조를 정의해야 합니다. 중요한 점은 오토와이어드 어노테이션은 프록시 객체로 변수를 채웁니다. 프록시 객체가 실제 메서드를 호출하기 전후에 처리를 수행하므로 이는 중요한 세부 사항입니다. 생성자에서 서비스 개체를 직접 생성하면 @Transactional 어노테이션이 적용되지 않습니다.
CmdRunner.java
|
1 2 |
@Autowired CouchbaseTemplate template; @Autowired AirportGatesService airportGatesService; |
이 서비스는 서비스 어노테이션을 사용합니다. 생성자는 다음과 같은 인수를 받습니다. @Bean 객체를 생성하여 인프라가 다음과 같은 프록시 객체를 생성할 수 있도록 합니다. 오토와이어드.
AirlineGateService.java
|
1 2 3 4 5 6 |
@Service public class AirlineGatesService { CouchbaseTemplate template; public AirlineGatesService(CouchbaseTemplate template) { this.template = template; } |
AirlineGateService.java (계속)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// The @Transactional annotation results in the method of the proxy for the service executing this in a transaction @Transactional public void transferGates(String fromId, String toId, int gatesToTransfer, RuntimeException exceptionToThrow) { // Not necessary, but may wish to include this check to confirm this is actually in a transaction. TransactionalSupport.checkForTransactionInThreadLocalStorage().map((h) -> { if (!h.isPresent()) throw new RuntimeException("not in transaction!"); return h; }); AirlineGates fromAirlineGates = template.findById(AirlineGates.class).one(fromId); AirlineGates toAirlineGates = template.findById(AirlineGates.class).one(toId); toAirlineGates.gates += gatesToTransfer; fromAirlineGates.gates -= gatesToTransfer; template.save(fromAirlineGates); // maybe simulate an error occurring after the fromAirlineGates doc has been saved if(exceptionToThrow != null){ throw exceptionToThrow; } template.save(toAirlineGates); } |
이제 우리가 구축한 모든 것을 사용하세요:
- 두 개 저장 AirlineGates 문서, 각 문서는 200개의 게이트를 나타냅니다.
- 저장되었는지 확인합니다.
- 를 실행하고 transferGates() 거래에서 한 항공사에서 다른 항공사로 50개의 게이트를 전송하는 서비스 방식입니다.
- 전송이 완료되었는지 확인합니다.
- 실행 시도 transferGates() 다시 한 번, 이번에는 첫 번째 문서가 저장된 후에 예외가 발생합니다.
CmdRunner.java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
AirlineGates airlineGates1 = new AirlineGates("1", "JFK", "American Airlines", Long.valueOf(200)); //1 AirlineGates airlineGates2 = new AirlineGates("2", "JFK", "Lufthansa", Long.valueOf(200)); AirlineGates saved1 = airlineGatesService.save(airlineGates1); AirlineGates saved2 = airlineGatesService.save(airlineGates2); AirlineGates found1 = airlineGatesService.findById(saved1.getId()); //2 AirlineGates found2 = airlineGatesService.findById(saved2.getId()); System.err.println("found before transferGates: " + found1); System.err.println("found before transferGates: " + found2); // move 50 gates from airline1 to airline2 int gatesToTransfer=50; airlineGatesService.transferGates(airlineGates1.getId(), airlineGates2.getId(), gatesToTransfer, null); //3 found1 = airlineGatesService.findById(saved1.getId()); found2 = airlineGatesService.findById(saved2.getId()); System.err.println("found after transferGates: " + found1); //4 System.err.println("found after transferGates: " + found2); Assert.isTrue(found1.getGates().equals(airlineGates1.getGates()-gatesToTransfer), "should have transferred"); Assert.isTrue(found2.getGates().equals(airlineGates1.getGates()+gatesToTransfer), "should have transferred"); // attempt to move 44 gates from airline1 to airline2, but it fails. try { // 5 airlineGatesService.transferGates(airlineGates1.getId(), airlineGates2.getId(), 44, new SimulateErrorException()); } catch (RuntimeException rte) { if (!(rte instanceof TransactionSystemUnambiguousException) && rte != null && rte.getCause() instanceof SimulateErrorException) { throw rte; } } System.err.println("found after transferGates: " + airlineGatesService.findById(airlineGates1.getId())); System.err.println("found after transferGates: " + airlineGatesService.findById(airlineGates2.getId())); Assert.isTrue(found1.getGates().equals(airlineGates1.getGates()-gatesToTransfer), "should be same as previous"); Assert.isTrue(found2.getGates().equals(airlineGates1.getGates()+gatesToTransfer), "should be same as previous"); |
작동 방식:
- 그리고 트랜잭션 인터셉터 빈인 추상 카우치베이스 구성 는 내장된 트랜잭션 인터셉터 트랜잭션 주석이 달린 메서드에 대해.
- 트랜잭션 주석을 사용하면 다음과 같은 프록시가 발생합니다. 공항 게이트 서비스 를 사용하여 트랜잭션 인터셉터 를 사용하여 주석이 달린 메서드를 호출하려면 카우치베이스 콜백 트랜잭션 관리자.
- 그리고 카우치베이스 콜백 트랜잭션 관리자 는 트랜잭션을 초기화한 다음 트랜잭션의 컨텍스트에서 실제 서비스 메서드를 호출합니다.
- 메서드 호출이 성공하면 카우치베이스 콜백 트랜잭션 관리자 는 트랜잭션을 커밋합니다.
- 커밋이 성공하면 호출이 반환됩니다.
- 재시도 가능한 예외로 커밋이 실패하면 카우치베이스 콜백 트랜잭션 관리자 는 새 트랜잭션을 초기화하고 실제 서비스 메서드를 실행한 다음 트랜잭션을 커밋하는 전체 프로세스를 다시 시도합니다.
그리고 카우치베이스 콜백 트랜잭션 관리자 는 커밋이 성공하거나 최대 재시도 횟수에 도달할 때까지 이 작업을 반복합니다.
다음 단계를 위한 옵션
- 카우치베이스 트랜잭션은 다음에서 활용할 수 있습니다. Spring 데이터 카우치베이스 를 사용하여 트랜잭션 오퍼레이터 - 를 참조하십시오. Spring 데이터 문서는 여기.
- 트랜잭션은 다음을 수행할 수 있습니다. 는 Couchbase Java SDK에서 직접 사용할 수도 있습니다. Java ACID 트랜잭션 문서는 여기.
안녕하세요 여러분 - 카우치베이스/거래 프로젝트는 아직 스프링 데이터 예제에 병합되지 않았습니다. 곧 제공될 예정입니다.
- Mike
카우치베이스/트랜잭션 프로젝트는 이제 boot-3 브랜치에서 사용할 수 있습니다.
https://github.com/spring-projects/spring-data-examples/tree/boot-3/couchbase/transactions
트랜잭션 샘플을 실행하려면 다른 3.0.0* 버전의 리액터와 호환되지 않기 때문에 스프링-부팅-스타터-데이터-카우치베이스 3.0.0-RC2가 필요합니다.
안녕하세요 마이클, 기본 제공 카우치베이스 트랜잭션 지원을 구현해 주셔서 감사합니다. 단일 버킷을 사용하여 "카우치베이스 트랜잭션"을 사용할 때는 매우 잘 작동하지만 여러 버킷을 사용하는 트랜잭션에서 오류가 발생합니다.
이 링크를 구현했습니다. https://github.com/spring-projects/spring-data-couchbase/issues/878. 이 구현은 트랜잭션에서 작동하지 않습니다. 혹시 알고 계신가요?
스프링 데이터 카우치베이스의 멀티버킷을 사용하려면 카우치베이스에 여러 개의 연결이 필요하고 트랜잭션이 단일 연결에 있어야 하므로 스프링 데이터 카우치베이스 트랜잭션은 멀티버킷에서 작동하지 않습니다.
- Mike