Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- 추상메서드
- 컬렉션프레임워크
- 객체 비교
- oracle
- 인터페이스
- 사용자예외클래스생성
- GRANT VIEW
- 제네릭
- Java
- cursor문
- 참조형변수
- exception
- 한국건설관리시스템
- EnhancedFor
- 생성자오버로드
- 어윈 사용법
- 대덕인재개발원
- 오라클
- 자동차수리시스템
- 자바
- 다형성
- 메소드오버로딩
- 집합_SET
- 컬렉션 타입
- 정수형타입
- 예외미루기
- 예외처리
- 환경설정
- abstract
- NestedFor
Archives
- Today
- Total
거니의 velog
231207_SPRING 2 (12-2) 본문
package kr.or.ddit.controller.transaction;
public class TransactionController {
/*
* [ 15장. 트랜잭션 ]
*
* 1. 트랜잭션 설명
*
* - 한 번에 이루어지는 작업의 단위를 의미한다.
*
* # 트랜잭션 성격(ACID 원칙)
*
* - 원자성(Actomicity)
* > 하나의 트랜잭션은 모두 하나의 단위로 처리되어야 한다.
* - 일관성(Consistency)
* > 트랜잭션이 성공했다면 모든 데이터는 일관성을 유지해야 한다.
* - 격리성(Isolation)
* > 트랜잭션으로 처리되는 동안에는 외부에서의 간섭이 없어야 한다.
* - 영속성(Durability)
* > 트랜잭션이 성공적으로 처리되면 그 결과는 영속적으로 보관되어야 한다.
*
* 2. 트랜잭션 설정
*
* # 스프링 설정
*
* - root-context.xml 설정
* > 네임스페이스, 스키마 추가
* - transactionManager 빈 등록
* - 어노테이션 기반의 트랜잭션 제어 활성화
*
* 3. 트랜잭션 적용
*
* [테스트는 AOP가 적용된 CrudBoardController와 연계된 ServiceImpl에서 진행합니다.]
* [테스트는 AOP가 적용된 CrudMemberController와 연계된 ServiceImpl에서 진행합니다.]
*
* # 트랜잭션 상황 부여
*
* 예) 회원은 반드시 하나의 권한을 가진다는 비즈니스 규칙이 있다.
* 회원과 회원권한 테이블이 각각 개별로 존재하지만 회원 정보를 저장할 때 반드시 회원권한 정보를 동시에 저장해야 한다.
* 클래스나 메소드에 대해 @Transactional 어노테이션을 부여하여 트랜잭션을 적용할 수 있다.
*
* # @Transactional 어노테이션의 예외처리 견해
*
* @Transactional을 적용하고 중간에 에러를 발생시킨 후, 롤백된 상태를 확인하려고 했으나 롤백이 되지 않고
* 데이터베이스에 데이터가 등록된다. 그 이유는 스프링 프레임워크에서 @Transactional은 기본적으로
* Checked Exception에 대해서는 롤백 처리를 하지 않도록 설계되어 있다.
* 기본적으로 스프링에서 트랜잭션 처리는 RuntimeException계열(Unchecked Exception)이라면
* Rollback 처리를 한다.
*
* 여기서, 트랜잭션으로 국한된 롤백 정책이 아니라 스프링 프레임워크에서의 기본 정책에 대한 내용일 뿐이다.
* 실제로 '트랜잭션 롤백 처리', 'Checked Exception vs Unchecked Exception'등 검색해 보면
* 잘못된 정보들로 복사/붙여넣기 되어 있는 내용이 수두룩하여 많은 에러 정보를 공유하고 있다.
* (모든 블로그기 그렇진 않지만, 대표적인 표가 무수히 많이 공유되고 있음을 알 수 있다)
*
* ***
* '예외처리 시 트랜잭션 처리'에 대한 정보가 잘못 표기된 경우가 많은데...
* 이럴 때 되고 저럴 때 되는 것이 아니라, 기본적인 스프링 트랜잭션 정책이 있고 그 안에서 정책 이외의 옵션으로
* 특정 에러가 발생했을 때 롤백을 진행할 수 있도록 제공한다. (많은 블로그들을 100% 믿지 말길 바랄게요...)
* 혹여나 블로그를 관리하는 사람이 있다면 검증된 내용으로만 적어주길...
*
* # Exception(예외)와 Error(에러)
*
* - Exception : 개발 알고리즘에서 발생하는 오류로 개발자가 작성한 코드에서 발생하므로
* 예외를 상황에 맞춰 처리할 수 있다.
* - Error : 시스템에서 발생하는 심각한 수준의 에러로 개발자가 미리 예측하여 대응할 수 없기 때문에 예외처리에
* 대한 부분을 신경쓰지 않아도 된다.
*
* CheckedException과 UncheckedException
* - RuntimeException의 상속 여부에 따라 Checked Exception 과 Unchecked Exception 으로 나누어진다.
*
* ------------------------------------------------------------------------------------
* | CheckedException | UncheckedException
* ------------------------------------------------------------------------------------
* 예외처리 여부 | 반드시 예외 처리 코드가 있어야 한다. | 강제로 예외 처리는 아니다.
* 예외 확인 시점 | 컴파일 단계에서부터 컴파일이 되지 않는다. | 런타임중 예외가 확인된다.
* 클래스 | IOException, SQLException... | NullPointException, IndexOutOf...
* ------------------------------------------------------------------------------------
*
* *** 트랜잭션도 AOP의 개념이 반영된 관점 지향 프로그래밍이라 할 수 있다.
*
* # RuntimeException 계열의 종류
* - ArithmeticException
* - ArraysStoreException
* - ArrayIndexOutOfBoundsException
* - ClassCastException
* - NullPointerException
* - NegativeArraySizeException
* - NoClassDefFoundException
* - OutOfMemoryException
* - IndexOutOfBoundsException
* - IllegalArgumentException
* - IllegalMonitorStateException
*
* # 선언적 트랜잭션 @Transactional
*
* - 컨트롤러 메소드 각 단위로 세밀한 트랜잭션 속성 제어가 가능
* - 해당 어노테이션이 클래스 수준에서 선언되면 선언 클래스 및 해당 하위 클래스의 모든 메서드에 기본값으로 적용된다.
* - RuntimeException 계열, Error 예외에 대해서는 Rollback이 가능하다. (공식문서 내용)
*
* 1) isolation (격리 수준)
* - 각 트랜잭션이 존재할 때, 트랜잭션들 끼리 서로 고립된 수준을 나타내며 서로 간에 가용된 데이터를 컨트롤 할지에
* 대한 부분들을 설정할 수 있다.
* - isolation 기본 값은 DEFAULT 이다.
* - 새롭게 시작된 트랜잭션에만 적용되므로, Propagation.REQUIRED 또는 Propagation.REQUIRED_NEW
* 와 함께 사용되도록 독점 설계되었다.
*
* 1-1) 옵션
*
* *** 용어
* # Dirth read
* - Commit이 이뤄지지 않은 다른 트랜잭션의 데이터를 읽는 것을 의미
* # Non-repeatable Read(데이터의 수정/삭제)
* - 처리 중인 트랜잭션에서 다른 트랜잭션이 Commit한 데이터를 읽을 수 있는 것을 의미
* (처음 조회에 대한 트랜잭션과 두 번째 조회에 대한 트랜잭션 결과가 다를 수 있다.)
* # Phantom Read
* - 자신이 처리중인 트랜잭션에서 처리했던 내용 안에서 다른 트랜잭션이 데이터를 수정 후 Commit 하더라도 자신의
* 트랜잭션에서 처리한 내용만 사용하는 것을 의미
*
* - DEFAULT : 기본 데이터 저장소의 기본 격리 수준을 사용한다.
* - READ_COMMITED : Dirth read 가 방지됨을 나타내는 상수이다.
* Non-repeatable Read 및 Phantom Read가 발생할 수 있다.
* > 하나의 트랜잭션 처리가 이루어진 변경 내용이 Commit 된 후, 다른 트랜잭션에서 조회가 가능하다.
* > A 트랜잭션이 데이터를 변경하고 B 트랜잭션이 조회를 진행할 때 B 트랜잭션은 Shared lock이 걸린다.
* - READ_UNCOMMITED : Dirth read, Non-repeatable Read 및 Phantom Read가 발생할 수 있음
* 을 나타내는 상수이다.
* > 다른 트랜잭션의 내용이 Commit 또는 Rollback 되거나 되지 않아도 다른 트랜잭션에서 조회가 가능
* - REPEATABLE_READ : Dirth read, Non-repeatable Read가 방지됨을 나타내는 상수이다.
* Phantom Read가 발생할 수 있다.
* > 트랜잭션 Commit이 일어난 데이터에 대해서 조회가 가능
* (트랜잭션 완료 시까지 조회에 대한 Shared lock이 걸리지 않음)
* - SERIALIZABLE : Dirth read, Non-repeatable Read 및 Phantom Read가 방지됨을 나타냄.
* > Phantom Read가 발생하지 않는다. (거의 사용하지 않는 옵션임)
*
* 2) propagation (전파 옵션)
* - 기존 진행중인 트랜잭션 외에 추가적으로 진행중인 트랜잭션이 존재할 때 추가적인 트랜잭션에 대해서 어떻게
* 처리할 지에 대한 설정
* - 추가적인 트랜잭션을 기존 트랜잭션에 포함 시켜 함께 처리할 수도 있고, 추가적인 트랜잭션처럼 별도의 트랜잭션으로
* 추가할 수도 있고 다른 트랜잭션처럼 진행되다 에러를 발생 시킬 수도 있다.
*
* 2-1) 옵션
*
* - REQUIRED : 현재 트랜잭션을 지원하고 존재하지 않는 경우 새 트랜잭션을 만든다.
* > propagation 기본 default 옵션
* > 부모/자식간에 상관관계에서 자식부분의 트랜잭션이 rollback 처리 시, 부모까지 영향이 가서
* rollback 처리가 된다.
* - REQUIRED_NEW : 새로운 트랜잭션을 생성한다.
* > rollback은 각각 이루어 진다.
* - SUPPORTS : 트랜잭션이 있으면 현재 트랜잭션을 지원하고 트랜잭션이 없으면 트랜잭션이 아닌 방식으로 실행한다.
* - MANDATORY : 현재 트랜잭션을 지원하고, 없으면 예외를 발생시킨다.
* > 독립적인 트랜잭션으로 진행하면 안 되는 경우 사용
* - NESTED : 현재 트랜잭션이 있는 경우 중첩된(부모-자식) 트랜잭션 내에서 실행하고 그렇지 않은 경우
* REQUIRED와 같이 동작한다.
* > 부모에서 예외가 발생했을 때, 자식까지 영향이 가서 Commit 되지 않는다.
* - NEVER : 트랜잭션이 아닌 방식으로 실행하고 트랜잭션이 있으면 예외를 발생시킨다.
* > 실행 자체가 트랜잭션을 필요로 하지 않고, 트랜잭션이 존재한다면 예외를 발생시키도록 한다.
* > Existing transition found for transaction marked with propagation
* 'never' 에러
*
* 3) readOnly (읽기 전용 설정)
* - 읽기 전용인 경우 설정할 수 있는 Bool Flag, 런타임시 최적화를 허용한다.
* - readOnly 속성을 설정했다고 해서, 읽기 전용으로 무조건 설정된다는 보장이 없음
* (쓰기와 같은 트랜잭션이 실행될 수도 있음)
* > 읽기 전용에 대한 힌트를 분석할 수 없는 트랜잭션인 경우 throw를 던지지 않고 조용히 힌트를 무시한다.
*
* 4) timeout (트랜잭션 제한시간)
* - 기본값은 -1로 무제한이다.
* - timeout은 클라이언트와 서버와의 통신 중, 서버측 문제로 다음 처리를 이어나가지 못하는 'DeadLock'을
* 방지할 수 있는 속성이다.
* - 클라이언트와 서버간의 Restful API를 개발 시 고려해볼 속성
*
* 5) rollbackFor
* - 트랜잭션 롤백을 유발해야 하는 예외 유형을 나타내는 0개 이상의 예외 유형을 정의한다.
* - 기본적으로 트랜잭션은 롤백 되지만, CheckedException 계열의 에러를 롤백되지 않는다.
* > 공식 문서에서 제공하는 것처럼 RuntimeException 계열과 Error에 대해서는 기본적으로 롤백 가능
* - 기본적인 정책 이외에 에러를 처리할 경우, 해당 에러를 선언하여 롤백 정책을 추가한다.
*/
}
[root-context.xml]
<!-- 트랜잭션 관리자의 빈을 정의 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 어노테이션 기반의 트랜잭션 제어를 활성화 -->
<tx:annotation-driven />
package kr.or.ddit.service;
import java.io.IOException;
import java.util.List;
import kr.or.ddit.vo.Board;
public interface IBoardService {
public void register(Board board) throws IOException;
public List<Board> list();
public Board read(int boardNo);
public void modify(Board board);
public void remove(int boardNo);
public List<Board> search(Board board);
}
package kr.or.ddit.service.impl;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import kr.or.ddit.mapper.IBoardMapper;
import kr.or.ddit.service.IBoardService;
import kr.or.ddit.vo.Board;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class BoardServiceImpl implements IBoardService {
@Inject
private IBoardMapper mapper;
@Transactional
@Override
public void register(Board board) throws IOException {
log.info("BoardServiceImpl register 실행...!");
mapper.create(board);
if(true)
throw new IOException();
}
@Override
public List<Board> list() {
return mapper.list();
}
@Override
public Board read(int boardNo) {
return mapper.read(boardNo);
}
@Override
public void modify(Board board) {
mapper.update(board);
}
@Override
public void remove(int boardNo) {
log.info("BoardServiceImpl remove 실행...!");
mapper.delete(boardNo);
}
@Override
public List<Board> search(Board board) {
return mapper.search(board);
}
}
package kr.or.ddit.controller.crud;
import java.io.IOException;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.springframework.aop.support.AopUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import kr.or.ddit.service.IBoardService;
import kr.or.ddit.vo.Board;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping("/crud/board")
public class CrudBoardController {
@Inject
private IBoardService service;
@PostConstruct
public void init() {
log.info("aopProxy 상태(interface 기반) : {}", AopUtils.isAopProxy(service));
log.info("aopProxy 상태(클래스 상속 기반) : {}", AopUtils.isCglibProxy(service));
}
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String crudRegisterForm() {
log.info("crudRegisterForm() 실행...!");
return "crud/register";
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String crudRegister(Board board, Model model) throws IOException {
log.info("crudRegister() 실행...!");
service.register(board);
// 게시글을 입력 후 최근 게시글 번호가 담겨있다(boardNo)
log.info("게시글 등록 후 만들어진 최신 게시글 번호 : " + board.getBoardNo());
model.addAttribute("msg", "등록이 완료되었습니다!");
return "crud/success";
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String crudList(Model model) {
log.info("crudList() 실행...!");
List<Board> boardList = service.list();
model.addAttribute("boardList", boardList);
return "crud/list";
}
@RequestMapping(value = "/read", method = RequestMethod.GET)
public String crudRead(int boardNo, Model model) {
log.info("crudRead() 실행...!");
Board board = service.read(boardNo);
model.addAttribute("board", board);
return "crud/read";
}
@RequestMapping(value = "/modify", method = RequestMethod.GET)
public String crudModifyForm(int boardNo, Model model) {
log.info("crudModifyForm() 실행...!");
Board board = service.read(boardNo);
model.addAttribute("board", board);
model.addAttribute("status", "u"); // '수정을 진행합니다' 라는 flag
return "crud/register";
}
@RequestMapping(value = "/modify", method = RequestMethod.POST)
public String crudModify(Board board, Model model) {
log.info("crudModify() 실행...!");
service.modify(board);
model.addAttribute("msg", "수정이 완료되었습니다.");
return "crud/success";
}
@RequestMapping(value = "/remove", method = RequestMethod.POST)
public String crudDelete(int boardNo, Model model) {
log.info("crudDelete() 실행...!");
service.remove(boardNo);
model.addAttribute("msg", "삭제가 완료되었습니다.");
return "crud/success";
}
@RequestMapping(value = "/search", method = RequestMethod.POST)
public String crudSearch(String title, Model model) {
log.info("crudSearch() 실행...!");
Board board = new Board();
board.setTitle(title);
List<Board> boardList = service.search(board);
model.addAttribute("boardList", boardList);
model.addAttribute("board", board);
return "crud/list";
}
}
- http://localhost/crud/board/register
@Transactional
@Override
public void register(Board board) throws IOException {
log.info("BoardServiceImpl register 실행...!");
mapper.create(board);
// CheckedException 계열로 롤백 처리가 되지 않는다.
// if(true)
// throw new IOException();
if(true)
throw new NullPointerException();
}
@Transactional(rollbackFor = IOException.class)
@Override
public void register(Board board) throws IOException {
log.info("BoardServiceImpl register 실행...!");
mapper.create(board);
// CheckedException 계열로 롤백 처리가 되지 않는다.
if(true)
throw new IOException();
// RuntimeException 계열에 해당하는 에러는 롤백 처리가 가능하다.
// if(true)
// throw new NullPointerException();
}
@Service
public class MemberServiceImpl implements IMemberService {
@Inject
private IMemberMapper mapper;
@Transactional
@Override
public void register(CrudMember member) {
// 회원 1명의 정보를 등록 시, 하나의 권한을 가질 수 있다.
mapper.create(member); // 회원정보 1명의 데이터를 등록
// 등록된 회원정보를 이용해서 권한을 등록
CrudMemberAuth memberAuth = new CrudMemberAuth();
memberAuth.setUserNo(member.getUserNo());
memberAuth.setAuth("ROLE_USER");
if(true) {
throw new NullPointerException();
}
mapper.createAuth(memberAuth);
}
- http://localhost/crud/member/register
'대덕인재개발원 > 대덕인재개발원_웹기반 애플리케이션' 카테고리의 다른 글
231208_SPRING 2 (13-2) (0) | 2023.12.08 |
---|---|
231208_SPRING 2 (13-1) (0) | 2023.12.08 |
231207_SPRING 2 (12-1) (1) | 2023.12.07 |
231206_SPRING 2 (11-3 : 과제 _ 아이디, 비밀번호 찾기) (0) | 2023.12.06 |
231206_SPRING 2 (11-2) (1) | 2023.12.06 |