답변:
좋은 질문이지만 대답하기는 쉽지 않습니다.
트랜잭션이 서로 관련되는 방식을 정의합니다. 일반적인 옵션 :
Required
: 코드는 항상 트랜잭션에서 실행됩니다. 새 트랜잭션을 작성하거나 사용 가능한 경우 재사용합니다.Requires_new
: 코드는 항상 새 트랜잭션에서 실행됩니다. 현재 트랜잭션이있는 경우 일시 중단합니다.트랜잭션 간의 데이터 계약을 정의합니다.
Read Uncommitted
: 더티 읽기를 허용합니다.Read Committed
: 더티 읽기를 허용하지 않습니다.Repeatable Read
: 동일한 트랜잭션에서 행을 두 번 읽는 경우 결과는 항상 동일합니다.Serializable
: 모든 트랜잭션을 순서대로 수행합니다.다중 스레드 응용 프로그램에서 수준에 따라 성능 특성이 다릅니다. dirty reads
개념 을 이해하면 좋은 옵션을 선택할 수 있다고 생각합니다 .
더티 읽기가 발생할 수있는 예 :
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
따라서 제정 된 기본값 (그런 주장이있을 수있는 경우)이 될 수 있습니다 Read Committed
. 이는 전파 수준과 함께 실행중인 다른 트랜잭션에 의해 이미 커밋 된 값만 읽을 수 있습니다 Required
. 그런 다음 응용 프로그램에 다른 요구 사항이있는 경우 거기서부터 작업 할 수 있습니다.
provideService
루틴을 시작할 때 항상 새 트랜잭션이 작성 되고 떠날 때 완료 되는 실제 예 :
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
우리가 대신 사용했다 Required
, 트랜잭션은 열린 상태를 유지 할 루틴을 입력 할 때 트랜잭션이 이미 열려 있다면. 또한 rollback
여러 트랜잭션이 동일한 트랜잭션에 참여할 수 있으므로 결과 가 다를 수 있습니다.
테스트를 통해 동작을 쉽게 확인하고 전파 수준에 따라 결과가 어떻게 다른지 확인할 수 있습니다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
전파 수준
Requires new
: 우리는 기대 fooService.provideService()
했다 NOT 그것이 자신의 서브 트랜잭션의 생성 이후 롤백.
Required
: 모든 것이 롤백되고 백업 저장소가 변경되지 않은 것으로 예상합니다.
sessionFactory.getCurrentTransaction()
추가 HibernateTemplate
되었으므로 더 이상 트랜잭션을 관리 하기 위해 실행할 필요가 없다는 것입니다 . 나는 그것을 :) 제거
PROPAGATION_REQUIRED = 0 ; DataSourceTransactionObject T1이 메소드 M1에 대해 이미 시작된 경우 다른 메소드 M2에 대해 Transaction 오브젝트가 필요하지 않은 경우 새 Transaction 오브젝트가 작성되지 않습니다. 동일한 오브젝트 T1이 M2에 사용됩니다.
PROPAGATION_MANDATORY = 2 ; 메소드는 트랜잭션 내에서 실행해야합니다. 기존 트랜잭션이 진행 중이 아닌 경우 예외가 발생합니다.
PROPAGATION_REQUIRES_NEW = 3 ; 메소드 M1에 대해 DataSourceTransactionObject T1이 이미 시작되어 진행 중이면 (실행 메소드 M1). 다른 메소드 M2가 실행을 시작하면 T2는 M2.M2에 대한 새 DataSourceTransactionObject T2가있는 자체 트랜잭션 컨텍스트 내에서 실행되어 메소드 M2 지속 기간 동안 일시 중단됩니다.
PROPAGATION_NOT_SUPPORTED = 4 ; DataSourceTransactionObject T1이 메소드 M1에 대해 이미 시작된 경우 다른 메소드 M2가 동시에 실행되는 경우 M2는 트랜잭션 컨텍스트 내에서 실행되지 않아야합니다. M2가 완료 될 때까지 T1이 일시 중단됩니다.
PROPAGATION_NEVER = 5 ; 트랜잭션 컨텍스트에서 실행되는 메소드가 없습니다.
격리 수준 : 다른 동시 트랜잭션의 활동에 의해 영향을받을 수있는 트랜잭션의 양입니다 . 이는 여러 테이블에 걸쳐 데이터를 일관된 상태로 유지하는 일관성을 지원합니다. 데이터베이스에서 행 및 / 또는 테이블을 잠그는 작업이 포함됩니다.
여러 거래의 문제
시나리오 1. T1 트랜잭션이 다른 동시 트랜잭션 T2에 의해 작성된 테이블 A1에서 데이터를 읽는 경우 T2가 롤백되는 동안 T1에 의해 얻은 데이터는 유효하지 않은 데이터입니다 (예 : a = 2는 원본 데이터입니다). T2 롤백 인 경우 a = 1은 DB에서 a = 2로 롤백됩니다.하지만, T1은 a = 1이지만 DB 테이블에서는 a = 2로 변경됩니다.
시나리오 2. T1 트랜잭션이 테이블 A1에서 데이터를 읽는 경우 다른 동시 트랜잭션 (T2)이 테이블 A1의 데이터를 업데이트하는 경우 T1이 읽은 데이터가 테이블 A1과 다른 경우 T2가 테이블 A1의 데이터를 업데이트 했으므로 T1 인 경우 a = 1을 읽고 T2가 a = 2를 업데이트 한 다음 a! = b를 읽습니다.
시나리오 3 T1 트랜잭션이 특정 행 수를 가진 테이블 A1에서 데이터를 읽는 경우 다른 동시 트랜잭션 (T2)이 테이블 A1에 더 많은 행을 삽입하는 경우 T1이 읽은 행 수는 테이블 A1의 행과 다릅니다.
시나리오 1을 Dirty 읽기 라고 합니다.
시나리오 2를 반복 불가능한 읽기라고합니다.
시나리오 3을 팬텀 읽기 라고 합니다.
따라서 격리 수준은 시나리오 1, 시나리오 2, 시나리오 3 을 방지 할 수있는 확장입니다. 잠금을 구현하여 완전한 격리 수준을 얻을 수 있습니다. 따라서 동시 읽기 및 쓰기가 동일한 데이터에 발생하는 것을 막을 수 있지만 성능에 영향을 미칩니다. 격리 수준은 응용 프로그램에서 응용 프로그램에 따라 얼마나 많은 격리가 필요한지에 따라 다릅니다.
ISOLATION_READ_UNCOMMITTED : 아직 커밋되지 않은 변경 사항을 읽을 수 있습니다. 시나리오 1, 시나리오 2, 시나리오 3이 발생합니다.
ISOLATION_READ_COMMITTED : 커밋 된 동시 트랜잭션에서 읽을 수 있습니다. 시나리오 2 및 시나리오 3으로 인해 문제가 발생할 수 있습니다. 다른 트랜잭션이 데이터를 업데이트 할 수 있기 때문입니다.
ISOLATION_REPEATABLE_READ : 동일한 필드를 여러 번 읽으면 자체적으로 변경 될 때까지 동일한 결과를 얻을 수 있습니다. 시나리오 3으로 인해 어려움을 겪을 수 있습니다. 다른 트랜잭션이 데이터를 삽입 할 수 있기 때문에
ISOLATION_SERIALIZABLE : 시나리오 1, 시나리오 2, 시나리오 3이 발생하지 않습니다. 완전 격리입니다. 전체 잠금이 포함됩니다. 잠금으로 인해 성능이 저하됩니다.
당신은을 사용하여 테스트 할 수 있습니다
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
격리 및 전파에 대해 다른 값으로 디버깅하고 결과를 볼 수 있습니다.
각 매개 변수에 대한 충분한 설명은 다른 답변으로 제공됩니다. 그러나 실제 사례를 요청한 경우 다음은 다양한 전파 옵션 의 목적을 설명하는 예입니다 .
확인 전자 메일이 사용자에게 전송되는 가입 서비스 구현을 담당한다고 가정합니다 . 하나 는 사용자 를 등록 하기위한 것이고 다른 하나는 전자 메일 을 보내기 위한 것입니다. 하나는 첫 번째 것에서 호출됩니다. 예를 들면 다음과 같습니다./* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
두 번째 서비스가 전파 유형이 REQUIRES_NEW 임을 알 수 있습니다. 이고 예외가 발생할 가능성이있을 수 있습니다 (SMTP 서버 다운, 유효하지 않은 전자 메일 또는 기타 이유로). 전체 프로세스가 롤백되는 것을 원하지 않을 것입니다. 데이터베이스 또는 다른 것들로부터 사용자 정보를 제거하는 것; 따라서 별도의 트랜잭션으로 두 번째 서비스를 호출합니다.
예제로 돌아가서 이번에는 데이터베이스 보안에 대해 염려하므로 DAO 클래스를 다음과 같이 정의하십시오./* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
즉, DAO 개체와 db에 대한 잠재적 액세스가 생성 될 때마다 서비스 중 하나에서 호출이 이루어 졌음을 확인해야합니다. 즉, 실제 트랜잭션이 존재해야합니다. 그렇지 않으면 예외가 발생하므로 전파 유형은 MANDATORY 입니다.
격리 수준 은 한 트랜잭션에 의해 일부 데이터 리포지토리에 대한 변경 내용이 다른 동시 동시 트랜잭션에 미치는 영향과 변경된 데이터가 다른 트랜잭션에 언제 어떻게 사용되는지를 정의합니다. Spring 프레임 워크를 사용하여 트랜잭션을 정의 할 때 동일한 트랜잭션이 실행될 격리 레벨을 구성 할 수도 있습니다.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
READ_UNCOMMITTED 격리 수준은 트랜잭션이 다른 트랜잭션에 의해 아직 커밋되지 않은 데이터를 읽을 수 있음을 나타냅니다.
READ_COMMITTED 격리 수준은 트랜잭션이 아직 다른 트랜잭션에 의해 커밋되지 않은 데이터를 읽을 수 없음을 나타냅니다.
REPEATABLE_READ 격리 수준은 트랜잭션이 데이터베이스에서 하나의 레코드를 여러 번 읽는 경우 모든 해당 읽기 작업의 결과는 항상 동일해야한다고 명시합니다.
직렬화 가능 격리 수준은 모든 격리 수준 중에서 가장 제한적입니다. 트랜잭션은 모든 수준 (읽기, 범위 및 쓰기 잠금)에서 잠금으로 실행되므로 마치 마치 직렬화 된 방식으로 실행 된 것처럼 보입니다.
전파 는 논리적 또는 물리적 트랜잭션에서 비즈니스 방법을 캡슐화하는 방법을 결정하는 기능입니다.
Spring REQUIRED 동작은 현재 Bean 메소드 실행 컨텍스트에 이미 열린 트랜잭션이있는 경우 동일한 트랜잭션이 사용됨을 의미합니다.
REQUIRES_NEW 동작은 컨테이너에서 항상 새 실제 트랜잭션이 작성됨을 의미합니다.
NESTED 동작은 중첩 된 스프링 트랜잭션이 동일한 물리적 트랜잭션을 사용하도록하지만 중첩 된 호출간에 저장 점을 설정하므로 내부 트랜잭션도 외부 트랜잭션과 독립적으로 롤백 될 수 있습니다.
MANDATORY 동작은 기존의 열린 트랜잭션이 이미 존재해야한다는 것을 나타냅니다. 그렇지 않으면 컨테이너에서 예외가 발생합니다.
NEVER 동작은 기존의 열린 트랜잭션이 아직 존재하지 않아야 함을 나타냅니다. 트랜잭션이 존재하면 컨테이너에 의해 예외가 발생합니다.
NOT_SUPPORTED 동작은 모든 트랜잭션 범위 밖에서 실행됩니다. 열린 트랜잭션이 이미 존재하면 일시 중지됩니다.
열린 트랜잭션이 이미 존재하는 경우 SUPPORTS 동작은 트랜잭션 범위에서 실행됩니다. 이미 열린 트랜잭션이없는 경우 메소드는 트랜잭션이 아닌 방식으로 실행됩니다.
트랜잭션은 데이터베이스의 작업 단위를 나타냅니다.
스프링 TransactionDefinition
호환 트랜잭션 속성을 정의하는 스프링 인터페이스. @Transactional
주석은 메소드 또는 클래스의 트랜잭션 속성을 설명합니다.
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
전파 (재생) : 트랜잭션 간 관계에 사용됩니다. (자바 인터 쓰레드 통신과 유사)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
격리 : 격리는 데이터베이스 트랜잭션의 ACID (원 자성, 일관성, 격리, 내구성) 속성 중 하나입니다. 격리는 다른 사용자 및 시스템이 트랜잭션 무결성을 표시하는 방법을 결정합니다. 리소스 잠금, 즉 동시성 제어에 사용되므로 지정된 지점에서 하나의 트랜잭션 만 리소스에 액세스 할 수 있어야합니다.
잠금 인식 : 격리 수준에 따라 잠금이 유지되는 기간이 결정됩니다.
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
읽기 인식 : 다음과 같은 3 가지 주요 문제가 발생합니다.
UPDATES
다른 TX에서 커밋 된 읽기 .INSERTS
및 / 또는 DELETES
다른 TX에서 읽 습니다 .다른 종류의 읽기로 분리 수준 :
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
Read Uncommited
실제로 ACID
호환 되지 않기 때문에 거의 사용하고 싶지 않습니다 . Read Commmited
좋은 기본 시작 장소입니다. Repeatable Read
보고, 롤업 또는 집계 시나리오에서만 필요합니다. postgres가 포함 된 많은 DB는 실제로 Repeatable Read를 지원하지 않으므로 Serializable
대신 사용해야 합니다. Serializable
당신이 알고있는 것들이 다른 어떤 것과도 완전히 독립적으로 일어나야한다는 것에 유용합니다. synchronized
Java에서 와 같이 생각하십시오 . 직렬화는 REQUIRES_NEW
전파 와 함께 진행 됩니다.
REQUIRES
"서비스"레벨 함수뿐만 아니라 UPDATE 또는 DELETE 쿼리를 실행하는 모든 함수에 사용 합니다. SELECT 만 실행하는 DAO 레벨 기능의 SUPPORTS
경우 TX가 이미 시작된 경우 (즉, 서비스 기능에서 호출되는 경우) TX에 참여하는 것을 사용 합니다.
트랜잭션 격리 및 트랜잭션 전파는 관련이 있지만 분명히 두 가지 다른 개념입니다. 두 경우 모두 선언적 트랜잭션 관리 또는 프로그래밍 방식 트랜잭션 관리 를 사용하여 클라이언트 경계 구성 요소에서 기본값을 사용자 정의 합니다 . 각 격리 수준 및 전파 특성에 대한 자세한 내용은 아래 참조 링크에서 확인할 수 있습니다.
데이터베이스에 대한 두 개 이상의 실행중인 트랜잭션 / 연결에 대해 한 트랜잭션의 쿼리에 의한 변경이 다른 트랜잭션의 쿼리에 영향을 미치거나 가시적 인 방식 및시기. 또한이 트랜잭션의 변경 내용을 다른 트랜잭션과 격리하거나 그 반대로 변경하는 데 사용할 데이터베이스 레코드 잠금 종류와 관련이 있습니다. 이것은 일반적으로 트랜잭션에 참여하는 데이터베이스 / 자원에 의해 구현됩니다.
.
주어진 요청 / 처리를위한 엔터프라이즈 응용 프로그램에는 작업을 수행하는 데 관련된 많은 구성 요소가 있습니다. 이 구성 요소 중 일부는 각 구성 요소와 하위 구성 요소에 사용될 트랜잭션의 경계 (시작 / 종료)를 표시합니다. 구성 요소의이 트랜잭션 경계에 대해 Transaction Propogation은 각 구성 요소가 트랜잭션에 참여할 것인지 여부와 호출하는 구성 요소가 이미 트랜잭션을 만들거나 시작하지 않은 경우 발생하는 상황을 지정합니다. 이것은 Java EE 트랜잭션 속성과 동일합니다. 이것은 일반적으로 클라이언트 트랜잭션 / 연결 관리자에 의해 구현됩니다.
참고:
내가 실행 한 outerMethod
, method_1
그리고 method_2
다른 전파 모드.
다음은 다른 전파 모드에 대한 출력입니다.
외부 방법
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
방법 _1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
방법 _2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
우리는 이것을 추가 할 수 있습니다 :
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
다음과 같이 사용할 수 있습니다 :
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
이 것을 사용할 수도 있습니다.
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}