팀 정보를 가진 TEAM 테이블과 팀에 속하는 회원들의 정보를 가진 MEMBER 테이블이 TEAM_ID
를 매개로 관계를 맺는다고 가정한다.
TEAM_ID
만 업데이트되면 됨연관관계의 주인(Owner)
mappedBy
속성 사용 XmappedBy
: 내가 누군가에 의해 mapping 되었다는 뜻Member.team
이 주인이 된다.@Entity @Getter @Setter public class Member { // 자동 생성 primary key MEMBER_ID @Id @GeneratedValue @Column(name="MEMBER_ID") private Long id; // USERNAME 컬럼 @Column(name="USERNAME") private String name; // N:1 관계, 외래키가 있는 N측 객체가 주인이다. // Team 객체를 통째로 필드에 주입하고 관계 컬럼을 정의해 준다. @ManyToOne @JoinColumn(name="TEAM_ID") private Team team; }
- @ManyToOne : 1대 다의 개념 주입, 여기서는
Member
가 Many- @JoinColumn : 관계 컬럼을 정의(
TEAM_ID
를 기준으로 조인)
@Entity @Getter @Setter public class Team { // 자동 생성 primary key TEAM_ID (Member 측에서 상속하는 외래키) @Id @GeneratedValue @Column(name="TEAM_ID") private Long id; private String name; // Member 객체들로 이루어진 ArrayList로 1대 다 관계성 주입 @OneToMany(mappedBy = "team") private List<Member> member = new ArrayList<>();
- @OneToMany : 1대 다의 개념 주입, 여기서는
Team
이 One- mappedBy : 주인이 아닌 쪽을 뜻하는 속성
mappedBy
가 적힌 곳은 읽기만 가능하며, 값을 넣는 등의 업데이트 시도를 해도 작동하지 않음
Team
, Member
객체를 각각 하나씩 생성하고 Team
에 담긴 member
리스트 결과값을 보고자 한다.try { // team 생성, 1차 캐시 저장 Team team = new Team(); team.setName("TeamA"); em.persist(team); // member 생성, 1차 캐시 저장 Member member = new Member(); member.setName("member1"); member.setTeam(team); em.persist(member); // team에 담긴 member 리스트 정보 셀렉트 시도 Team findTeam = em.find(Team.class, team.getId()); List<Member> members = findTeam.getMember(); for(Member m : members) { System.out.println("member = " + m.getName()); } tx.commit(); }catch(Exception e) { tx.rollback(); }finally { em.close(); emf.close(); }
member
값을 가져오지 못하는데, team
이 영속성 컨텍스트 즉 1차 캐시에 저장되고 아직 flush
나 commit
이 호출되지 않아 변경을 감지하지 못한 상태에서 검색했기 때문이다. 즉 1차 캐시에 담긴 내용(null
)이 그대로 조회된다.flush
를 호출하고 조회하면 검색은 가능하다.em.flush(); em.clear();
flush
를 호출할 수 없으므로, 양방향 매핑 시에는 양쪽 객체에 값을 모두 입력시켜 주어야 한다. 이렇게 하면 DB를 다녀오지 않고 순수 객체 상태로만 사용할 수 있다.team.getMember().add(member);
➕ 참고로
member.setTeam(team);
을 하지 않고team.getMember().add(member);
만으로 자료를 업데이트하려고 하면 동작하지 않는다.
해당 객체 매핑에서 연관관계의 주인이Member.team
에 있고Team
에서의 관계는 읽기 전용이기 때문이다.
Member
를 기준으로 team
을 넣을 경우Member
클래스에서 기존의 세터를 변형한 메서드이다. ➕ 일반적인public void changeTeam(Team team) { this.team = team; // this : 나 자신의 인스턴스(Member) team.getMember().add(this); }
setter
의 형태를 벗어나면 추후 소스코드를 볼 때 파악하기 쉽도록 이름을 바꿔 주는 것이 좋다.@Setter(value=AccessLevel.NONE)
setTeam
대신 사용하면 자동으로 team
쪽에도 값이 입력된다.Team
을 기준으로 member
를 넣을 경우 Team
클래스에서 추가한 메서드이다. public void addMember(Member member) { member.setTeam(this); this.member.add(member); }
team
에 멤버를 입력할 때 자동으로 member
측에도 team
값이 설정된다.JPQL
에서 역방향으로 탐색할 일이 많음