분류 전체보기 1341

Ch04. 스프링과 문제 해결(트랜잭션) - 트랜잭션 문제 해결(트랜잭션 템플릿)

반복되는 코드 트랜잭션을 시작하고, 비즈니스 로직을 실행하고, 성공하면 커밋하고, 예외가 발생해서 실패하면 롤백한다. 다른 서비스에서 트랜잭션을 시작하려면 try , catch , finally를 포함한 성공 시 커밋, 실패 시 롤백 코드가 반복될 것이다. 이런 형태는 각각의 서비스에서 반복된다. 달라지는 부분은 비즈니스 로직뿐이다. 이럴 때 템플릿 콜백 패턴을 활용하면 이런 반복 문제를 깔끔하게 해결할 수 있다. 스프링은 TransactionTemplate라는 템플릿 클래스를 제공한다. TransactionTemplate public class TransactionTemplate { private PlatformTransactionManager transactionManager; public T exe..

Ch04. 스프링과 문제 해결(트랜잭션) - 트랜잭션 문제 해결(트랜잭션 매니저)

MemberRepositoryV3 /** * 트랜잭션 - 트랜잭션 매니저 DataSourceUtils.getConnection() DataSourceUtils.releaseConnection() */ @Slf4j public class MemberRepositoryV3 { private final DataSource dataSource; public MemberRepositoryV3(DataSource dataSource) { this.dataSource = dataSource; } public Member save(Member member) throws SQLException { String sql = "insert into member(member_id, money) values (?, ?)"; Co..

Ch01. 프로젝트 환경 설정 - 예제 도메인 모델과 동작확인

Member Entity @Entity @Getter @Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @ToString(of = {"id","username","age"}) public class Member { @Id @GeneratedValue @Column(name = "member_id") //DB테이블에 Member_id로 메핑 private Long id; private String username; private int age; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "team_id") private Team team; /* protected Member(){ //JPA 표준 스..

Ch04. 스프링과 문제 해결(트랜잭션) - 트랜잭션 동기화

스프링이 제공하는 트랜잭션 매니저는 크게 2가지 역할을 한다. 트랜잭션 추상화 리소스 동기화 리소스 동기화 트랜잭션을 유지하려면 트랜잭션의 시작부터 끝까지 같은 데이터베이스 커넥션을 유지해야 한다. 결국 같은 커넥션을 동기화(맞추어 사용) 하기 위해서 이전에는 파라미터로 커넥션을 전달하는 방법을 사용했다. 파라미터로 커넥션을 전달하는 방법은 코드가 지저분해지는 것은 물론이고, 커넥션을 넘기는 메서드와 넘기지 않는 메서드를 중복해서 만들어야 하는 등 여러 가지 단점들이 많다. 스프링은 트랜잭션 동기화 매니저를 제공한다. 이것은 쓰레드 로컬( ThreadLocal )을 사용해서 커넥션을 동기화해준다. 트랜잭션 매니저는 내부에서 이 트랜잭션 동기화 매니저를 사용한다. 트랜잭션 동기화 매니저는 쓰레드 로컬을 사..

Ch04. 스프링과 문제 해결(트랜잭션) - 트랜잭션 추상화

현재 서비스 계층은 트랜잭션을 사용하기 위해서 JDBC 기술에 의존하고 있다. 향후 JDBC에서 JPA 같은 다른 데이터 접근 기술로 변경하면, 서비스 계층의 트랜잭션 관련 코드도 모두 함께 수정해야 한다. 구현 기술에 따른 트랜잭션 사용법 트랜잭션은 원자적 단위의 비즈니스 로직을 처리하기 위해 사용한다. 구현 기술마다 트랜잭션을 사용하는 방법이 다르다. JDBC : con.setAutoCommit(false) JPA : transaction.begin() 트랜잭션을 사용하는 코드는 데이터 접근 기술마다 다르다. 만약 다음 그림과 같이 JDBC 기술을 사용하고, JDBC 트랜잭션에 의존하다가 JPA 기술로 변경하게 되면 서비스 계층의 트랜잭션을 처리하는 코드도 모두 함께 변경해야 한다. JDBC -> J..

Ch04. 스프링과 문제 해결(트랜잭션) - 문제점들

애플리케이션 구조 프레젠테이션 계층 UI와 관련된 처리 담당 웹 요청과 응답 사용자 요청을 검증 주 사용 기술: 서블릿과 HTTP 같은 웹 기술, 스프링 MVC 서비스 계층 비즈니스 로직을 담당 주 사용 기술: 가급적 특정 기술에 의존하지 않고, 순수 자바 코드로 작성 데이터 접근 계층 실제 데이터베이스에 접근하는 코드 주 사용 기술: JDBC, JPA, File, Redis, Mongo 순수한 서비스 계층 핵심 비즈니스 로직이 들어있는 서비스 계층이 가장 중요하다. 시간이 흘러서 UI(웹)와 관련된 부분이 변하고, 데이터 저장 기술을 다른 기술로 변경해도, 비즈니스 로직은 최대한 변경 없이 유지되어야 한다 이렇게 하려면 서비스 계층을 특정 기술에 종속적이지 않게 개발해야 한다. 이렇게 계층을 나눈 이유..

Ch05. API 개발 고급(실무 필수 최적화) - OSIV와 성능 최적화

OSIV와 성능 최적화 Open Session In View: 하이버네이트 Open EntityManager In View: JPA (관례상 OSIV라 한다.) OSIV ON spring.jpa.open-in-view : true 기본값 초기 설정을 하지 않고 application을 실행만 하여도 WARN 경고가 발생한다. 기본값을 뿌리면서 애플리케이션 시작 시점에 warn 로그를 남기는 것은 이유가 있다. OSIV 전략은 트랜잭션 시작처럼 최초 데이터베이스 커넥션 시작 시점부터 API 응답이 끝날 때까지 영속성 콘텍스트와 데이터베이스 커넥션을 유지한다. 그래서 지금까지 View Template이나 API 컨트롤러에서 지연 로딩이 가능했던 것이다. Transaction이 끝나더라도, 프록시 객체를 채워야..

Ch01. 프로젝트 환경설정 - H2 데이터베이스, JPA와 DB 설정 동작 확인

H2 설치하기 윈도우 설치 버전 https://h2database.com/h2-setup-2019-10-14.exe 윈도우, 맥, 리눅스 실행 버전 https://h2database.com/h2-2019-10-14.zip JPA와 DB 설정 동작 확인 Application.yml 설정 JPA 관련 설정을 한다. spring.jpa.hibernate.ddl-auto: create 이 옵션은 애플리케이션 실행 시점에 테이블을 drop 하고, 다시 생성한다 show_sql : 옵션은 System.out 에 하이버네이트 실행 SQL을 남긴다. show_sql 옵션은 System.out 에 하이버네이트 실행 SQL을 남긴다. org.hibernate.SQL : 옵션은 logger를 통해 하이버네이트 실행 SQL을..

Ch03. 트랜잭션 이해 - 트랜잭션(적용 후)

트랜잭션은 비즈니스 로직이 있는 서비스 계층에서 시작해야 한다. 비즈니스 로직이 잘못되면 해당 비즈니스 로직으로 인해 문제가 되는 부분을 함께 롤백해야 하기 때문이다. 그런데 트랜잭션을 시작하려면 커넥션이 필요하다. 결국 서비스 계층에서 커넥션을 만들고, 트랜잭션 커밋 이후에 커넥션을 종료해야 한다. 애플리케이션에서 DB 트랜잭션을 사용하려면 트랜잭션을 사용하는 동안 같은 커넥션을 유지해야 한다. 그래야 같은 세션을 사용할 수 있다. 애플리케이션에서 같은 커넥션을 유지하려면 어떻게 해야할까? 가장 단순한 방법은 커넥션을 파라미터로 전달해서 같은 커넥션이 사용되도록 유지하는 것이다. 먼저 리포지토리가 파라미터를 통해 같은 커넥션을 유지할 수 있도록 파라미터를 추가하자 MemberRepositoryV2 /*..

Ch03. 트랜잭션 이해 - 트랜잭션(적용 전)

MemberServiceV1 @RequiredArgsConstructor public class MemberServiceV1 { private final MemberRepositoryV1 memberRepository; public void accountTransfer(String fromId, String toId, int money) throws SQLException { Member fromMember = memberRepository.findById(fromId); Member toMember = memberRepository.findById(toId); memberRepository.update(fromId, fromMember.getMoney() - money); validation(toMem..