답변:
거래는 서비스 계층에 속한다고 생각합니다. 작업 단위 및 사용 사례에 대해 알고있는 사람입니다. 단일 트랜잭션에서 함께 작동해야하는 여러 DAO를 서비스에 주입 한 경우 정답입니다.
일반적으로 거래는 일반적으로 서비스 수준에서 시작된다는 진술에 동의합니다 (물론 필요한 세분성에 따라 다름).
그러나 @Transactional(propagation = Propagation.MANDATORY)
그 동안 발신자에서 거래를 시작하지 않은 오류를 훨씬 쉽게 감지 할 수 있기 때문에 DAO 레이어 (및 거래를 시작할 수 없지만 기존 레이어가 필요한 다른 레이어)에 추가하기 시작했습니다 ( 예를 들어 서비스). DAO에 필수 전파로 주석이 달린 경우 메소드가 호출 될 때 활성 트랜잭션이 없다는 예외가 발생합니다.
또한이 주석에 대한 모든 bean (bean post processor) @Transactional
을 확인하고 서비스 계층에 속하지 않는 bean에서 Mandatory 이외의 전파 가있는 주석 이 있으면 실패하는 통합 테스트가 있습니다. 이렇게하면 잘못된 계층에서 트랜잭션을 시작하지 않습니다.
@Transactional
서비스 구현 클래스를 입어야한다고 말하고 @Transactional(propagation = MANDATORY)
DAO (리포지토리) 클래스 구현을 입어야한다고 말하고 있습니까?
트랜잭션 주석은 분리 할 수없는 모든 작업 주위에 배치해야합니다.
예를 들어 전화는 "비밀번호 변경"입니다. 그것은 두 가지 작업으로 구성됩니다
따라서 위의 감사가 실패하면 암호 변경도 실패해야합니까? 그렇다면 트랜잭션은 약 1과 2 여야합니다 (서비스 계층에서도). 전자 메일이 실패하는 경우 (아마도 이것에 대해 안전 장치가 있어야 실패하지 않을 것입니다) 변경 암호와 감사를 롤백해야합니까?
이것들은 어디에 넣을지를 결정할 때 물어봐야 할 질문들 @Transactional
입니다.
전통적인 Spring 아키텍처에 대한 정답은 다른 사람들이 이미 설명한 이유로 서비스 클래스에 트랜잭션 의미 체계를 배치하는 것입니다.
Spring에서 떠오르는 트렌드는 도메인 기반 디자인 (DDD)에 대한 것입니다. Spring Roo 는 트렌드를 잘 보여줍니다. 아이디어는 도메인 객체 POJO 를 일반적인 Spring 아키텍처보다 훨씬 더 풍부하게 만들고 (일반적으로 빈혈이 없음 ), 특히 도메인 객체 자체에 트랜잭션 및 지속성 의미를 두는 것입니다. 필요한 모든 것이 간단한 CRUD 작업 인 경우 웹 컨트롤러는 도메인 개체 POJO에서 직접 작동하며 (이 컨텍스트에서 엔터티로 작동하고 있음) 서비스 계층이 없습니다. 도메인 객체간에 조정이 필요한 경우 다음과 같은 서비스 Bean 핸들을 가질 수 있습니다.@Transaction
전통에 따라. 도메인 오브젝트에서 트랜잭션 전파를 설정하여 도메인 오브젝트가 REQUIRED
서비스 Bean에서 시작된 트랜잭션과 같은 기존 트랜잭션을 사용하도록 할 수 있습니다.
기술적으로이 기술은 AspectJ 및를 사용 <context:spring-configured />
합니다. Roo는 AspectJ 인터 타입 정의를 사용하여 엔티티 시맨틱 (트랜잭션 및 지속성)을 도메인 객체 (기본적으로 필드 및 비즈니스 메소드)와 분리합니다.
일반적인 경우는 서비스 계층 수준에 주석을 달지만 실제로는 요구 사항에 따라 다릅니다.
서비스 계층에 주석을 달면 DAO 수준에서 주석을 달 때보 다 트랜잭션이 길어집니다. 예를 들어 동시 트랜잭션은 서로의 변경 사항을 볼 수 없으므로 트랜잭션 격리 수준에 따라 문제를 해결할 수 있습니다. 반복 가능한 읽기.
DAO에 주석을 달면 서비스 계층이 제공하는 기능이 단일 (롤백 가능) 트랜잭션으로 수행되지 않는다는 단점이 있으므로 트랜잭션을 최대한 짧게 유지합니다.
전파 모드가 기본값으로 설정된 경우 두 레이어에 주석을다는 것은 의미가 없습니다.
레이어 @Transactional
에 배치하고 예외를 @Service
설정 하고 트랜잭션을 추가로 최적화합니다.rollbackFor
readOnly
기본적으로 (확인되지 않은 예외) @Transactional
만 찾고 RuntimeException
롤백을 Exception.class
(확인 된 예외) 로 설정 하면 예외가 롤백됩니다.
@Transactional(readOnly = false, rollbackFor = Exception.class)
확인 된 예외와 확인되지 않은 예외를 참조하십시오 .
아니면 두 "레이어"에 주석을 추가하는 것이 합리적입니까? -DAO에서 전파가 "필수"인 서비스 계층에서 DAO 방법이 항상 호출 (전파)되도록하려면 서비스 계층과 dao 계층 모두에 주석을 추가하는 것이 이치에 맞지 않습니다. 이것은 DAO 메소드가 UI 계층 (또는 컨트롤러)에서 호출되는 것을 제한합니다. 또한-특히 DAO 계층을 단위 테스트 할 때 DAO에 주석을 달면 트랜잭션 기능에 대해 테스트됩니다.
propagation=Propagation.REQUIRES_NEW
. 그렇지 않으면 propogation = mandatory를 포함한 대부분의 경우 DAO는 서비스 계층에서 시작한 기존 트랜잭션에 참여합니다.
또한 Spring은 구체적인 클래스에서만 주석을 사용하고 인터페이스는 사용하지 않는 것이 좋습니다.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
데이터베이스 레벨의 트랜잭션
주로 @Transactional
DAO에서 메소드 수준에서만 사용 했으므로 구성은 메소드 / 특히 기본값을 사용하여 (필수) 구성 할 수 있습니다
데이터 가져 오기 (select ..)를 얻는 DAO의 방법- @Transactional
이것을 필요로하지 않아도
트랜잭션 인터셉터 / 및 AOP 프록시로 인해 오버 헤드가 발생할 수 있습니다.
삽입 / 업데이트를 수행하는 DAO의 메소드는 @Transactional
transactional 에 대한 아주 좋은 블로그
응용 프로그램 수준-
비즈니스 논리에 트랜잭션을 사용하고 있습니다. 예기치 않은 오류가 발생하면 롤백하고 싶습니다.
@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){
try {
//service logic here
} catch(Throwable e) {
log.error(e)
throw new MyApplicationException(..);
}
}
Transactional
에서Java
일반적으로 서비스 계층에서 거래를해야합니다.
그러나 앞에서 언급했듯이 작업의 원자 성은 주석이 필요한 위치를 알려줍니다. 따라서, 최대 절전 모드와 같은 프레임 워크를 사용하는 경우 개체에 대한 단일 "저장 / 업데이트 / 삭제 / ... 수정"작업이 여러 테이블의 여러 행을 수정할 수있는 가능성이 있습니다 (개체 그래프를 통한 계단식으로 인해). 물론이 특정 DAO 방법에 대한 트랜잭션 관리도 있어야합니다.
@Transactional
분리 할 수없는 모든 작업에 주석을 달아야합니다. @Transactional
트랜잭션 전파 사용 은 자동으로 처리됩니다.이 경우 현재 메소드에서 다른 메소드를 호출하면 해당 메소드에 진행중인 트랜잭션에 참여할 수있는 옵션이 있습니다.
예를 들어 보자.
우리는이 모델의 예를 가지고 Country
와 City
. 관계형 매핑 Country
및 City
모델은 Country
여러 도시를 가질 수 있으므로 매핑은
@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;
여기서 국가는 여러 도시를 가져 와서 매핑했습니다 Lazily
. 그래서 여기의 역할 온다 @Transactinal
우리가 다음 우리는 국가 개체의 모든 데이터를 얻을 것이다 데이터베이스에서 국가 개체를 검색하지만 우리는 도시를 가져 오는 있기 때문에 도시의 설정을받지 않습니다 때를 LAZILY
.
//Without @Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//After getting Country Object connection between countryRepository and database is Closed
}
국가 개체에서 도시 집합에 액세스하려는 경우이 집합 만 생성 된 집합의 개체가 집합의 값을 가져 오는 데이터로 초기화되지 않기 때문에 집합에서 null 값을 얻습니다 @Transactional
.
//with @Transactional
@Transactional
public Country getCountry(){
Country country = countryRepository.getCountry();
//below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
Object object = country.getCities().size();
}
따라서 기본적 @Transactional
으로 서비스는 엔드 포인트와의 연결을 닫지 않고도 단일 트랜잭션에서 여러 호출을 할 수 있습니다.
@Transactional
실제로 무엇이 있는지에 대한 설명
이 @Transactional
는 비즈니스 로직을 포함로 서비스 계층에 사용되어야한다. DAO 계층에는 일반적으로 데이터베이스 CRUD 작업 만 있습니다.
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
Foo getFoo(String fooName);
Foo getFoo(String fooName, String barName);
void insertFoo(Foo foo);
void updateFoo(Foo foo);
}
스프링 문서 : https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html
서비스 계층은 @Transactional
여기에 존재하는 대부분의 비즈니스 논리와 같이 주석 을 추가하는 가장 좋은 장소 이며 세부 수준의 사용 사례 동작을 포함합니다.
서비스를 DAO에 추가하고 서비스에서 우리는 2 개의 DAO 클래스를 호출한다고 가정합니다. 하나는 실패하고 다른 성공입니다.이 경우 @Transactional
서비스 중 하나 가 아니면 하나의 DB는 커밋하고 다른 하나는 롤백합니다.
따라서이 주석을 현명하게 사용하고 서비스 계층에서만 사용하는 것이 좋습니다.
먼저 트랜잭션 을 어디에서 사용 해야하는지 정의 해 봅시다 .
정답은-하나의 원자 작업으로 일련의 작업이 함께 완료되거나 작업 중 하나가 실패하더라도 변경되지 않도록해야 할 때입니다.
비즈니스 로직을 서비스에 넣는 것은 잘 알려진 관행입니다. 따라서 서비스 메소드에는 단일 논리 작업 단위로 수행해야하는 다른 조치가 포함될 수 있습니다. 그렇다면 그러한 방법은 트랜잭션 으로 표시해야합니다 . 물론 모든 방법에 이러한 제한이 필요한 것은 아니므로 전체 서비스를 transactional 로 표시 할 필요는 없습니다 .
그리고 @Transactional이 메서드 성능을 저하시킬 수 있다는 점을 잊지 마십시오 . 전체 그림을 보려면 트랜잭션 격리 수준을 알아야합니다. 그것이 필요하지 않은 곳에서 @Transactional 사용을 피하는 데 도움이 될 수 있습니다 .
@Transactional 을 DAO와 서비스 계층 사이의 별도 중간 계층에 유지하는 것이 좋습니다 . 롤백이 매우 중요하므로 모든 DB 조작을 중간 계층에 배치하고 비즈니스 논리를 서비스 계층에 작성할 수 있습니다. 중간 계층은 DAO 계층과 상호 작용합니다.
ObjectOptimisticLockingFailureException 과 같은 여러 상황에서 도움이됩니다. 이 예외는 거래가 끝난 후에 만 발생합니다. 따라서 중간 계층에서는 잡을 수 없지만 이제 서비스 계층에서는 잡을 수 있습니다. 서비스 계층에 @Transactional이 있으면 불가능합니다. 컨트롤러를 잡을 수는 있지만 컨트롤러는 가능한 깨끗해야합니다.
모든 저장, 삭제 및 업데이트 옵션을 완료 한 후 별도의 스레드로 메일 또는 SMS를 보내는 경우 중간 계층에서 트랜잭션이 완료된 후 서비스에서이를 수행 할 수 있습니다. 서비스 계층에서 @Transactional을 언급하면 트랜잭션이 실패하더라도 메일이 전송됩니다.
따라서 중간 @Transaction 레이어를 사용하면 코드를보다 쉽고 효율적으로 처리 할 수 있습니다. 그렇지 않으면 DAO 계층에서 사용하는 경우 모든 작업을 롤백하지 못할 수 있습니다. 서비스 계층에서 사용 하는 경우 경우에 따라 AOP (Aspect Oriented Programming) 를 사용해야 할 수도 있습니다 .