엔티티들의 관계
- 하나는 Owner 이며 하나는 Non-owning 이다.
- 관계의 레퍼런스를 가지고 있는 쪽이 Owner 다.
- 1 = @OneToMany
- N = @ManyToOne
N 테이블이 Owner 일 경우
- 1 에 대한 FK 가 생성 된다.
@Entity
public class Study {
@Id
@GeneratedValue
private Long id;
private String name;
// 1 : N >> N
@ManyToOne
private Account owner;
// //
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Study study = (Study) o;
return id.equals(study.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public Account getOwner() {
return owner;
}
public void setOwner(Account owner) {
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
DDL = 자식 테이블인 Study 에 FK 를 건 모습.
Hibernate:
create table account (
id int8 not null,
city varchar(255),
state varchar(255),
home_street varchar(255),
zip_code varchar(255),
created time,
password varchar(255) not null,
username varchar(255) not null,
yes varchar(255),
primary key (id)
)
Hibernate:
create table study (
id int8 not null,
name varchar(255),
owner_id int8,
primary key (id)
)
Hibernate:
alter table if exists account
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 unique (username)
Hibernate:
alter table if exists study
add constraint FK210g5r7wftvloq2ics531e6e4
foreign key (owner_id)
references account
1 테이블이 Owner 일 경우
- Join Table이 생성 된다.
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(columnDefinition = "varchar(255) not null")
private String password;
// 1 : N >> 1
@OneToMany
private Set<Study> studies = new HashSet<>();
//
public void setStudies(Set<Study> studies) {
this.studies = studies;
}
@Temporal(TemporalType.TIME)
private Date created = new Date();
private String yes;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street" , column = @Column(name = "home_street"))
})
private Address address;
@Transient
private String no;
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return id.equals(account.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public Set<Study> getStudies() {
return studies;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
DDL = 복합키를 가진 중간 테이블 생성된 모습
Hibernate:
create table account (
id int8 not null,
city varchar(255),
state varchar(255),
home_street varchar(255),
zip_code varchar(255),
created time,
password varchar(255) not null,
username varchar(255) not null,
yes varchar(255),
primary key (id)
)
Hibernate:
create table account_studies (
account_id int8 not null,
studies_id int8 not null,
primary key (account_id, studies_id)
)
Hibernate:
create table study (
id int8 not null,
name varchar(255),
primary key (id)
)
Hibernate:
alter table if exists account
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 unique (username)
Hibernate:
alter table if exists account_studies
add constraint UK_tevcop76y9etp9vx5vce7gns6 unique (studies_id)
Hibernate:
alter table if exists account_studies
add constraint FKem9ae62rreqwn7sv2efcphluk
foreign key (studies_id)
references study
Hibernate:
alter table if exists account_studies
add constraint FK4h3r1x3qcsugrps8vc6dgnn25
foreign key (account_id)
references account
단방향 관계의 주인
- Entity 의 레퍼런스를 가진 쪽이 Owner 가 된다.
- 단방향 맵핑의 경우 Owner 에서 Non-owning Entity 를 가져와 영속화 시킨다.
Study 가 Owner Entity 일 경우의 영속화
@Component
@Transactional
public class JpaRunner implements ApplicationRunner {
@PersistenceContext
private EntityManager entityManager;
@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("Jungsoomin2");
account.setPassword("jpa");
Study study = new Study();
study.setName("spring data jpa");
study.setOwner(account); // Owner 인 Study 에 객체참조 값을 지정하는 모습
Session session = entityManager.unwrap(Session.class);
session.save(account);
session.save(study);
// entityManager.persist(account);
}
}
양방향 관계
Entity 사이의 양방향 레퍼런스를 만들고 싶을 경우 설정한다.
- mappedBy 속성을 사용하며 Non-owning 에서 설정한다.
- Owner Entity 에서 지정한 맵핑될 멤버변수의 이름을 선언해야한다.
Non-owning Entity = Account = mappedBy
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(columnDefinition = "varchar(255) not null")
private String password;
// 1 : N >> 1
@OneToMany(mappedBy = "owner")
private Set<Study> studies = new HashSet<>();
//
public void setStudies(Set<Study> studies) {
this.studies = studies;
}
@Temporal(TemporalType.TIME)
private Date created = new Date();
private String yes;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street" , column = @Column(name = "home_street"))
})
private Address address;
@Transient
private String no;
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return id.equals(account.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public Set<Study> getStudies() {
return studies;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Owner Entity = Study
@Entity
public class Study {
@Id
@GeneratedValue
private Long id;
private String name;
// 1 : N >> N
@ManyToOne
private Account owner;
// //
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Study study = (Study) o;
return id.equals(study.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public Account getOwner() {
return owner;
}
public void setOwner(Account owner) {
this.owner = owner;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
DDL = Join Table이 생성되지않고, 자식테이블에서 FK 를 지정하는 모습을 가지고 있음.
Hibernate:
create table account (
id int8 not null,
city varchar(255),
state varchar(255),
home_street varchar(255),
zip_code varchar(255),
created time,
password varchar(255) not null,
username varchar(255) not null,
yes varchar(255),
primary key (id)
)
Hibernate:
create table study (
id int8 not null,
name varchar(255),
owner_id int8,
primary key (id)
)
Hibernate:
alter table if exists account
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 unique (username)
Hibernate:
alter table if exists study
add constraint FK210g5r7wftvloq2ics531e6e4
foreign key (owner_id)
references account
양방향 관계의 주인
- 양방향 관계의 Owner Entity 는 FK를 가지고 있는 쪽이다.
- 즉, @ManyToOne 이 Owner Entity.
- 결국, Non-owning Entity 에서 mappedBy 속성을 사용하여 Owner Entity 의 필드를 설정해줘야하는 것.
양방향 관계 맵핑은 Owner Entity(FK) 에서 해야만 한다.
- 하지만, 객체지향 관점으로 보면, 서로의 레퍼런스를 참조할 시에 각 인스턴스의 참조 값에서는 값이 존재해야한다.
- 그러므로, Owner Side 에서 메서드를 정의하여, 각 인스턴스의 참조 값에 데이터를 저장할 필요가 있다.
즉, 서로의 인스턴스에 참조 값을 주는 과정과 지우는 과정은 , 반드시 서로 묶여다녀야만한다.
해당 코드는 Non-owning Entity 인 Account 에서 addStudy(), removeStudy() 로 참조 값을 다루고 있으니 참고.
@Entity
public class Account {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(columnDefinition = "varchar(255) not null")
private String password;
// 1 : N >> 1
@OneToMany(mappedBy = "owner")
private Set<Study> studies = new HashSet<>();
//
public void setStudies(Set<Study> studies) {
this.studies = studies;
}
@Temporal(TemporalType.TIME)
private Date created = new Date();
private String yes;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street" , column = @Column(name = "home_street"))
})
private Address address;
@Transient
private String no;
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
Account account = (Account) o;
return id.equals(account.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
public Set<Study> getStudies() {
return studies;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void addStudy(Study study) {
this.getStudies().add(study); // 양방향 관계의 주인은 Study , 객체지향 관점으로 보면 account 에도 데이터가 들어가야한다.
study.setOwner(this);
}
public void removeStudy(Study study){
this.getStudies().remove(study);
study.setOwner(null);
}
}
Account 에 정의된 레퍼런스 정의 메서드 사용 후 영속화 시키고 있다.
@Component
@Transactional
public class JpaRunner implements ApplicationRunner {
@PersistenceContext
private EntityManager entityManager;
@Override
public void run(ApplicationArguments args) throws Exception {
Account account = new Account();
account.setUsername("Jungsoomin2");
account.setPassword("jpa");
Study study = new Study();
study.setName("spring data jpa");
account.addStudy(study);
Session session = entityManager.unwrap(Session.class);
session.save(account);
session.save(study);
// entityManager.persist(account);
}
}
'springframework > Spring Data JPA' 카테고리의 다른 글
Fetch 전략 (0) | 2020.11.13 |
---|---|
Cascade, Entity 상태 (0) | 2020.11.13 |
Value 타입 맵핑 (0) | 2020.11.13 |
엔티티타입 맵핑 (0) | 2020.11.13 |
JPA 실행하고 적용하기 (0) | 2020.11.09 |