Member 테이블과 Team 테이블이 있을 경우, 외래키를 Member에 지정하면서, Member가 Team을 가졌으나, Team은 Member의 정보를 가져오지 못하게 되어있다. 이를 단방향 연관 관계로 본다.
@ManyToOne
@JoinColumn(name="TEAM_ID")
@Setter(value=AccessLevel.NONE) // lombok에서 자동 setter를 막는다.
private Team team;
Member 클래스에 다음과 같이 일대다 연관 관계를 맺어 정보를 가져오지만, Team클래스에는 별도로 지정한것이 없기에 한 방향으로만 관계를 가지고 있다.
DB에서는 외래키를 지정하게 해주면 쿼리문을 통해 조인이 양쪽으로 가능하다는걸 보여주지만
자바의 객체개념으로 보면 별도의 어노테이션을 이용해서 단방향을 서로 만들어 해주어야 한다는걸 보여주는 구문
Member에서 Team으로 가는 team 참조 값과, Team에서 Member로가는 members참조 값이 있다.
Member에서 Team값이 수정 됐을 때 MEMBER 테이블의 TEAM_ID가 수정이 되야 하는지, Team에 있는 members를 수정 했을 때 MEMBER에 있는 TEAM_ID가 수정이 되야 하는지?
=> DB입장에서는 MEMBER에 있는 TEAM_ID만 update가 되면 된다.
즉 현재 보는것처럼 Member에 FK가 존재하기에, Member 클래스 에서 외래키를 관리한다.
@OneToMany(mappedBy = "team")
private List<Member> member = new ArrayList<>();
위 코드는 Team 클래스에 일대다 관계를 맺어주는 구문이다. 하나의 팀에 여러 멤버들이 있기 때문에, List를 선언하는 모습을 볼 수 있다.
Member member = new Member();
member.setUsername("member1");
em.persist(member);
Team team = new Team();
team.setName("TeamA");
team.getMember().add(member);
em.persist(team);
TEAM테이블이 먼저 지정이 되어야 TEAM_ID의 값이 들어가고, MEMBER 테이블에 TEAM_ID컬럼에 값을 넣어줄 수 있는데, 순서가 바뀌어있다. 그래서 코드에서는 team.getMember().add(member);
를 사용해서 멤버 클래스에 TEAM_ID에 값을 넣어주고 싶었으나, null값 즉, 값이 들어가지 않았음을 볼 수 있다.
거듭 말하지만 현재 관계의 주인은 Member 클래스이다. 즉, Team클래스에서는 수정할 권한이 없단걸 인지하자.
그렇다면 어떻게할까?
@Entity
@Getter @Setter
public class Member2 {
@Id @Column(name="MEMBER_ID")
@GeneratedValue
private Long id;
private String username;
@ManyToOne
@JoinColumn(name="TEAM_ID")
@Setter(value=AccessLevel.NONE) // lombok에서 자동 setter를 막는다.
private Team2 team2;
public void setTeam(Team2 team2) {
this.team2 = team2;
}
}
임시로만든 코드를 보자 Member2클래스에 Team2 클래스를 불러오도록 메서드 선언 해주고,
public static void main(String[] args) {
EntityManagerFactory emf
= Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
// 수정
Team2 team2 = new Team2();
team2.setName("TeamB");
em.persist(team2);
Member2 member2 = new Member2();
member2.setUsername("member2");
member2.setTeam(team2); // 연관관계 주인에게 값 설정
em.persist(member2);
tx.commit();
em.close();
emf.close();
}
위와 같이 member2에서 setTeam메서드를 불러와서 team2객체 값을 넣어주면 된다.
team2 객체에 추가한 값을 그냥 Member2에 set메서드를 통해 한번 선언 해주면 업데이트가 되는것이다.
Team 객체에 값을 넣고, 생성된 값을 Member 클래스에 값을 업데이트하고 그 걸 다시 Team 클래스 즉, 자기 자기자신에게 다시 덮어 씌우는 2중 과정을 거쳐야한다.
// Member 클래스에 추가한 메서드
public void setTeam(Team team) {
this.team = team;
}
// Team 클래스에 추가한 메서드,
public void addMember(Member member) {
member.setTeam(this);
this.member.add(member);
}
// 엔티티매니저에서 실행 구문
team.addMember(member); // 1의 입장에서 값 넣기
위 코드가 추가로 더 필요하다.
일대다인 시점의 Team 클래스에서는 member클래스를 불러와서 set하고 다시 그 인스턴스를 에 member 객체에 추가하는게 보인다. 서로 체인되는걸 제대로 인지 안한다면 어렵다..
사실 위처럼 EntityManager가 들어있는 클래스에 코드를 짜 넣을 필요없이, 각 객체에 메서드를 미리 짜두어 코드를 반복해서 사용할 필요없이 할 수 있다.
이렇게 한다면 코드가 더 깔끔해진다.
@ManyToOne
@JoinColumn(name="TEAM_ID")
@Setter(value=AccessLevel.NONE) // lombok에서 자동 setter를 막는다.
private Team team;
// 일방적인 setter의 형태가 아니면 매서드 이름을 바꿔준다.
// 추후 코드르 봤을때 단순 setter 작업이 아닌 중요한 작업을 진행하는지를 파악할 수 있다.
public void changeTeam(Team team) {
this.team = team;
// this : 나 자신의 인스턴스(객체)
team.getMember().add(this);
// team.getMember().add(member); 를 여기서 만듬
}
// 다대일, Team에는 여러 Member가 있기에 배열로 나타낸다. 일대다
// mappedBy = "team"은 Member에있는 Private Team team에 객체이름
@OneToMany(mappedBy = "team")
private List<Member> member
= new ArrayList<>();
public void addMember(Member member) {
member.setTeam(this);
this.member.add(member);
}
즉 외래키가 있는 테이블이 오너이므로 헤깔리지 말자.
startDate와 endDate는 하나의 묶음, city,street,zipcode를 한묶음으로 볼 수 있다.
그렇다면 자바에서 했던 객체지향을 할 수있지않을까? 이러한 접근을 임베디드로 하는것이다.
@Embedded
private Period period;
@Embedded
private Address address;
@Embeddable
@Getter @Setter
public class Period {
private LocalDateTime startDate;
private LocalDateTime endDate;
// 테스트를 위해 파라미터가 있는 생성자
public Period(LocalDateTime startDate, LocalDateTime endDate) {
super();
this.startDate = startDate;
this.endDate = endDate;
}
public Period() {}
}
두번째 코드처럼 하나의 클래스를 만들어 @Embeddable 을 붙여주고 @Embedded를 선언하여 따로 관리가 가능하다. DB테이블에서는 달라진 점이없으나, 개발자시점에서는 관리하기 매우 용이해진다.
한 엔티티에서 같은 값 타입을 사용하려면 컬럼 명이 중복
@AttributeOverride, @AttributeOverrides를 사용해서 컬럼명 속성을 재 정의
자바의 오버라이드 속성을 기억한다면 별 어려움이 없다. 즉 기존에 가지고 있는 변수타입을 크게 해치지 않는 선에서 변수명만 덧씌워준다
Java Persistence Query Language 의 약자, JPA를 사용하면 엔티티 객체를 중심으로 개발하게 되는데. 여기서 걸리는 점이 쿼리 문이다.