스프링부트와 JPA를 활용해 웹 애플리케이션을 개발한다.
다음 코드 및 진행 방법은 인프런 김영한 강사님의 유로 강의 내용을 발췌한 내용이다.
강의 : <실전! 스프링 부트와 JPA 활용 1 - 웹 애플리케이션 개발>
출처 : https://inf.run/zzKt
- JPA와 DB 설정 및 동작 확인 * 띄어쓰기 주의!
spring: datasource: url: jdbc:h2:tcp://localhost/~/jpashop; username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: # show_sql: true format_sql: true logging.level: org.hibernate.SQL: debug org.hibernate.orm.jdbc.bind: trace ``
@Repository
public class MemberRepository {
@PersistenceContext
EntityManager em;
public Long save(Member member) {
em.persist(member);
return member.getId();
}
public Member find(Long id) {
return em.find(Member.class, id);
}
}
ORDER가 아닌 ORDERS로 쓰는 이유는 예약어 ORDER가 있기 때문에 관례상 ORDERS로 사용한다.
싱글 테이블 전략을 사용해 자식 테이블을 부모 테이블에 다 넣어 한 개 테이블만 사용하고, 구분 컬럼(DTYPE)을 이용해 어떤 자식 데이터가 저장되었는지 확인한다.
객체는 컬랙션을 이용해 N:M 관계로 바로 연결할 수 있지만 관계형 데이터는 일반적인 설계로 안되고 매핑 테이블을 두고 1:N & N:1로 연결해야된다.
1:N 또는 N:1의 양방향 관계면 연관 관계 주인을 정해야 한다.
주로 다방향 부분에 외래키가 존재하는데 외래키가 있는 다방향을 주인으로 정하는게 좋다.
Entity 설계 시 유지보수 및 변경 포인트를 줄이기 위해 가급적 Setter를 사용하지 않는것이 좋다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {
@Id @GeneratedValue
@Column(name = "item_id")
private Long id;
private String name;
private int price;
private int stockQuantity;
@ManyToMany(mappedBy = "items")
private List<Category> categories = new ArrayList<Category>();
}
@Entity
@Table(name = "orders")
@Getter @Setter
public class Order {
@Id @GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member; //주문 회원
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<OrderItem> orderItems = new ArrayList<>();
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "delivery_id")
private Delivery delivery; //배송정보
private LocalDateTime orderDate; //주문시간
@Enumerated(EnumType.STRING)
private OrderStatus status; //주문상태 [ORDER, CANCEL]
//==연관관계 메서드==//
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public void setDelivery(Delivery delivery) {
this.delivery = delivery;
delivery.setOrder(this);
}
}
@Service
@Transactional(readOnly = true)
public class MemberService {
@Autowired
MemberRepository memberRepository;
/**
* 회원가입
*/
@Transactional //변경
public Long join(Member member) {
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
List<Member> findMembers =
memberRepository.findByName(member.getName());
if (!findMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers() {
return memberRepository.findAll();
}
public Member findOne(Long memberId) {
return memberRepository.findOne(memberId);
}
}
@Embeddable : 값 타입 정의하는 곳
@Embedded : 값 타입 사용하는 곳