@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
}
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
}
회원은 [Member.team](http://Member.team)
으로 팀 엔티티를 참조할 수 있지만 반대로 팀에는 회원을 참조하는 필드가 없다. Member.team
필드로 회원 테이블의 TEAM_ID
외래키를 관리한다.
다대일 양방향의 객체 연관관계에서 실선이 연관관계의 주인이고 점선은 연관관계의 주인이 아니다.
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
public void setTeam(Team team) {
this.team = team;
// 무한 루프 방지
if (!team.getMembers().contains(this)) {
team.getMembers().add(this);
}
}
}
@Getter
@Setter
@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<>();
public void addMember(Member member) {
this.members.add(member);
if (member.getTeam() != this) { // 무한 루프 방지
member.setTeam(this);
}
}
}
테스트 환경시 실행이 잘되지 않는 경우 @Transactional
어노테이션을 붙이면 실행되는 경우가 있다.
@Transactional
은 데이터의 추가, 갱신, 삭제 등으로 이루어진 작업을 처리하던 중 오류가 발생했을 때 모든 작업을 원상태로 되돌릴 수 있다. 즉, 모든 작업이 성공적으로 수행되었을때 DB에 반영한다.
DB와 관련된 서비스 클래스 혹은 메서드에 @Transactional
을 붙여 사용할 수 있다.
@JoinColumn
으로 해당 컬럼으로 join이 수행되어야함을 알려주어야 한다.@OneToMany
Annotation을 활용하여 관계 정의 (EAGER은 트랜잭션과 관련 링크)name
: MemberHistory 클래스의 member_id 로 조회insertable
: Member entity에서 조회는 가능하지만 추가은 할 수 없도록 함updatable
: Member entity에서 조회는 가능하지만 수정은 할 수 없도록 함@Getter
@Setter
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "team_id") // member 테이블의 team_id(pk)
private List<Member> members = new ArrayList<>();
}
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
}
@JoinColumn
을 꼭 사용해야 한다. 그렇지 않으면 조인 테이블 방식을 사용@Getter
@Setter
@Entity
public class Team {
@Id
@GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "team_id") // member 테이블의 team_id(pk)
private List<Member> members = new ArrayList<>();
}
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@ManyToOne
@JoinColumn(name = "team_id", insertable = false, updatable = false)
private Team team;
}
이 방법은 일대다 양방향 매핑이라기 보다는 일다다 단방향 매핑 반대편에 다대일 단방향 매핑을 읽기 전용으로 추가해서 일대다 양방향처럼 보이도록 하는 방법이다. 따라서 일대다 단뱡향 매핑이 가지는 단점을 그대로 가진다. 다대일 양방향 매핑을 권장한다.
@OneToOne
Annotation table 간에 1대1 관계를 가지는 컬럼에 붙여준다.optional = false
: not null 이 된다.mappedBy = “Table명”
: 연관키를 해당 테이블에서는 더이상 가지지 않게 된다.ToString Method
등의 경우 stack overflow, 순환 참가 발생할 수 있다.@ToString.Exclude
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
}
@Getter
@Setter
@Entity
public class Locker {
@Id
@GeneratedValue
@Column(name = "locker_id")
private Long id;
private String name;
}
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
}
@Getter
@Setter
@Entity
public class Locker {
@Id
@GeneratedValue
@Column(name = "locker_id")
private Long id;
private String name;
@OneToOne(mappedBy = "locker")
private Member member;
}
양방향이므로 연관관계의 주인을 정해야 한다. Member 테이블이 외래키를 가지므로 Member.locker가 연관관계의 주인이다.
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@ManyToMany
@JoinTable(name = "member_product",
joinColumns = @JoinColumn(name = "member_id"),
inverseJoinColumns = @JoinColumn(name = "product_id"))
private List<Product> products = new ArrayList<>();
}
@Getter
@Setter
@Entity
public class Product {
@Id
@GeneratedValue
private String id;
private String name;
}
회원 엔티티와 상품 엔티티를 @ManyToMany
로 매핑했다. 여기서 중요한 점은 manyToMany 와 @JoinTable을 사용해서 연결 테이블로 바로 매핑한 것이다. 따라서, 회원과 상품을 연결하는 회원_상품
테이블 없이 매핑을 완료한 것이다.
Product에 @ManyToMany
를 사용한다. 그리고 양쪽 중 원하는 곳에 mappedBy
로 연관관계의 주인을 지중한다. (mappedBy
가 없는 곳이 연관관계의 주인이다.)
@Getter
@Setter
@Entity
public class Product {
@Id
@GeneratedValue
private String id;
private String name;
@ManyToMany(mappedBy = "products")
private List<Member> members;
public void addMember(Member member) {
members.add(member);
member.getProducts().add(this);
}
}
@Getter
@Setter
@Entity
public class Order {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
private int orderAmount;
}
대리키를 사용함으로써 이전에 보았던 식별 관계에 복합키를 사용하는 것보다 매핑이 단순하고 이해하기 쉽다.
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue
@Column(name = "member_id")
private Long id;
private String username;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
@Getter
@Setter
@Entity
public class Product {
@Id @Column(name = "product_id")
private String id;
private String name;
@OneToMany(mappedBy = "product")
private List<Order> orders = new ArrayList<>();
}