자바 ORM 표준 JPA 프로그래밍 - 기본편 수업을 듣고 정리한 내용입니다.
객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.
Direction
) : 단방향, 양방향Multiplicity
) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M) 이해Owner
) : 객체 양방향 연관관계는 관리 주인 이 필요
‘객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.’
–조영호(객체지향의 사실과 오해)
객체를 테이블에 맞추어 모델링하면 연관관계가 없는 객체가 된다.
→ 참조 대신, MEMBER
테이블의 외래 키, TEAM_ID
를 그대로 사용한다.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name="MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@Column(name="TEAM_ID")
private Long teamId;
~
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
~
}
Team
객체를 참조하지 않고, 외래 키 값 TEAM_ID
를 그대로 사용한다.create table Member{
MEMBER__ID bigint not null,
TEAM_ID bigint,
USERNAME varchar(255),
primary key (MEMBER_ID)
}
create table Team{
id bigint not null,
name varchar(255),
primary key (id)
}
//팀 저장
Team team = new Team();
team.setName("TeamA"); em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1"); member.setTeamId(team.getId()); em.persist(member);
member.setTeamId(team.getId());
: 외래 키 식별자를 직접 다룬다.
// 조회
Member findMember = em.find(Member.class, member.getId());
// 연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());
member
를 조회해왔음에도 불구하고 member
가 속한 팀을 알기 위해서member
가 가진 team.id
, 즉 식별자로 테이블을 검색해서 가져와야 한다.
⚠️ 문제점
객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.
- 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
- 객체는 참조를 사용해서 연관된 객체를 찾는다.
✔️ 객체 연관관계 사용
TEAM
의 id
가 아닌 TEAM
의 참조값을 그대로 가져온다.MEMBER
객체의 TEAM
레퍼런스와 MEMBER
테이블의 TEAM_ID(FK)
를 매핑해야 한다.
✔️ 객체의 참조와 테이블의 외래 키를 매핑
@Entity
public class Member {
@Id @GeneratedValue
@Column(name="MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
// @Column(name="TEAM_ID")
// private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
Member
: 다수, team
: 하나 → N:1
(다대일 관계)@JoinColumn(name = "TEAM_ID")
: 객체 연관관계 TEAM
참조와 테이블 연관관계 TEAM_ID
외래 키와 매핑 → 이는 ORM
매핑이 완성된 것이다.
Team
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
// getter, setter
...
}
}
✔️ ORM 매핑
✔️ 연관관계 저장
// 팀 저장
Team team = new Team();
team.setName("TeamA"); em.persist(team);
// 회원 저장
Member member = new Member(); member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장 em.persist(member);
member
에 team
참조를 저장한다.JPA
가 알아서 team
엔티티에서 pk
값을 추출해서 member
의 fk
로 설정한다.
실행 결과
Hibernate:
call next value for hibernate_sequence
Hibernate:
call next value for hibernate_sequence
Hibernate:
/* insert hellojpa.Team
*/ insert
into
Team
(name, TEAM_ID)
values
(?, ?)
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(TEAM_ID, USERNAME, MEMBER_ID)
values
(?, ?, ?)
✔️ 참조로 연관관계 조회 - 객체 그래프 탐색
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
✔️ 연관관계 수정
// 새로운 팀B
Team teamB = new Team(); teamB.setName("TeamB"); em.persist(teamB);
// 회원1에 새로운 팀B 설정 member.setTeam(teamB);
✔️ 양방향 객체 연관관계
현재, 객체 연관관계
Member.team
)Team.members
)
✔️ 테이블 연관관계
TEAM
테이블이 위처럼 되는 이유
TEAM
테이블의 TEAM_ID
로 MEMBER
테이블에 조인하면 된다.MEMBER
의 TEAM_ID
로 TEAM
테이블에 join
하면 된다.➡️ 따라서, 데이터베이스 테이블은 외래 키 하나로 양방향으로 조회할 수 있다.
Member
@Entity
public class Member {
@Id @GeneratedValue
@Column(name="MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
// Getter, Setter ...
}
Team
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
// Getter, Setter ...
}
List<Member> members
를 추가하였다.@OneToMany
매핑 정보를 사용하였다.@OneToMany
의 mappedBy
속성은 양방향 매핑일 때 사용하는데, 반대쪽 매핑의 필드 이름을 값으로 주면 된다.Member.team
이므로 team
을 값으로 주었다! (mappedBy
는 뒤에서 알아본다.)
일대다 방향으로 객체 그래프 탐색
try {
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
Member member2 = new Member();
member2.setName("member2");
member2.setTeam(team);
em.persist(member2);
em.flush();
em.clear();
//조회
Team team2 = em.find(Team.class, 1L);
System.out.println("team2 = " + team2);
List<Member> members = team2.getMembers(); // (팀 -> 회원) 객체 그래프 탐색
for (Member m : members) {
System.out.println("member.username = " + m.getName());
}
tx.commit();
member.username = 회원1
member.username = 회원2
Team
의 @OneToMany
에서 mappedBy
속성
➡️ 따라서, 테이블은 외래 키 하나만으로 양방향 연관관계를 맺는다!
✔️ 객체와 테이블이 관계를 맺는 차이
객체 연관관계 = 2개
테이블 연관관계 = 1개
➡️ 엔터티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나이다. 따라서 둘 사이에 차이가 발생한다.
그렇다면 둘 중 어떤 관계를 사용해서 외래 키를 관리해야할까?
: JPA에서는 두 객체 연관관계 중 하나를 정해서 테이블의 외래키를 관리해야 하는데 이것을 연관관계 주인이라고 한다.
✔️ 객체의 양방향 관계
A → B (a.getB())
B → A (b.getA())
class A{
B b;
}
class B{
A a;
}
✔️ 테이블의 양방향 연관관계
MEMBER.TEM_ID
외래 키 하나로 양방향 연관관계 가진다. (양쪽으로 조인할 수 있다.)SELECT *
FROM MEMBER M
JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
SELECT *
FROM TEAM T
JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID
✔️ 양방향 매핑의 규칙: 연관관계의 주인
양방향 연관관계 매핑 시 지켜야할 규칙 : 두 연관간계 중 하나를 연관관계의 주인으로 정해야한다.
연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)할 수 있다.
반면에 주인이 아닌 쪽은 읽기만 할 수 있다.
어떤 연관관계를 주인으로 정할지는 mappedBy
속성을 사용하면 된다!
mappedBy
속성을 사용하지 않는다.mappedBy
속성을 사용해서 속성의 값으로 연관관계의 주인을 지정해야 한다.그렇다면 Member.team, Team.members
둘 중 어떤 것을 연관간계의 주인으로 정해야할까?
회원 → 팀(Member.team
) 방향
class Member {
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
// ...
}
팀 → 회원(Team.members
) 방향
class Team {
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
// ...
}
🔔 연관관계의 주인을 정한다는 것은 사실 외래 키 관리자를 선택하는 것이다!
- 그림을 보면, 여기서는 회원 테이블에 있는
TEAM_ID
외래 키를 관리할 관리자를 선택해야 된다.- 만약 회원 엔티티에 있는
Member.team
을 주인으로 선택하면 자기 테이블에 있는 외래 키를 관리하면 된다.- 하지만 팀 엔티티에 있는
Team.members
를 주인으로 선택하면 물리적으로 전혀 다른 테이블의 외래 키를 관리해야 한다.- 왜냐하면 이 경우
Team.members
가 있는Team
엔티티는TEAM
테이블에 매핑되어 있는데 관리해야할 외래 키는MEMBER
에 있기 때문이다.
✔️ 연관관계의 주인은 외래키가 있는 곳으로
연관관계의 주인은 테이블에 외래 키가 있는 곳으로 정해야한다!
여기서는 회원 테이블이 외래 키를 가지고 있으므로 Member.team
이 주인이 된다.
주인이 아닌 Team.members
에는 mappedBy="team"
속성을 사용해서 주인이 아님을 설정해야 한다.
여기서 mappedBy
의 값으로 사용된 team
은 연관관계의 주인인 Member
엔티티의 team
필드를 말한다!
📌 양방향 매핑 규칙 정리
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정한다.
- 연관관계의 주인만이 외래 키를 관리할 수 있다.(등록, 수정)
- 주인이 아닌쪽은 읽기만 가능하며 외래 키를 변경하지 못한다.
- 주인은
mappedBy
속성을 사용하지 않는다.- 주인이 아니면
mappedBy
속성으로 주인을 지정한다.- 외래키가 있는 곳을 주인으로 정해야한다!
💡 참고
- 데이터베이스 테이블의 다대일, 일대다 관계에서는 항상 다 쪽이 외래 키를 가진다.
- 다 쪽인
@ManyToOne
은 항상 연관관계의 주인이 되므로mappedBy
를 설정할 수 없다.- 따라서
@ManyToOne
에는mappedBy
속성이 없다.
public void save() {
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
// 회원1 저장
Member member1 = new Member("member1", "회원1");
member.setTeam(team1); // 연관관계 설정 team1 -> team1
em.persist(member1);
// 회원2 저장
Member member2 = new Member("member2", "회원2");
member2.setTeam(team1); // 연관관계 설정 member2 -> team1
em.persist(member2);
}
Member.team
필드를 통해서 회원과 팀의 연관관계를 설정하고 저장했다.데이터베이스에서 회원테이블을 조회할 시
MEMBER_ID | USERNAME | TEAM_ID |
---|---|---|
member1 | 회원1 | team1 |
member2 | 회원2 | team1 |
TEAM_ID
외래 키에 팀의 기본 키 값이 저장되어 있다.team1.getMembers().add(member1); // 무시(연관관계의 주인이 아님)
team1.getMembers().add(member2); // 무시(연관관계의 주인이 아님)
Team.members
는 연관관계의 주인이 아니다.
Member member1 = new Member("member1", "회원1");
em.persist(member1);
Member member2 = new Member("member2", "회원2");
em.persist(member2);
Team team1 = new Team("team1", "팀1");
team1.getMembers().add(member1);
team1.getMembers().add(member2);
em.persist(team1);
회원1, 회원2를 저장하고 팀의 컬렉션에 담은 후에 팀을 저장한다.
위 코드를 실행한 결과, 회원 테이블을 조회한다.
MEMBER_ID | USERNAME | TEAM_ID |
---|---|---|
member1 | 회원1 | null |
member2 | 회원2 | null |
외래 키 TEAM_ID
에 team1
이 아닌 null
값이 입력되어 있는데, 연관관계의 주인이 아닌 Team.members
에만 값을 저장했기 때문이다. → 연관관계의 주인만이 외래 키의 값을 변경할 수 있다.
연관관계의 주인인 Member.team
에 아무 값도 입력하지 않았기 때문에, TEAM_ID
외래 키의 값도 null
이 저장된다.
정말 연관관계의 주인에만 값을 저장하고 주인이 아닌 곳에는 값을 저장하지 않아도 되는걸까?
: 사실은 객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 안전하다. 양쪽 방향 모두 값을 입력하지 않으면 JPA를 사용하지 않는 순수한 객체 상태에서 심각한 문제가 발생할 수 있다.
예를 들어 JPA를 사용하지 않고 엔티티에 대한 테스트 코드를 작성한다고 가정해볼때 ORM은 객체와 관계형 데이터베이스 둘 다 중요하다. 데이터베이스뿐만 아니라 객체도 함께 고려해야 한다.
public void test(){
Team team1 = new Team("team1", "팀1");
Member member1 = new Member("member1", "회원1");
Member member2 = new Member("member2", "회원2");
member1.setTeam(team1);
member2.setTeam(team2);
List<Member> members = team1.getMembers();
System.out.println("members.size = " + members.size());
}
member.size = 0
코드를 보면 Member.team
에만 연관관계를 설정하고 반대 방향은 연관관계를 설정하지 않았다.
마지막 줄에서 팀에 소속된 회원이 몇 명인지 출력해보면 결과는 0이 나온다.
이것은 우리가 기대하는 양방향 연관관계의 결과가 아니다.
따라서 양방향은 양쪽다 관계를 설정해야 한다.
이처럼 회원 → 팀 을 설정하면 다음 코드처럼 반대방향인 팀 → 회원도 설정해야한다.
양쪽 모두 관계를 설정한 코드
public void test(){
Team team1 = new Team("team1", "팀1");
Member member1 = new Member("member1", "회원1");
Member member2 = new Member("member2", "회원2");
member1.setTeam(team1);
team1.getMembers().add(member1);
member2.setTeam(team2);
team1.getMembers().add(member2);
List<Member> members = team1.getMembers();
System.out.println("members.size = " + members.size());
}
member.size = 2
JPA를 사용해서 완성한 예제
public void test(){
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
team1.getMembers().add(member1);
em.persist(member1);
Member member2 = new Member("member2", "회원2");
member2.setTeam(team2);
team1.getMembers().add(member2);
em.persist(member2);
}
양쪽에 연관관계를 설정한다.
순수한 객체 상태에서도 동작하며, 테이블의 외래 키도 정상 입력된다.
양방향 연관관계는 결국 양쪽 다 신경 써야 한다.
member.setTeam(team)
과 team.getMembers().add(member)
를 각각 호출하다 보면 실수로 둘 중 하나만 호출해서 양방향이 깨질 수 있다.
그래서 Member
클래스의 setTeam()
메서드를 수정해서 코드를 리팩토링 해보면
public class Member{
private Team team;
public void setTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
// ...
}
setTeam()
메서드 하나로 양방향 관계를 모두 설정하도록 변경했다.
setTeam()
메서드를 사용하는 코드
public void test() {
Team team1 = new Team("team1", "팀1");
em.persist(team1);
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
em.persist(member1);
Member member2 = new Member("member2", "회원1");
member2.setTeam(team1);
em.persist(member2);
}
➡️ 이렇게 한 번에 양방향 관계를 설정하는 메서드를 연관관계 편의 메서드라고 한다.
member.setTeam(team1);
member.setTEam(team2);
Member findMember = teamA.getMember(); // member1이 여전히 조회된다.
teamB
로 변경할 때 teamA
→ member1
관계를 제거하지 않았기 때문에 teamA.getMember()
메서드를 실행했을 때 member1
이 남아있다.Member.setTeam()
public void setTeam(Team team){
if(this.team != null) { // this.team이 null이 아니면 이 member객체는 team이 있음을 의미
this.team.getMembers().remove(this); // 해당 팀의 멤버에서 삭제
}
this.team = team;
team.getMembers().add(this);
}
JpaMain
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Team team2 = new Team();
team2.setName("TeamB");
em.persist(team2);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team);
member.setTeam(team2);
em.persist(member);
Member member2 = new Member();
member2.setName("member2");
member2.setTeam(team);
member2.setTeam(team2);
em.persist(member2);
em.flush();
em.clear();
//조회
Team team3 = em.find(Team.class, 1L);
Team team4 = em.find(Team.class, 2L);
System.out.println("team3 = " + team3);
System.out.println("team3 = " + team4);
List<Member> members = team3.getMembers(); // (팀 -> 회원) 객체 그래프 탐색
List<Member> members2 = team4.getMembers(); // (팀 -> 회원) 객체 그래프 탐색
for (Member m : members) {
System.out.println("member.username = " + m.getName());
}
for (Member m : members2) {
System.out.println("member2.username = " + m.getName());
}
tx.commit();
Member
@Entity
public class Member {
@Id @GeneratedValue
@Column(name="MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
// @ManyToOne
// @JoinColumn(name = "TEAM_ID")
// private Long teamId;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
public Team getTeam() {
return team;
}
public void setTeam(Team team) {
if(this.team != null) { // this.team이 null이 아니면 이 member객체는 team이 있음을 의미
this.team.getMembers().remove(this); // 해당 팀의 멤버에서 삭제
}
this.team = team;
team.getMembers().add(this);
}
// getter, setter
...
}
}
Team
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
public List<Member> getMembers() {
return members;
}
// getter, setter
}
실행결과
member2.username = member1
member2.username = member2
✔️ 연관관계의 주인에 값을 입력하지 않을 때
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
//역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);
em.persist(member);
ID | USERNAME | TEAM_ID |
---|---|---|
1 | member1 | null |
✔️ 양방향 매핑시 연관관계의 주인에 값을 입력해야 한다.
(순수한 객체 관계를 고려하면 항상 양쪽다 값을 입력해야 한다.)
Team team = new Team(); team.setName("TeamA");
em.persist(team);
Member member = new Member(); member.setName("member1");
team.getMembers().add(member); //연관관계의 주인에 값 설정
member.setTeam(team); //**
em.persist(member);
ID | USERNAME | TEAM_ID |
---|---|---|
1 | member1 | 2 |
lombok
사용시 toString()
을 사용하지 말자
JSON
생성 라이브러리 : 컨트롤러에서는 entity
를 절대로 반환하지 말자!
entity
를 api
로 반환시 api
스택이 변경된다.entity
는 컨트롤러에 dto
를 반환해서 사용해야한다.
✔️ 양방향 매핑 정리
✔️ 연관관계의 주인을 정하는 기준
✔️ 테이블 구조
테이블 구조는 실전 예제 1 와 같다
✔️ 객체 구조
실전 예제 1과 다르게 참조를 사용하도록 변경한다.
✔️ 소스
Item
package jpabook.jpashop.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Item {
@Id @GeneratedValue
@Column(name = "ITEM_ID")
private Long id;
private String name; //이름
private int price; //가격
private int stockQuantity; //재고수량
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 int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getStockQuantity() {
return stockQuantity;
}
public void setStockQuantity(int stockQuantity) {
this.stockQuantity = stockQuantity;
}
}
Member
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private String city;
private String street;
private String zipcode;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
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
package jpabook.jpashop.domain;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne
@JoinColumn(name="MEMBER_ID")
private Member member;
@OneToMany(mappedBy = "order")
private List<OrderItem> orderItems = new ArrayList<>();
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;//주문상태
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
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;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public void setOrderDate(LocalDateTime orderDate) {
this.orderDate = orderDate;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
}
OrderItem
package jpabook.jpashop.domain;
import javax.persistence.*;
@Entity
@Table(name = "ORDER_ITEM")
public class OrderItem {
@Id
@GeneratedValue
@Column(name = "ORDER_ITEM_ID")
private Long id;
@ManyToOne
@JoinColumn(name="ORDER_ID")
private Order order;
@ManyToOne
@JoinColumn(name ="ITEM_ID")
private Item item;
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;
}
private int orderPrice; //주문 가격
private int count; //주문 수량
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public 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;
}
}
OrderStatus
package jpabook.jpashop.domain;
public enum OrderStatus {
ORDER, CANCEL
}
JpaMain
package jpabook.jpashop;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.OrderItem;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Order order = new Order();
em.persist(order);
OrderItem orderItem = new OrderItem();
orderItem.setOrder(order);
em.persist(orderItem);
// order.addOrderItem(new OrderItem());
tx.commit();
}catch (Exception e){
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
참고