spring-boot-starter-aop
필요타겟(Target)
조인포인트(Join Point)
포인트컷(Pointcut)
애스펙트(Aspect)
어드바이스(Advice)
위빙(Weaving)
@Aspect
@Component
public class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
@Around("org.prgms.kdt.aop.CommonPointcut.servicePublicMethodPointCut()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable{
log.info("Before method called {}", joinPoint.getSignature().toString());
var result = joinPoint.proceed();
log.info("After method called with result => {}", result);
return result;
}
}
@Aspect
사용@Component
로 Bean 등록@Around("execution(접근제한자 반환타입 패키지명.클래스명.메서드이름(타입 OR ..))")
@Aspect
@Component
public class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
@Pointcut("execution(public * org.prgms.kdt..*Service.*(..))")
public void servicePublicMethodPointCut(){};
@Around("servicePublicMethodPointCut()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable{
log.info("Before method called {}", joinPoint.getSignature().toString());
var result = joinPoint.proceed();
log.info("After method called with result => {}", result);
return result;
}
}
@TrackTime
이 붙은 메서드에 AOP가 적용 된다.
@Target(METHOD)
@Retention(RUNTIME)
public @interface TrackTime { }
@Aspect
@Component
public class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
@Around("@annotation(org.prgms.kdt.aop.TrackTime)")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable{
log.info("Before method called {}", joinPoint.getSignature().toString());
var result = joinPoint.proceed();
log.info("After method called with result => {}", result);
return result;
}
}
@Override
@TrackTime
public Voucher insert(Voucher voucher) {
storage.put(voucher.getVoucherId(), voucher);
return voucher;
}
spring-boot-starter-jdbc
필요var transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
jdbcTemplate.update("update customers set name = :name where customer_id = UUID_TO_BIN(:customerId)", toParaMap(customer));
jdbcTemplate.update("update customers set email = :email where customer_id = UUID_TO_BIN(:customerId)", toParaMap(customer));
transactionManager.commit(transaction);
}catch(DataAccessException e) {
logger.error("Got error", e);
transactionManager.rollback(transaction);
}
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) { // 알아서 롤백 처리
// 아래의 2개의 명령어가 하나의 transaction
jdbcTemplate.update("update customers set name = :name where customer_id = UUID_TO_BIN(:customerId)", toParaMap(customer));
jdbcTemplate.update("update customers set email = :email where customer_id = UUID_TO_BIN(:customerId)", toParaMap(customer));
}
});
@Transactional
사용@Override
@Transactional
public void createCustomers(List<Customer> customers) {
customers.forEach(customerRepository::insert);
}
@Transactional(propagation = Propagation.값)
값 | 설명 |
---|---|
REQUIRED | 기본값. 진행중인 트랜잭션이 있는 경우 그 트랜잭션을 사용. 만약 없다면 새로운 트랜잭션 시작. |
MANDATORY | 호출 전에 반드시 진행 중인 트랜잭션이 존재해야한다. 존재하지 않을 경우 예외가 발생한다 |
REQUIRES_NEW | 항상 새로운 트랙잭션을 사용 |
SUPPORTS | 트랜잭션을 필요로 하지는 않는다. 하지만 진행 중인 트랜잭션이 있다면 해당 트랜잭션을 사용하게 된다. |
NOT_SUPPORTED | 트랜잭션이 필요하지 않다는 것을 의미한다. 진행 중인 트랜잭션이 있다면 잠시 중단하고 메소드 실행이 종료된 후 트랜잭션을 재개한다. |
NEVER | 트랜잭션이 필요하지 않다는 것을 의미한다. 진행 중인 트랜잭션이 있다면 예외를 발생시킨다. |
NESTED | 이미 진행 중인 트랜잭션이 있으면 중첩 트랜잭션을 시작한다. 즉, 별개의 트랜잭션을 만드는 것이 아니라 트랜잭션 안에 다시 트랜잭션을 만든다. 중첩 트랜잭션은 먼저 시작된 트랜잭션의 커밋과 롤백에 영향을 받지만 자신의 커밋과 롤백은 먼저 시작된 트랜잭션에 영향을 주지 못한다. DB 벤더에 따라 지원이 안되는 경우도 있다. |
격리 수준 | 설명 |
---|---|
LV.0 READ_UNCOMMITED | 한 트랜잭션의 변경된 내용을 COMMIT이나 ROLLBACK과 상관 없이 다른 트랜잭션에서 읽을 수 있다. |
LV1. READ_COMMITED | 한 트랜잭션의 변경 내용이 COMMIT 되어야만 다른 트랜잭션에서 조회할 수 있다. 가장 많이 사용되는 격리 수준 |
LV2. REPEATABLE_READ | 트랜잭션이 시작되기 전에 COMMIT된 내용에 대해서만 조회할 수 있다(트랜잭션 동안 같은 데이터를 읽게 한다. 여러 버전 만든다.). 일관성 있는 결과 보장. |
LV3. SERIALIZABLE | 트랜잭션이 특정 테이블을 읽으면 다른 트랜잭션은 그 테이블의 데이터를 추가/변경/삭제할 수 없다. |
Dirty Read
커밋되지 않은 수정중인 데이터를 다른 트랜잭션에서 읽을 수 있도록 허용할 때 발생하는 현상이다.
한 트랜잭션(T1)이 데이타에 접근하여 값을 A에서 B로 변경했고 아직 커밋을 하지 않았을때, 다른 트랜잭션(T2)이 해당 데이타를 조회한 상황에서 만약 T1이 변경을 commit하지 않는다면?
😱 데이터가 꼬이는 문제가 발생
Non-Repeatable Read
한 트랜잭션에서 같은 쿼리를 두 번 수행할 때 그 사이에 다른 트랜잭션 값을 수정 또는 삭제하면서 두 쿼리의 결과가 상이하게 나타나는 일관성이 깨진 현상이다.
Phantom Read
한 트랜잭션 안에서 일정 범위의 레코드를 두 번 이상 읽었을 때, 첫번째 쿼리에서 없던 레코드가 두번째 쿼리에서 나타나는 현상이다.