결국 회원A가 책 2권 산 것과 회원B가 책 두 권 산 것으로 2개의 주문 데이터를 만드는 작업입니다.
package jpabook.jpashop;
@Component
@RequiredArgsConstructor
public class InitDb {
private final InitService initService;
@PostConstruct
public void init() {
initService.dbInit1();
initService.dbInit2();
}
@Component //...2
@Transactional
@RequiredArgsConstructor
static class InitService {
private final EntityManager em;
public void dbInit1() {
Member member = createMember("userA", "서울", "1", "1111");
em.persist(member);
Book book1 = createBook("JPA1 BOOK", 10000, 100);
em.persist(book1);
Book book2 = createBook("JPA2 BOOK", 20000, 100);
em.persist(book2);
//...1
OrderItem orderItem1 = OrderItem.createOrderItem(book1, 10000, 1);
OrderItem orderItem2 = OrderItem.createOrderItem(book2, 20000, 2);
Order order = Order.createOrder(member, createDelivery(member), orderItem1, orderItem2);
em.persist(order);
}
public void dbInit2() {
Member member = createMember("userB", "진주", "2", "2222");
em.persist(member);
Book book1 = createBook("SPRING1 BOOK", 20000, 200);
em.persist(book1);
Book book2 = createBook("SPRING2 BOOK", 40000, 300);
em.persist(book2);
OrderItem orderItem1 = OrderItem.createOrderItem(book1, 20000, 3);
OrderItem orderItem2 = OrderItem.createOrderItem(book2, 40000, 4);
Order order = Order.createOrder(member, createDelivery(member), orderItem1, orderItem2);
em.persist(order);
}
private Member createMember(String name, String city, String street, String zipcode) {
Member member = new Member();
member.setName(name);
member.setAddress(new Address(city, street, zipcode));
return member;
}
private Book createBook(String name, int price, int stockQuantity) {
Book book = new Book();
book.setName(name);
book.setPrice(price);
book.setStockQuantity(stockQuantity);
return book;
}
private Delivery createDelivery(Member member) {
Delivery delivery = new Delivery();
delivery.setAddress(member.getAddress());
return delivery;
}
}
}
내부 클래스는 명시적으로 @Component 어노테이션으로 지정해야만 스프링 빈으로 등록됩니다.
initService를 스프링 빈으로 등록하여 @PostConstruct로 인해 (의존관계 주입이 끝난 후 실행) initService.dbInit1();
을 호출할 수 있습니다.
서버가 뜰 때 스프링의 컴포넌트 스캔으로 스프링 빈이 다 엮이고 나서 스프링 빈이 등록 되면 스프링이 @PostConstruct 를 실행시킵니다. 그래서 init() 이 호출되면서 데이터가 저장됩니다.
참고로 application.yml 에 ddl-auto 는 create 로 바꾸는게 좋습니다.
회원 조회 API 를 만들면서 데이터 변경이나 테이블 변경이 없을거라 ddl-auto 가 none 으로 바꿨었는데요, @PostConstruct 는 서버가 뜰 때마다 실행이 됩니다. 그러니까 서버가 뜰 때마다 ddl-auto 가 none 이므로 매번 동일한 데이터가 반복해서 들어갈겁니다.
이를 방지하기 위해서 매번 테이블을 삭제하고 새로 만드는 create 설정으로 변경하는겁니다.
근데 의문이 하나 생길 수 있습니다.
왜 굳이 InitService 란 내부 클래스를 만들어서 빈으로 등록하고 InitService 의 메서드를 호출했을까요?
@PostConstructor 는 빈 생성만을 완료한 이후 호출이 됩니다. 해당 빈에 관련된 AOP등을 포함한, 전체 스프링 애플리케이션 컨텍스트가 초기화 된 것을 의미하지는 않습니다.
트랜잭션을 처리하는 AOP등은 스프링의 후 처리기(post processer)가 완전히 동작을 끝내서, 스프링 애플리케이션 컨텍스트의 초기화가 완료되어야 적용됩니다.
정리하면 @PostConstruct는 해당빈의 AOP 적용을 보장하지 않습니다.