새로운 멤버 정보를 빈 객체를 생성한 뒤, 폼에서 입력받은 이름 값과 생성된 주소값을 설정한다.
생성된 멤버 값은 멤버 서비스 계층에서 만든 회원가입 join 메서드를 활용해 멤버 서비스에 보내 DB에 반영시킨다.
화면은 redirect:/를 통해 홈화면으로 돌아간다.
Address address = new Address(form.getCity(), form.getStreet(),
form.getZipcode());
Member member = new Member();
member.setName(form.getName());
member.setAddress(address);
memberService.join(member);
return"redirect:/";
}
=> 도메인 모델 패턴을 통해 비즈니스 로직을 구현하는 메서드를 엔터티에서 사용하고 있기 때문에, 서비스 계층은 구현이 아닌 로직을 호출하고 트랜잭션을 관리하여, DB에 반영하는 역할을 수행한다.
=> 이후, 도메인 주도 설계 DDD에 대해 공부할 것.
회원id, 상품id, 수량을 변수로 받아 id값을 통해 리포지토리에서 반환했던 엔터티 값을 조회한다.
빈 배송정보 객체 생성한 뒤, 받아온 회원 엔터티의 주소 정보를 배송정보의 주소정보로 설정한다.
도메인에서 생성한 비즈니스 로직인 주문상품과 주문을 생성한다.
리포지토리에 구현된 주문 정보를 저장하고 id값을 반환한다.
//주문@Transactionalpublic Long order(Long memberId, Long itemId, int count){
//엔티티 조회
Member member = memberRepository.findOne(memberId);
Item item = itemRepository.findOne(itemId);
//배송정보 생성
Delivery delivery = new Delivery();
delivery.setAddress(member.getAddress());
//주문상품 생성
OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);
//주문 생성
Order order = Order.createOrder(member, delivery, orderItem);
//주문 저장
orderRepository.save(order);
return order.getId();
}
public Item findOne(Long id){
return em.find(Item.class, id);
}
public List<Item> findAll(){
return em.createQuery("select i from Item i",Item.class).getResultList();
}
save를 통해 전달 받은 Member 객체를 persist메서드를 사용하여 영속성 컨텍스트에 저장한다.
이후 서비스 계층에서 트랜잭션이 커밋되면, DB에 반영된다.
2. findOne(Long id) 메서드:
Member의 주어진 id를 찾는 메서드이다.
1차적으로 영속성 컨텍스트내의 캐시에서 조회를 한 후, 없다면 JPA가 직접 데이터베이스에 select구문을 만들어 조회한다. 이는 id가 식별자이기 때문에 가능한 것이다.
em.find(클래스, 기본키)
3. findAll() 메서드:
DB에 저장된 모든 회원을 List로 반환한다.
select m from Member m을 통해 모든 회원을 조회하는 쿼리 구문을 생성한다.
select 쿼리가 실행된 후 getResultList를 통해 리스트로 받아온다.
4. findByName(String name) 메서드:
매개변수인 이름값으로 조회하는 방법
이름에 들어갈 내용은 동적인 데이터이기 때문에 setParameter 매개변수 바인딩을 사용해 안정적으로 제공한다.
예를 들면,
select m from Member m where m.name = 'John' 이 쿼리에서는 .setParameter("name", John)이 된다.
publicvoidsave(Member member){
em.persist(member);
}
public Member findOne(Long id){
return em.find(Member.class, id);
}
public List<Member> findAll(){
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
public List<Member> findByName(String name){
return em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
}
}
회원 서비스 개발
@Service 서비스 계층임을 나타내며, readOnly = true를 통해 읽기 전용으로 사용된다.
즉, 데이터의 수정은 일어나지 않음을 의미한다.
@RequiredArgsConstructor를 통해 생성자를 자동으로 생성하여, ItemRepository를 주입받는다.
(final 또는 @NonNull로 표시된 필드를 사용하여, 생성자를 만든다.)
생성자 주입을 하는 이유:
리포지토리에서 구현된 회원 정보를 외부에서 받아와 사용하기 때문에 유연성과 재사용성의 장점을 가진다.
새로운 회원정보가 데이터베이스에 저장되고 회원id가 반환되어야 클라이언트가 이 정보를 활용할 수 있다.
로그인, 회원 정보 수정 등에 활용된다.
//회원가입@Transactionalpublic Long join(Member member){
validateDuplicateMember(member);
memberRepository.save(member);
return member.getId();
}
privatevoidvalidateDuplicateMember(Member member){
List<Member> findMembers = memberRepository.findByName(member.getName());
if (!findMembers.isEmpty()) {
thrownew IllegalStateException("이미 존재하는 회원입니다.");
}
}
회원조회는 사용하지 않고 회원가입에만 Transactional이 사용된 이유:
Transactional을 사용하는 이유는 트랜잭션을 통해 리포지토리에서 구현한 정보를 DB에 반영하기 위함이다.
즉, 가입에는 새로운 회원 정보를 DB에 저장해야 하지만 회원 조회에서는 새로운 값을 넣는 것이 아닌 get을 통한 호출만 다루고 있기 때문에 Transactional이 필요하지 않다.
// 전체 회원 조회public List<Member> findMembers(){
return memberRepository.findAll();
}
public Member findOne(Long memberId){
return memberRepository.findOne(memberId);
}
}
가장 먼저 중심부를 보면 ORDERS와 ITEM이 놓여있다. 이로써 상품과 주문을 중심으로 비즈니스 로직이 돌아가는 프로젝트임을 알 수 있다.
1. 주문 클래스 위아래로 회원 클래스와 배송 클래스가 붙어 있음을 볼 수 있다.
- 회원 클래스는 일대다로 회원을 기준으로 여러 주문을 가질 수 있다. - 배송 클래스는 주문과 일대일 관계로 하나의 주문은 하나의 배송을 가진다.
2. 상품 클래스는 카테고리와 다대다 관계를 가진다.
그러나, 실제로 직접 다대다 연결을 할 수 없어 중간에 카테고리_상품 클래스를 두고 각각 다대일 관계를 맺어 연결한다.
3. 테이블 설계와 연관관계 매핑의 차이:
참조와 매핑이라는 단어를 생각해보면 이해가 쉽다. 공통적으로는 정보덩어리인 엔티티를 연결시킨다. 테이블 설계는 데이터베이스에서 테이블 간의 참조를 통한 관계 형성을 다루고, 연관관계 매핑은 객체간의 관계를 설정한다. 쉽게 테이블 참조는 비교적 간단한 관계 형성에 사용된다. 이를테면 회원과 주문이 있을 때, 이 주문이 어떤 회원의 것인지 알기 위해 회원의 고유키를 주문의 외래키로 참조하는 것이다. 반면, 객체의 관계에서는 좀더 복잡해진다. 주문과 배송은 각각 서로를 위해 존재한다. 하나의 주문에는 하나의 배송이 있어야 하고 하나의 배송에는 하나의 주문 정보가 필요하다. 반대로 회원과 주문처럼 한명의 회원이 여러 주문을 가지는 관계도 있다.