beadss / jpa-study

jpa슽터디입니다
1 stars 2 forks source link

13장 정리 #36

Open 2xel opened 5 years ago

2xel commented 5 years ago

13. 웹 애플리케이션과 영속성 관리

1. 트랜잭션 범위의 영속성 컨텍스트

1.1 스프링 컨테이너의 기본 전략

@Controller
clas helloController {
    @Autowired HelloServce HelloService;

    pulbic void hello() {
        Member member = helloService.logic();
    }
}

@Service
class HelloService {
    @PersistenceContext EntityManager em;

    @AutoWired Repository1 repository1;
    @Autowired Repository2 repository2;

    @Transactional
    public void logic() {
        repository.hello();
        Member member = repository2.findMember();
        return member;
    }
}

@Repository
class Repository1 {
    @PersistenceContext EntityManager em;

    public void hello() {
        em.xxx();
    }
}

@Repository
class Repository2 {
    @PersistenceContext EntityManager em;

    public Member findMember() {
        return em.find(Member.class, "id1")
    }
}

트랜잭션이 같으면 같은 영속성 컨텍스트를 사용한다.

트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다.

2. 준영속 상태와 지연 로딩

@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne(fetch=FetchType.LAZY)
    private Member member;
}

class OrderController {
    public String view(Long orderId) {
        Order order = orderService.findOne(orderId);
        Member member = order.getMember();
        member.getName();   // 지연로딩시 예외발생
    }
}

준영속 상태와 변경 감지

준영속 상태와 지연 로딩

2.1 글로벌 페치 전략 수정

@Entity
public class Order {
    @Id @GeneratedValue
    private Long id;

    @ManyToOne(fetch=FetchType.EAGER)   // 즉시 로딩
    private Member member;
}

글로벌 페치 전략에 즉시 로딩 사용 시 단점

2.2 JPQL 페치 조인

JPQL 페치 조인의 단점

2.3 강제로 초기화

class OrderService{
    @Transactional
    public Order findOrder(id){
        Order order = orderRepository.findOrder(id);
        order.getMember().getName();    // 프록시 객체를 강제로 초기화한다.
        return order;
    }
}
initialize(order.getMember());  // 프록시 초기화

// JPA는 초기화 여부만 확인 할 수 있다.
PersistenceUnitUtil persistenceUnitUtil = em.getEntityMangerFactory()
                                            .getPersistenceUnitUtil();

boolean isLoaded = persistenceUnitUtil.isLoaded(order.getMember());

2.4 FACADE 계층 추가

FACADE 계층의 역할과 특징

class OrderFacade {
    @Autowired OrderService orderService;

    public Order findOrder(id) {
        Order order = orderService.findOrder(id);
        // 프리젠테이션 계층이 필요한 프록시 객체를 강제 초기화
        order.getMember().getName();
        return order;
    }
}

class OrderService {
    public Order findOrder(id) {
        return orderRepository.findOrder(id);
    }
}

2.5 준영속 상태와 지연 로딩의 문제점

3. OSIV

3.1 과거 OSIV: 요청 당 트랜잭션

요청 당 트랜잭션 방식의 OSIV 문제점

엔티티를 읽기 전용 인터페이서로 제공

interface MemberView{
    public String getName();
}

@Entity
class Member implements MemberView {
    ....
}

class MemberService {
    public MemberView getMember(id) {
        return memberRepository.findById(id);
    }
}

엔티티 레핑

class MemberWrapper {
    private Member member;

    public MemberWrapper(member){
        this.member = member;
    }

    public String getName(){
        member.getName();
    }
}

DTO만 반환

class MemberDTO{
    private String name;
    ...
}

...
MemberDTO memberDTO = new MemberDTO();
memberDTO.setName(member.getName());
return MemberDTO;

정리

3.2 스프링 OSIV: 비즈니스 계층 트랜잭션

스프링 프레임워크가 제공하는 OSIV 라이브러리

스프링 OSIV 분석

트랜잭션 없이 읽기

스프링 OSIV 주의사항

class MemberController {
    public String viewMember(Long id){
        Member member = memberService.getMember(id);
        member.setName("XXX");

        memberService.biz();    // 비즈니스 로직
        return "view";
    }
}

class MemberService {
    @Transactional
    public void biz(){
        // ... 비즈니스 로직
    }
}
  1. 컨트롤러에서 회원 엔티티를 조회하고 이름을 member.setName("XXX")로 수정했다.
  2. biz() 메소드를 실행해서 트랜잭션이 있는 비즈니스 로직을 실행.
  3. 트랜잭션 AOP가 동작하면서 영속성 컨텍스트에 트랜잭션 시작 그리고 biz() 메소드를 실행한다.
  4. biz() 메소드가 끝나면 트랜잭션 AOP는 트랜잭션을 커밋하고 영속성 컨텍스트를 플러시한다. 이때 변경 감지가 동작하면서 회원 엔티티의 수정 사항을 데이터베이스에 반영한다.

3.3 OSIV 정리

스프링 OSIV의 특징

스프링 OSIV 단점

OSIV vs FACADE vs DTO

OSIV를 사용하는 방법이 만능은 아니다

OSIV는 같은 JVM을 벗어난 원격 상황에서는 사용할 수 없다.

4. 너무 엄격한 계층

class OrderController {
    @Autowired OrderService orderService;
    @Autowired orderRepsotory orderRepository;

    public String orderRequest(Order order, Model model) {
        long Id = orderService.order(order);    // 상품 구매

        // 리포지토리 직접 접근
        Order orderResult = orderRepository.findOne(id);
        model.addAttribute("order", orderResult);
        ...
    }  
}

@Transactional
class OrderService {
    @Autowired OrderRepository orderRepository;

    public Long Order(order) {
        // ... 비즈니스 로직
        return orderRepository.save(order);
    }
}

class OrderRepository {
    @PersistenceContext EntityManager em;

    public Order findOne(Long id) {
        return em.find(Order.class, id);
    }
}