JAVA. Transaction & AOP
남궁성님의 스프링 정석 : 남궁성과 끝까지 간다를 정리한 내용입니다.
0. 차례
- Transaction
- AOP
- @Transactional
1. Transaction
1-1. 트랜잭션이란?
먼저 Transaction 이 무엇인지 알아보자.
트랜잭션(Transaction)이란
간단히는 더이상 나눌 수 없는 작업의 단위
데이터베이스에서 논리적 상태 변화, 즉 Insert, Update, Delet로 데이터베이스의 데이터가 변화가 있는 것을 트랜잭션이라 한다.
데이터의 변화가 많은 테이블을 트랜잭션 테이블이라 하고, 그렇지 않은 것을 마스터 테이블이라 한다.
1-2. 트랜잭션의 속성 (ACID)
- Atomicity(원자성)
- 나눌수 없는 하나의 작업으로 이루어져야 한다 .
- Consistency(일관성)
- Tx 수행 전과 후가 일관된 상태를 유지해야한다.
- Isolation(고립성)
- 각 Tx는 독립적으로 수행되어야 한다.
- Isolation Level 을 적절히 조절해야 한다.
- Durability(영속성)
- 성공한 Tx의 결과는 유지되어야 한다.
1-3. 커밋과 롤백
- Commit(커밋)
- 작업한 내용을 DB에 영구히 저장한다.
- 자동커밋
- Default 값이 1 이다.
- 명령 실행 후, 자동으로 커밋이 수행 (rollback 불가)
- 수동커밋
- Set autocommit (0) 을 통해 변경
- 명령 실행 후, 명시적으로 commit 또는 rollback 을 입력
- Lollback(롤백)
- 최근 변경 사항을 취소(마지막 커밋으로 복귀)
1-4. Tx의 Isolation Level
아래 4개의 격리수준중 아래로 갈수록 트랜잭션 간의 데이터 격리(고립) 정도가 높아지며, 동시성 처리 성능이 떨어진다.
- READ UNCOMMITED
- 커밋되지 않은 데이터도 읽기 가능
- Dirty Reead 발생
- 다른사람이 손을 대서 더러워짐을 뜻함,,
- 더티 리드란 다른 트랜잭션에서 처리한 작업이 완료되지 않았음에도, 다른 트랜잭션에서 볼수 있게 되는 현상을 말한다.
- READ COMMITTED
- Phantom Read 발생
- 커밋된 데이터만 읽기 가능
- 온라인 서비스에서 가장 많이 사용되는 격리수준
- NON-REPEATABLE READ 발생
- 하나의 트랜잭션 내에서 동일한 SELECT 쿼리를 실행했을 때 항상 같은 결과를 보장해야 한다는 REPEATABLE READ 정합성에 어근사는 것을 말함
- 오라클 기본 격리수준
- REPEATABLE READ (기본값)
- Tx이 시작된 이후 변경은 무시된다.
- 반복해서 읽기 가능
- InnoDb 스토리지 엔진 기본 격리 수준
- Phantom Read 발생
- 첫 번째 쿼리에서는 없었던 레코드가 두분째 쿼리에서는 존재하는 것처럼 보이는 현상을 말한다. (Repeatable Read 나 ReadCommited 격리 수준에서 발생할 수 있음)
- SERIALIZBLE
- 격리수준이 가장 높음
- 한번에 하나의 Tx만 독립적으로 수행.
- 직렬 처리를 하여 데이터의 일관성과 무결성을 보장할 수 있지만, 서비스 성능(처리량이 많은)이 많이 떨어진다.
1-5. Transaction Test
public void transcationTest() throws Exception {
Connection conn = null;
try {
conn = ds.getConnection();
conn.setAutoCommit(false); // auto commit 을 false 로 해두어라
String sql = "insert into test (id, pw) values (?, ?)"; // id 는 pk
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "han");
pstmt.setString(2, "sw");
int rowCnt = pstmt.executeUpdate(); // insert, delete, update
pstmt.setString(1, "han")
rowCnt = pstmt.executeUpdate;
conn.commit();
} catch(Exception e) {
conn.rollback();
}
}
2. AOP
2-1. AOP란
AOP는 관점 지향 프로그래밍 (Aspect Oriented Programming)의 약자이다. 관점지향프로그래밍이라는 용어가 굉장히 어색하지만, 부가기능을(advice)를 동적으로 추가해주는 기술이라 생각하면된다. 코드의 메소드 시작 또는 끝에 자동으로 코드(advice)를 추가해주는 기술이다.
OOP 와 마찬가지로 AOP도 변경에 유리한 코드를 생성하기 위함이다. (분리)
2-2. AOP 직접 구현하기 example
import java.lang.reflect.Method;
public class AopMain {
public static void main(String[] args) throws Exception {
MyAdvice myAdvice = new MyAdvice();
Class myClass = class.forName("com.test.aop.MyClasss");
Object obj = myClass.newInstance();
for (Method m : myClass.getDeclareMethods()) {
myAdvice.invoke(m, obj, null);
}
}
}
public MyAdvice {
void invoke(Method m, Object obj, Object... args) throws Excpetion {
// 이곳에 여러가지 조건문을 통해, 출력 여부를 결정할수도 있다.
// if(m.getAnnotation(Transactional.class)!=null)
System.out.println("before{");
m.invoke(obj, args);
System.out.println("}after");
}
}
public MyClass {
void test1() {
System.out.println("test1() is called");
}
void test2() {
System.out.println("test2() is called");
}
}
2-3. 코드를 자동으로 추가하려면, 어디에 추가해야 할까??
- Before Advice: 메소드의 시작지점
- After Advice: 메소드의 마지막 지점 (return 바로전)
- Around Advice: 양쪽다
메소드의 중간지점엔 불가능하다.
2-4. AOP 관련 용어
|용어|설명| |—|—–| |target|advice가 추가될 객체| |advice|target에 동적으로 추가될 부가기능(코드)| |join point|advice가 추가(join)될 대상(메서드)| |pointcut|join poin들을 정의한 패턴| |proxy|target에 advice가 동적으로 추가되어 생성된 객체| |weaving|target에 advice를 추가해서 proxy를 생성하는 것|
(advice) + (target) + (advice) = proxy
2-5. Adivce의 종류
Adivce 설정은 XML과 @Annotaion 으로 설정 가능하다.
종류 | 어노테이션 | 설명 |
---|---|---|
around advice | @Around | 메서드의 시작과 끝 부분에 추가되는 부가 기능 |
before advice | @Before | 메서드의 시작 부분에 추가되는 부가 기능 |
after advice | @After | 메서드의 끝 부분에 추가되는 부가기능 |
after returning | @AfterReturning | 예외가 발생하지 않았을 때, 실행되는 부가 기능 |
after throwing | @AfterThrowing | 예외가 발생했을 때, 실행되는 부가기능 |
2-6. AOP Example
dependency 추가
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
example code
public class LoggingAdvice {
@Around("execution(* com.my.test.aop.*.*.(..))") // around 안에 pointcut 추가 - 부가기능이 적요될 메서드의 패턴
public Object methodCallLog(ProceedingJoinPoint pjp) throws Throwable { // return 결과는 void 가능, ProceedingJoinPoint 는 메서드의 모든 정보를 불러올수 있다.
long start = System.currentTimeMillis();
System.out.println("start");
Object result = pjp.proceed(); // target의 메서드 호출
System.out.println("end" + (System.currentTimeMillis() - start) + "ms");
return result; // 메서드가 여러곳에 적용될수 있으므로 메서드 호출겨로가를 반환한다.
}
}