피할 수 없다면 즐기자! 스프링부트 너.. 뭐 돼?
조건 1) 회원 <-> 팀
조건 2) 회원은 하나의 팀에만 소속될 수 있어요.
조건 3) 회원과 팀은 다대일 관계에요.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamid;
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
}
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import com.koreait.jpaitem.relation.Member;
import com.koreait.jpaitem.relation.Team;
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 {
Team team = new Team();
team.setName("TeamA");
// 영속상태가 되면, PK의 값이 세팅이 된 후
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeamid(team.getId());
em.persist(member);
// select
// 어느팀 소속인지 알고 싶을 때 jpa or db에게 계속 물어봐야 한다.
Member findMember = em.find(Member.class, member.getId());
Long findTeamid = findMember.getTeamid();
Team findTeam = em.find(Team.class, findTeamid);
System.out.println("==============================\n\n\n");
System.out.println("findTeam : " + findTeam.getName());
System.out.println("\n\n\n==============================");
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
em.close();
emf.close();
}
}
}
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Member {
// 객체 참조로 연관된 객체를 갖는 방법(1)
// @Id @GeneratedValue
// @Column(name = "MEMBER_ID")
// private Long id;
// @Column(name = "USERNAME")
// private String name;
// @Column(name = "TEAM_ID")
// private Long teamid;
// 객체 참조로 연관된 객체를 갖는 방법(2)
// @ManyToOne : 여기에선 Team이 하나 (많은 것 중에 하나)
// @JoinColumn(name = "TEAM_ID") : 해당 관계 컬럼을 명시해줘야한다. TEAM_ID와 조인해야 한다.
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
}
package com.koreait.jpaitem.relation;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
}
package com.koreait.jpaitem;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import com.koreait.jpaitem.relation.Member;
import com.koreait.jpaitem.relation.Team;
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 {
Team team = new Team();
team.setName("TeamA");
// 영속상태가 되면, PK의 값이 세팅이 된 후
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
// 강제 db 쿼리를 보고 싶을 때
em.flush();
em.clear();
// select
// find시에 1차캐시에서 가지고 와서 select문이 없다.
Member findMember = em.find(Member.class, member.getId());
Team findTeam = findMember.getTeam();
System.out.println("==============================\n\n\n");
System.out.println("findTeam : " + findTeam.getName());
System.out.println("\n\n\n==============================");
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
em.close();
emf.close();
}
}
}
// 양방향 매핑시 가장 많이 하는 실수
// readOnly 만가능하다.
Member member = new Member();
member.setName("member1");
em.persist(member);
Team team = new Team();
team.setName("TeamA");
team.getMember().add(member);
em.persist(team);
package com.koreait.jpaitem.relation;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
@SequenceGenerator(
name = "MEMBER_SEQ_GENERATOR",
sequenceName = "MEMBER_SEQ",
initialValue = 1, allocationSize = 1)
public class Member {
// 객체 참조로 연관된 객체를 갖는 방법(1)
// @Id @GeneratedValue
// @Column(name = "MEMBER_ID")
// private Long id;
// @Column(name = "USERNAME")
// private String name;
// @Column(name = "TEAM_ID")
// private Long teamid;
// 객체 참조로 연관된 객체를 갖는 방법(2)
// @ManyToOne : 여기에선 Team이 하나 (많은 것 중에 하나)
// Member -> Team : N -> 1 => @ManyToOne
// @JoinColumn(name = "TEAM_ID") : 해당 관계 컬럼을 명시해줘야한다. TEAM_ID와 조인해야 한다.
// 외래키가 있는 객체가 주인
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
}
package com.koreait.jpaitem.relation;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.SequenceGenerator;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
@SequenceGenerator(
name = "TEAM_SEQ_GENERATOR",
sequenceName = "TEAM_SEQ",
initialValue = 1, allocationSize = 1)
public class Team {
/*
* team에 의해서 관리가 된다.
* mappedBy가 적힌 곳은 읽기만 가능하다.
* 값을 넣어봐야 아무일도 벌어지지 않는다.
* 대신 조희는 가능하다.
*/
@OneToMany(mappedBy = "team")
private List<Member> member = new ArrayList<Member>();
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
}
package com.koreait.jpaitem;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import com.koreait.jpaitem.relation.Member;
import com.koreait.jpaitem.relation.Team;
public class JpaMain2 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
/*
// 양방향 매핑시 가장 많이 하는 실수
// readOnly 만가능하다.
Member member = new Member();
member.setName("member1");
em.persist(member);
Team team = new Team();
team.setName("TeamA");
team.getMember().add(member);
em.persist(team);
*/
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);
// DB 트렌젝션
// em.flush();
// em.clear();
// 객체 지향적인 입장에서 양쪽에 모두 값을 넣어주어야한다.
// 양방향 매핑시에는 양쪽의 값을 모두 입력해 주어야 한다.
// DB를 다시 다녀오지 않고 객체 상태로만 사용할 수 있다.
team.getMember().add(member);
System.out.println("==============================");
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMember();
for( Member m : members ) {
System.out.println("m = " + m.getName());
}
System.out.println("==============================");
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
em.close();
emf.close();
}
}
}
JpaMain2.java 에서
team.getMember().add(member);
이 코드와 Member.java 의
public void setTeam(Team team) {
this.team = team;
team.getMember().add(this);
}
와 같은 역할을 해요.
일반적으로 setter의 형태가 아니면 메서드 이름을 바꿔주는데,
public void changeTeam(Team team) {
this.team = team;
// this : 나 자신의 인스터를 넣어준다.
team.getMember().add(this);
}
// Team.java 에서의 선언을 위해 추가 생성
public void setTeam(Team team) {
this.team = team;
}
이러한 형태는 추후 소스코드를 봤을 때 단순 setter의 작업이 아닌 중요한 작업을 진행하는지를 파악 할 수 있게 도와줍니다.
package com.koreait.jpaitem.relation;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Member {
// 객체 참조로 연관된 객체를 갖는 방법(1)
// @Id @GeneratedValue
// @Column(name = "MEMBER_ID")
// private Long id;
// @Column(name = "USERNAME")
// private String name;
// @Column(name = "TEAM_ID")
// private Long teamid;
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
// 객체 참조로 연관된 객체를 갖는 방법(2)
// @ManyToOne : 여기에선 Team이 하나 (많은 것 중에 하나)
// Member -> Team : N -> 1 => @ManyToOne
// @JoinColumn(name = "TEAM_ID") : 해당 관계 컬럼을 명시해줘야한다. TEAM_ID와 조인해야 한다.
// 외래키가 있는 객체가 주인
@ManyToOne
@JoinColumn(name = "TEAM_ID")
// @Setter 클래스 정의도 가능하지만, 전역 변수 정의도 가능하다.
@Setter(value = AccessLevel.NONE) // lombok 옵션으로 lombok 에서 자동 setter 생성을 막아준다.
private Team team;
// 일반적으로 setter의 형태가 아니면 메서드 이름을 바꿔준다.
// 추후 소스코드를 봤을 때 단순 setter의 작업이 아닌 중요한 작업을 진행하는지를 파악 할 수 있다.
public void changeTeam(Team team) {
this.team = team;
// this : 나 자신의 인스턴스를 넣어준다.
team.getMember().add(this);
}
// Team.java 에서의 선언을 위해 추가 생성
public void setTeam(Team team) {
this.team = team;
}
}
package com.koreait.jpaitem.relation;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
/*
* team에 의해서 관리가 된다.
* mappedBy가 적힌 곳은 읽기만 가능하다.
* 값을 넣어봐야 아무일도 벌어지지 않는다.
* 대신 조희는 가능하다.
*/
@OneToMany(mappedBy = "team")
private List<Member> member = new ArrayList<Member>();
public void addMember(Member member) {
// 추가 생성한 Membet setter
member.setTeam(this);
this.member.add(member);
}
}
package com.koreait.jpaitem;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import com.koreait.jpaitem.relation.Member;
import com.koreait.jpaitem.relation.Team;
public class JpaMain2 {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
/*
// 양방향 매핑시 가장 많이 하는 실수
// readOnly 만가능하다.
Member member = new Member();
member.setName("member1");
em.persist(member);
Team team = new Team();
team.setName("TeamA");
team.getMember().add(member);
em.persist(team);
*/
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
// member.setTeam(team);
member.changeTeam(team);
em.persist(member);
// DB 트렌젝션
// em.flush();
// em.clear();
// 객체 지향적인 입장에서 양쪽에 모두 값을 넣어주어야한다.
// 양방향 매핑시에는 양쪽의 값을 모두 입력해 주어야 한다.
// DB를 다시 다녀오지 않고 객체 상태로만 사용할 수 있다.
// team.getMember().add(member);
System.out.println("==============================");
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMember();
for( Member m : members ) {
System.out.println("m = " + m.getName());
}
System.out.println("==============================");
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
em.close();
emf.close();
}
}
}
package com.koreait.jpaitem.relation;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Member {
...
// lombok에서 지원하는 어노테이션 @toString으로 만들 수 있다.
// 그러나 Member.java와 Team.java에서 서로를 호출하고 있기 때문에 무한 루프가 발생한다.
// 그래서 @toString 선언은 주의해야하며 필요시에는 한 곳에서만 호출하는 것이 좋다. (추가적으로 주의할 어노테이션 @Data)
@Override
public String toString() {
return "Member [id=" + id + ", name=" + name + ", team=" + team + "]";
}
}
package com.koreait.jpaitem.relation;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Team {
...
@Override
public String toString() {
return "Team [id=" + id + ", name=" + name + ", member=" + member + "]";
}
}
출처
https://media.giphy.com/media/kyUIknbbDNvID5XzU4/giphy.gif
https://media.giphy.com/media/A6aHBCFqlE0Rq/giphy.gif