팀 정보를 가진 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에서 역방향으로 탐색할 일이 많음