LUMI_dev
5. 연관관계 매핑 기초 본문
객체의 참조와 테이블의 외래키 매핑
연관관계의 주인 - C언어의 포인터 같음
1. 단방향 연관 관계
Member.java
@Entity
public class Member {
@Id @GeneratedValue // (@GeneratedValue는 auto 모드)
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
…
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
…
}
hellojpa 아래 Team 클래스 생성
Team.java
@Entity
public class Team{
@Id @GeneratedValue
@Column(name ="TEAM_ID")
private Long id;
private String name;
}
멤버 테이블에 팀원 아이디 값을 그대로 가지고 있음
문제) 테이블에 맞춰 외래키 값을 그대로 가지고 있는 것
객체를 테이블에 맞춰 모델링했을 때의 문제점
JpaMain
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
//Member1을 TeamA에 소속시키고 싶을 때
member.setTeamId(team.getId()); //객체지향스럽지 않음 - 아직 연관관계 맵핑을 안배워서 이렇게 우선
em.persist(member);
DB 결과
근데 이 부분이 애매함
//Member1을 TeamA에 소속시키고 싶을 때
member.setTeamId(team.getId()); //객체지향스럽지 않음
그래서 외래키 식별자를 직접 다룸
조회할 때도 문제 있음 (find로 member의 team을 가져오는 과정이 복잡함)
1. 멤버에서 getId로 멤버 아이디 찾고 2.그곳에서 해당하는 TeamId 찾고 3.Team클래스에서 팀을 찾아야 함
-> 이렇게 계속 JPA에게 묻고 DB에서 끄집어내는 것은 연관관계가 없기 때문
테이블의 외래키로, 객체는 참조로 연관된 테이블/객체를 찾음
위 표처럼 Team의 id가 아닌 Team의 참조값을 그대로 가져오는 것이 객체지향적인 방법
JpaMain.java
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
team 객체 자체를 저장함
만약 member1의 팀을 바꾸고 싶다면?
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
2. 양방향 연관 관계, 연관관계의 주인
Member.java
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team
멤버 입장에서는 N : 팀이 1
@ManyToOne 어노테이션으로 다대일 관계
@JoinColumn은 객체끼리 연결된 FK
ex) Member와 Team은 TEAM_ID라는 FK로 연결
Team에서도 Member를 조회하려면? (ex. member1이 속한 팀의 모든 팀원 목록을 보고 싶다?)
→ Team도 Member 객체를 넣어줘야 함
Team.java
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>(); //초기화를 시켜줘야 .add()를 했을 때 null이 안 떨어짐
…
}
mappedBy가 언제 쓰이는지 알아야 함
반대방향으로 객체 그래프 탐색
멤버 테이블에서 팀 테이블 조회하고 그 팀테이블에서 또 멤버 테이블 조회
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam().getMembers();
Team findTeam = findMember.getTeam().getMembers();
연관관계의 주인과 mappedBy
객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다
객체 연관관계는 실제로는 단방향 두 개를 가지고 억지로 양방향이라고 하는 것
테이블 연관관계는 양방향 하나
객체를 두 방향으로 만듬 (멤버에서도 팀을 조회, 팀에서도 멤버를 조회할 수 있게 했음)
이 둘 중 뭘로 맵핑해야 할까?
멤버의 팀 값을 바꾸거나 팀에 있는 멤버들을 업데이트하면 외래키 값이 업데이트 되어야 함
멤버를 바꾸고 싶으면 Member 테이블에서 바꿔야할까 Team에서 바꿔야할까?
양방향이 되면 양방향에 있는 members를 다 신경써야함
외래키는 둘 중 하나로 관리해야 함
두 개의 members 중 하나를 주인으로 정해야 함 = 연관관계의 주인 (Owner)
즉, 양방향일 때는 mappedBy를 사용하는데 주인이 아닌 것에 mappedBy를 지정해서 주인 관계 지정한다.
★ 주인만이 외래키를 관리 / 주인이 아니면 읽기만 가능
외래 키가 있는 곳 = 주인 (즉, 관계에서 n인 쪽을 주인으로 해라)
Member.team이 주인
Team.member은 가짜 매핑
Member.java (외래키를 가지는 쪽)
@JoinColumn(name="TEAM_ID") → 외래키를 관리한다는 의미 (주인)
DB만 봐도 Member이 FK를 가짐
Team.java
@OneToMany(mappedBy ="team") → 주인의 반대편 / 여기는 조회만 됨 (Team.getMembers)
값 변경은 안됨
3. 주의점, 정리
오답) 만약 team에만 member을 추가하면?
Team findTeam = findMember.getTeam().add(member);
//회원 저장
Member member = new Member();
member.setName("member1");
em.persist(member);
//팀 저장
Team team = new Team();
team.setName("TeamA");
Team findTeam = findMember.getTeam().add(member);
em.persist(team);
인서트 쿼리는 두번 실행됨
멤버 아이디는 있지만 팀 아이디는 null 이 뜸
정답) 연관관계 주인에게만 값을 넣고, 주인이 아니면 값 넣지 않기
JpaMain
주인에게만 값을 넣은 버전
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setUserName("member1");
member.setTeam(team);
em.persist(member);
em.flush();
em.clear();
tx.commit();
(member을 넣고 > team을 세팅하는 것 ㅇ)
위 코드는 JPA 기준으로는 맞지만
객체지향적으로 생각해보면 사실 양쪽에 다 값을 넣어주는 게 맞음
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setUserName("member1");
member.setTeam(team);
em.persist(member);
team.getMembers().add(member); //team에도 넣어줘야 할까?
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
for(Member m : members){
System.out.println("m = "+ m.getUsername());
}
tx.commit();
근데 team의 members에 값을 안 넣었는데도 값이 출력됨 = JPA의 지연로딩
굳이 값을 세팅해주지 않아도 SELECT로 요소 불러오는 순간에 FK 참고해서 가져옴
team.getMembers().add(member); //team에도 넣어줘야 할까?
양쪽 모두 값을 넣어주는 것이 맞다
안 넣어주면 두개의 문제 생기기 때문
1) 플러시하고 클리어 해버리면 문제 없음 (flush랑 clear로 값이 비면 다시 조회해옴)
그러나 플러시하고 클리어 안하면 값이 영속성 컨텍스트에만 남아있어서 출력이 안됨
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setUserName("member1");
member.setTeam(team);
em.persist(member);
team.getMembers().add(member); //team에도 넣어줘야 할까?
// em.flush();
// em.clear();
Team findTeam = em.find(Team.class, team.getId());
//1차 캐시, 영속성 컨텍스트에 위에 넣은 형태 그대로 들어가 있음
//flush 및 clear을 안하면 1차 캐시에서 줘야 되는게 그대로 튀어나오는 것
List<Member> members = findTeam.getMembers(); //그래서 이 컬렉션에 값이 없음, DB의 셀렉트 쿼리 안나감
System.out.println("================");
for(Member m : members){
System.out.println("m = "+ m.getUsername());
}
System.out.println("================");
tx.commit();
System.out.println("================"); 사이에는 값이 없음
→ 즉 영속성 컨텍스트에 그대로 들어가있는 것
2) 테스트 케이스를 작성할 때 JPA 없이도 순수 자바 코드 상태로 테스트 케이스 작성하기도 함
그 케이스에서도 멤버.get은 나와도 반대의 경우가 널이 나올 수 있음
근데 깜빡할 수 있음
<연관관계 편의 메서드>
아래 방법 둘 중 하나 쓰기
첫번째 방법. Member을 기준으로 team에 멤버를 add
Member.java
Team의 setter에 넣어주고 -> setTeam 이름을 changeTeam으로 바꿈
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
public void getId(){return id;}
public void setId(Long id){this.id = id;}
public String getUsername(){return username;}
public void setUsername(String username){this.username = username;}
public Team getTeam(){return team;}
public void changeTeam(Team team){ //원래 이름 setTeam
this.team = team;
team.getMembers().add(this);
}
JpaMain
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setUserName("member1");
member.changeTeam(team);
em.persist(member);
//team.getMembers().add(member); // -> 여기에선 빼주기
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
//1차 캐시, 영속성 컨텍스트에 위에 넣은 형태 그대로 들어가 있음
//flush 및 clear을 안하면 1차 캐시에서 줘야 되는게 그대로 튀어나오는 것
List<Member> members = findTeam.getMembers(); //그래서 이 컬렉션에 값이 없음, DB의 셀렉트 쿼리 안나감
System.out.println("================");
for(Member m : members){
System.out.println("m = "+ m.getUsername());
}
System.out.println("================");
tx.commit();
2번째 방법.Team을 기준으로 add하기
위의 changeTeam 메서드 삭제
JpaMain
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setUserName("member1");
//member.changeTeam(team); 여기서 이거 없애기
em.persist(member);
team.addMember(member); //추가
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
System.out.println("================");
for(Member m : members){
System.out.println("m = "+ m.getUsername());
}
System.out.println("================");
tx.commit();
Team.java
Member.java의 changeTeam 없애기
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>(); //초기화를 시켜줘야 .add()를 했을 때 null이 안 떨어짐
public void addMember(Member member){
member.setTeam(this);
members.add(member);
}
public Long getId(){return id;}
public void setId(){this.id = id;}
public String getName(){return name;}
public void setName(String name){this.name = name;}
public List<Member> getMembers(){
return members;
}
public void setMembers(List<Member> members){
this.members= members;
}
}
Member.java
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
public void getId(){return id;}
public void setId(Long id){this.id = id;}
public String getUsername(){return username;}
public void setUsername(String username){this.username = username;}
public Team getTeam(){return team;}
public void setTeam(Team team){this.team = team;}
toString(), Lombok이나 JSON 생성 라이브러리에서 문제됨
toString() 문제
setTeam 부분에 generate toString(); 하기
Member.java
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
public void getId(){return id;}
public void setId(Long id){this.id = id;}
public String getUsername(){return username;}
public void setUsername(String username){this.username = username;}
public Team getTeam(){return team;}
public void setTeam(Team team){this.team = team;}
@Override
public String toString(){
return "Member{" +
"id="+id +
", username ='"+ username+'\''+
", team=" + team +
'}';
}
}
Team.java에서도 toString (alt+insert)해두기
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>(); //초기화를 시켜줘야 .add()를 했을 때 null이 안 떨어짐
public void addMember(Member member){
member.setTeam(this);
members.add(member);
}
public Long getId(){return id;}
public void setId(){this.id = id;}
public String getName(){return name;}
public void setName(String name){this.name = name;}
public List<Member> getMembers(){
return members;
}
public void setMembers(List<Member> members){
this.members= members;
}
@Override
public String toString(){
return "Team{" +
"id=" + id +
", name ='"+ name + '\''+
",members=" + members +
'}';
}
}
이 toString()은 각 컬렉션 안에 있는 toString을 호출
JpaMain
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setUserName("member1");
//member.changeTeam(team); 여기서 이거 없애기
em.persist(member);
team.addMember(member); //추가
em.flush();
em.clear();
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();
System.out.println("================");
System.out.println("members =" + findTeam); //이제 이걸 호출하면 무한 루프
System.out.println("================");
tx.commit();
결과 : 스택 오버 플로우 에러 발생
JSON 생성 라이브러리는 언제 실제 운영에서 문제가 생기냐?
엔티티를 직접 컨트롤러에서 리턴할 때,
이 엔티티가 가진 연관관계가 양방향으로 걸려있으면 문제 발생
Lombok에서 toString 만드는 건 웬만하면 쓰지 말기
컨트롤러에서는 절대 엔티티를 반환하지 말기
컨롤러에서 엔티티를 반환하면 스프링이 요즘 잘해줘서 JSON으로 출력함
근데 엔티티 자체를 JSON으로 API 스펙에 반환해버리면 2가지 문제가 생김
1. 무한 루프 발생
2. 엔티티가 충분히 변경될 수 있으니까 여러가지 이유로 필드가 추가될 수 있고
엔티티를 API에 반환해버리면 나중에 그 엔티티를 변경하는 순간 API 스펙이 바뀌어버리는 것
그래서 Entity는 웬만하면 단순하게 값만 있는 DTO를 사용해야 함 (값이 나갈 때, 들어올 때)
단방향 매핑으로 처음에 설계를 끝내야 함 - 양방향으로 하면 안됨
실무에서 객체만으로 설계하기 어려움
테이블 설계를 어느정도 머릿속에 그리면서 객체 설계를 동시에 진행
그럼 그 시점에는 테이블 관계에서 대략적인 외래키 다 나옴
N(다) 쪽에서 단방향 매핑을 설정해야 하는데
ManyToOne이나 OneToOne 관계에서는 초기에 절대 양방향 맵핑 사용하지 말기!!
처음에는 무조건 단방향 매핑으로 설계
양방향 매핑 = 반대 방향으로 조회 기능이 추가되는 것
JPA에서의 설계는 객체와 테이블 간의 매핑이 단방향 매핑만으로 이미 완료
사실 객체 관점에서는 양방향으로 설계해도 별로 이점이 없음
반대쪽 일대다 매핑이 언제 이뤄지냐면
생각보다 실무에서는 역방향으로 참조할 일이 많음
결론) 단방향 매핑을 잘 하고 (기본적으로 생각은 단방향 매핑으로 다 끝낸다고 가져가야 함)
일대다 양방향 매핑 mappedBy는 꼭 필요할 때 중간 추가 (어차피 코드 몇줄 되지 않으니)
→ 테이블에 영향을 주지 않음
비즈니스 로직을 기준으로 연관관계 주인을 선택하면 안됨
실전 예제 2. 연관관계 매핑 시작
- 실전 예제 1번에 연관관계 매핑을 씌움
외래키 가진 ORDERS를 연관관계 주인으로
Order.java
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "ORDERS") //sql에서 order by와 헷갈릴 수 있어서 orders를 많이 씀
public class Order {
//Order은 set을 넣으면 유지보수성이 떨어질 수 있음 - 아무데서나 바꿀 수 있으므로 좀 그렇다.
//가급적 생성자에서 값을 다 세팅하고 세터의 사용을 최소화하기
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
//@Column(name = "MEMBER_ID")
//private Long memberId;
@ManyToOne
@JoinColumn (name="MEMBER_ID")
private Member member;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; //ENUM으로 만들기
public OrderStatus getStatus() {
return status;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
}
가급적 단방향 매핑이 좋으므로 일단 단방향으로 설계
양방향은 복잡도가 높아짐
OrderItem.java
package jpabook.jpashop.domain;
import javax.persistence.*;
@Entity
public class OrderItem {
@Id @GeneratedValue
@Column (name = "ORDER_ITEM_ID")
private Long id;
/* @Column (name = "ORDER_ID")
private Long orderId;*/
@ManyToOne
@JoinColumn (name = "ORDER_ID")
private Order order;
/*@Column (name = "ITEM_ID")
private Long itemId;*/
@ManyToOne
@JoinColumn (name = "ITEM_ID")
private Item item;
private int orderPrice;
private int count;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
int getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(int orderPrice) {
this.orderPrice = orderPrice;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
양방향 맵핑 고려
상황) 멤버 입장에서 어떤 오더스 주문 목록이 중요해짐
이때 멤버의 orders를 넣는게 좋은 설계가 아님
특정 회원의 주문 내역을 보고 싶음
그럼 이미 ORDERS에 member_id라는 foreign key 값에 멤버 아이디가 들어가 있음
잘못된 설계) MEMBER에서 멤버를 찾아서 그 멤버를 가지고 get Orders해서 주문 내역을 뿌리는 것
주문이 필요하면 주문으로부터 시작
멤버가 굳이 orders를 알 필요는 없음
양방향 맵핑 후
Member.java
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private String city;
private String street;
private String zipcode;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<Order>(); //이건 잘못된 코드라고 생각함 //차라리 멤버 조회하고 Order 따로 조회하지
//이쪽이 1이니까 상대 orders는 컬렉션
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
}
Order.java
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "ORDERS") //sql에서 order by와 헷갈릴 수 있어서 orders를 많이 씀
public class Order {
//Order은 set을 넣으면 유지보수성이 떨어질 수 있음 - 아무데서나 바꿀 수 있으므로 좀 그렇다.
//가급적 생성자에서 값을 다 세팅하고 세터의 사용을 최소화하기
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
//@Column(name = "MEMBER_ID")
//private Long memberId;
@ManyToOne
@JoinColumn (name="MEMBER_ID")
private Member member;
@OneToMany (mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; //ENUM으로 만들기
public OrderStatus getStatus() {
return status;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
}
OrderItem.java
package jpabook.jpashop.domain;
import javax.persistence.*;
@Entity
public class OrderItem {
@Id @GeneratedValue
@Column (name = "ORDER_ITEM_ID")
private Long id;
/* @Column (name = "ORDER_ID")
private Long orderId;*/
@ManyToOne
@JoinColumn (name = "ORDER_ID")
private Order order;
/*@Column (name = "ITEM_ID")
private Long itemId;*/
@ManyToOne
@JoinColumn (name = "ITEM_ID")
private Item item;
private int orderPrice;
private int count;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
int getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(int orderPrice) {
this.orderPrice = orderPrice;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
orderItem에 order 넣기
첫번째 방법. addOrderItem 메서드 만들기
JpaMain
package jpabook;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderItem;
import jpabook.jpashop.domain.Team;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpashop");//persistenceUnitName을 넘기라고 함 - persistence.xml의 <persistence-unit name="hello"> 부분임
//이걸 연결하는 순간 데이터베이스랑 연결됨
//create-entity-manger 꺼내기 - 데이터에서 커넥션 하나 받은 것임
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Order order = new Order();
order.addOrderItem(new OrderItem());
tx.commit();
//커밋
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
Order.java
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "ORDERS") //sql에서 order by와 헷갈릴 수 있어서 orders를 많이 씀
public class Order {
//Order은 set을 넣으면 유지보수성이 떨어질 수 있음 - 아무데서나 바꿀 수 있으므로 좀 그렇다.
//가급적 생성자에서 값을 다 세팅하고 세터의 사용을 최소화하기
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
//@Column(name = "MEMBER_ID")
//private Long memberId;
@ManyToOne
@JoinColumn (name="MEMBER_ID")
private Member member;
@OneToMany (mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<OrderItem>();
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; //ENUM으로 만들기
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this); //현재 나의 order 넣음
}
public OrderStatus getStatus() {
return status;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
}
2번째 방법.
JpaMain
package jpabook;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderItem;
import jpabook.jpashop.domain.Team;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpashop");//persistenceUnitName을 넘기라고 함 - persistence.xml의 <persistence-unit name="hello"> 부분임
//이걸 연결하는 순간 데이터베이스랑 연결됨
//create-entity-manger 꺼내기 - 데이터에서 커넥션 하나 받은 것임
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Order order = new Order();
OrderItem orderItem = new OrderItem();
orderItem.setOrder(order);
em.persist(orderItem);
tx.commit();
//커밋
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
'JPA > 자바 ORM 표준 JPA 프로그래밍 - 기본편' 카테고리의 다른 글
4. 엔티티 매핑 (0) | 2025.03.23 |
---|---|
3. 영속성 관리(JPA 내부 구조)_영속성 컨텍스트 (0) | 2025.03.09 |
2. Hello JPA 실습_ H2 데이터베이스 설치와 실행, JPA와 JPQL기본(EntityManagerFactory,@Id,@Table 등 ) (1) | 2025.03.08 |
1. JPA란? (2) | 2025.03.03 |