개요
Spring Boot 는 프로덕션용 애플리케이션 개발을 간소화하는 오픈 소스 Java 프레임워크 입니다. Spring Data MongoDB 와 결합된 이 프레임워크 MongoDB 데이터베이스와 효율적으로 상호 작용 수 있는 방법을 제공합니다.
이 튜토리얼에서는 Spring Data MongoDB 사용하여 MongoDB 데이터베이스 에서 작업을 수행하는 반응형 Spring Boot 애플리케이션 빌드 방법을 보여줍니다. 이 샘플 현금 잔액 애플리케이션 사용하여 다음 작업을 수행하는 REST API를 호출할 수 있습니다.
계정 만들기 또는 가져오기
한 계정에서 또는 두 계정 간에 트랜잭션 수행
Spring Data MongoDB
Spring Data MongoDB MongoDB 에 대한 리포지토리 지원 제공하여 데이터베이스 와 상호 작용 하는 두 가지 프라이머리 방법을 제공합니다.
ReactiveMongoRepository: 일반적인 데이터베이스 작업에 대한 액세스 제공하는 상위 수준 추상화입니다.
ReactiveMongoTemplate: MongoDB 작업 및 쿼리 구성에 대한 더 많은 제어를 제공하는 하위 수준 추상화입니다.
다중 문서 ACID 트랜잭션
MongoDB 다중 문서 ACID 트랜잭션을 지원하므로 원자성, 일관성, 격리 및 내구성 보장 충족하는 여러 작업을 수행할 수 있습니다. Spring Data MongoDB @Transactional 주석 또는 TransactionalOperator 클래스를 통해 트랜잭션에 대한 내장 지원 제공합니다. 이 튜토리얼의 샘플 애플리케이션 데이터베이스 작업의 원자성을 보장하기 위해 트랜잭션 지원 구성합니다.
MongoDB 트랜잭션에 대해 자세히 학습하려면 MongoDB Server 매뉴얼의 트랜잭션 을 참조하세요.
튜토리얼
이 튜토리얼에서는 간소화된 현금 잔액 시스템을 구현하는 예시 애플리케이션 다운로드 방법을 보여줍니다. 이 애플리케이션 사용하면 은행 계좌 잔액을 생성, 조회 및 업데이트 할 수 있습니다. 전체 애플리케이션 보려면 mdb-spring-boot-reactive GitHub 리포지토리 참조하세요.
튜토리얼을 완료하여 다음 조치를 수행하는 방법을 학습 .
예시 Spring Boot 애플리케이션 다운로드
MongoDB 에 연결하도록 애플리케이션 구성
애플리케이션 구조 및 주요 구성 요소 이해
애플리케이션을 실행합니다.
은행 계좌를 생성하고 트랜잭션을 수행하기 위해 POST 요청을 보냅니다.
연결 다운로드 및 구성
전제 조건 확인
이 튜토리얼을 시작하기 전에 다음 구성 요소가 준비되어 있는지 확인하십시오.
클러스터 가 구성된 MongoDB Atlas 계정. 클러스터 만드는 방법을 학습 보려면 MongoDB 시작하기 가이드 참조하세요.
Java 21 이상.
Maven 3.5 이상.
애플리케이션 구조 이해
계정 관리 엔드포인트 검토
AccountController.java 파일 계정을 만들고 가져오기 위한 REST API 엔드포인트가 포함되어 있습니다. 이 파일 txn-demo.accounts 컬렉션 에 문서 삽입하는 POST 메서드 엔드포인트와 accountNum 값을 기반으로 문서 찾는 GET 메서드 엔드포인트를 정의합니다. 다음 코드는 이러한 메서드를 보여줍니다.
public class AccountController { //... public Mono<Account> createAccount( Account account) { return accountRepository.save(account); } public Mono<Account> getAccount( String accountNum) { return accountRepository.findByAccountNum(accountNum) .switchIfEmpty(Mono.error(new AccountNotFoundException())); } //... }
각 엔드포인트는 기본 Java Reactive Streams 드라이버 에서 추상화 역할을 하는 ReactiveMongoRepository 인터페이스인 AccountRepository에서 Mono<Account> 유형을 반환합니다.
잔액 트랜잭션 엔드포인트 검토
AccountController 클래스에는 다음 트랜잭션 엔드포인트도 포함되어 있습니다.
계정 잔액에 추가되는
.../debit엔드포인트계정 잔액에서 차감하는
.../credit엔드포인트한 계정에서 다른 계정으로 전송을 수행하는
.../transfer엔드포인트
이러한 엔드포인트에는 다음과 같은 정의가 있습니다.
public class AccountController { //... public Mono<Txn> debitAccount( String accountNum, Map<String, Object> requestBody) { //... txn.addEntry(new TxnEntry(accountNum, amount)); return txnService.saveTransaction(txn).flatMap(txnService::executeTxn); } public Mono<Txn> creditAccount( String accountNum, Map<String, Object> requestBody) { //... txn.addEntry(new TxnEntry(accountNum, -amount)); return txnService.saveTransaction(txn).flatMap(txnService::executeTxn); } public Mono<Txn> transfer( String from, TransferRequest transferRequest) { //... txn.addEntry(new TxnEntry(from, -amount)); txn.addEntry(new TxnEntry(to, amount)); return txnService.saveTransaction(txn).flatMap(txnService::executeTxn); } //... }
하나의 TxnEntry 객체 단일 계정에 대한 변경 사항을 나타내며, Txn 는 하나 또는 여러 개의 TxnEntry 객체로 구성될 수 있습니다. 샘플 코드에서 차변 및 크레딧 엔드포인트는 하나의 새 TxnEntry 객체 생성하고 전송 엔드포인트는 두 개의 TxnEntry 객체를 생성합니다.
사용자 지정 쿼리 메서드 검토
AccountRepository 인터페이스는 ReactiveMongoRepository 을 확장하고 다음 쿼리 메서드를 정의합니다.
public interface AccountRepository extends ReactiveMongoRepository<Account, String> { Mono<Account> findByAccountNum(String accountNum); Mono<Long> findAndIncrementBalanceByAccountNum(String accountNum, double increment); }
이 코드는 MongoDB 쿼리 때 메서드 인수로 동적으로 대체되는 자리 표시자를 사용할 수 있도록 하는 다음 주석을 사용합니다.
@Query:findByAccountNum()메서드에 주석을 달고 문서를 찾기 위한 쿼리 기준을 지정합니다.?0자리 표시자는accountNum매개 변수 값으로 대체됩니다.@Update:findAndIncrementBalanceByAccountNum()메서드에 주석을 달고 일치하는 문서에 수행할 업데이트 작업을 지정합니다.?1자리 표시자가increment매개 변수 값으로 대체되어 MongoDB의$inc연산자 사용하여 잔액을 늘릴 수 있습니다.
참고
이전 단계에서 설명한 POST 엔드포인트는 accountRepository.save() 메서드를 사용하지만 이 메서드를 정의할 필요는 없습니다. save() 메서드 및 기타 많은 기본 메서드는 ReactiveMongoRepository의 상속 체인에 있는 인터페이스에 의해 이미 선언되어 있습니다.
트랜잭션 서비스 이해
TxnService 클래스는 트랜잭션 실행을 처리합니다. 이 클래스에는 다음 코드가 포함되어 있습니다.
public class TxnService { //... public Mono<Txn> saveTransaction(Txn txn) { return txnTemplate.save(txn); } public Mono<Txn> executeTxn(Txn txn) { return updateBalances(txn) .onErrorResume(DataIntegrityViolationException.class /*lambda expression to handle error*/) .onErrorResume(AccountNotFoundException.class /*lambda expression to handle error*/) .then(txnTemplate.findAndUpdateStatusById(txn.getId(), TxnStatus.SUCCESS)); } public Flux<Long> updateBalances(Txn txn) { Flux<Long> updatedCounts = Flux.fromIterable(txn.getEntries()).concatMap( entry -> accountRepository.findAndIncrementBalanceByAccountNum( entry.getAccountNum(), entry.getAmount()) ); return updatedCounts.handle(/*...*/); } }
TxnService 클래스에는 다음 메서드가 포함되어 있습니다.
saveTransaction():Txn문서transactions컬렉션 에 저장합니다.executeTxn():updateBalances()메서드를 호출한 다음Txn문서 에서 트랜잭션 상태를 업데이트합니다.updateBalances(): 각TxnEntry를 반복하고 각account문서 에 해당하는 업데이트를 수행합니다.
트랜잭션 템플릿 검토
이전 단계에서 설명한 saveTransaction() 및 executeTxn() 메서드는 모두 TxnTemplate 클래스에 정의된 메서드를 사용합니다. 이러한 메서드에는 다음과 같은 정의가 있습니다.
public class TxnTemplate { //... public Mono<Txn> save(Txn txn) { return template.save(txn); } public Mono<Txn> findAndUpdateStatusById(String id, TxnStatus status) { Query query = query(where("_id").is(id)); Update update = update("status", status); FindAndModifyOptions options = FindAndModifyOptions.options().returnNew(true); return template.findAndModify(query, update, options, Txn.class); } //... }
이러한 메서드는 ReactiveMongoTemplate를 사용하여 MongoDB 와 상호 작용 .
다중 문서 트랜잭션 구성
계정 간에 돈을 이체할 때 다중 문서 트랜잭션 사용할 수 있습니다. 두 계정에 대한 업데이트는 원자적이어야 하며 트랜잭션은 데이터 원자성을 보장합니다.
Spring의 트랜잭션 지원 액세스 위해 샘플 애플리케이션 ReactiveMongoConfig.java 파일 에 ReactiveMongoTransactionManager 빈을 추가합니다.
public class ReactiveMongoConfig extends AbstractReactiveMongoConfiguration { //... ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory dbFactory) { return new ReactiveMongoTransactionManager(dbFactory); } }
TransactionalOperator 객체 또는 @Transactional 주석을 사용하여 트랜잭션 의 범위를 정의할 수 있습니다. TxnService 클래스는 두 가지 접근 방식을 모두 보여줍니다.
애플리케이션 테스트
계정 간 송금
한 계정에서 다른 계정으로 돈을 이체하려면 계정 번호가 67890인 새 계정을 만듭니다.
curl --location 'localhost:8080/account' \ --header 'Content-Type: application/json' \ --data '{ "accountNum": "67890" }'
그런 다음 다음 명령을 실행 하여 500 달러를 이 새 계정으로 이체합니다.
curl --location 'localhost:8080/account/12345/transfer' \ --header 'Content-Type: application/json' \ --data '{ "to": "67890", "amount": 500 }'
이 명령은 잔액 이체를 완료하기 위해 .../transfer 엔드포인트에 요청 보냅니다.
추가 리소스
Spring Data MongoDB 에 대해 자세히 학습하려면 Spring Data MongoDB 참고 문서를 참조하세요.
전체 예시 애플리케이션 보려면 mdb-spring-boot-reactive GitHub 리포지토리 참조하세요.