Entity의 연관 관계 - @OneToOne -
1 대 1 관계
Entity로 데이터베이스를 생성하고자 할 때
Entitiy 클래스를 JPA가 데이터베이스의 테이블에 매핑을 해주는 개념이다.
따라서 인터페이스인 JpaRepository를 상속한 인터페이스를 만들어 주어야 한다.
interface 간의 상속은 extends로 이루어진다.
Repository
public interface <Repository 명> extends JpaRepository<Type 명, ID 타입> {
}
application.properties
spring.datasource.url = <url>
spring.datasource.username = root
spring.datasource.password = <password>
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update
#update -> 해당하는 테이블이 없거나 필드가 맞지 않다면 CREATE TABLE, 또는 ALTER TALBE을 통해 수정해준다.
#create-drop -> 테스트를 할때마다 테이블을 드롭한다.
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
테스트 코드 예시
package com.practice.practice.relation;
import com.practice.practice.entity.Product;
import com.practice.practice.entity.User;
import com.practice.practice.repository.ProductRepository;
import com.practice.practice.repository.UserRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@SpringBootTest //빈으로 등록된 것들을 주입하거나 사용가능하게 해줌
public class OneToOneTest {
@Autowired
ProductRepository productRepository;
@Autowired
UserRepository userRepository;
@Test
@Rollback(value = false) // 테스트에서는 @Transactional 에 의해 자동으로 rollback 됨으로 false 설정을 해준다.
@DisplayName("1대1 단방향 테스트")
void test1() {
Product product = new Product("곰인형", 15000);
productRepository.save(product);
User user = new User("John");
userRepository.save(user);
// 왜래 키의 주인인 Product Entity user 필드에 user 객체를 추가한다.
product.setUser(user);
}
}
@OneToOne
1 : 1 관계를 맺어주는 역할
고객 Entity와 상품 Entity가 1 : 1 관계라고 가정
단방향 관계
외래 키의 주인 정하기
Entity에서 외래키의 주인은 일반적으로 N(다)의 관계인
Entity이지만 1 : 1 관계에서는 N(다)가 없으므로
외래키의 주인을 직접 정해야 한다.
Product Entity가 외래키의 주인인 경우
Product Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
"product table"에 "user_id"가 외래키로 컬럼이 생긴다고
생각하면 된다.
User Entity
package com.practice.practice.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@Table(name = "users")
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public User(String name) {
this.name = name;
}
}
단방향 관계이므로 고객의 Entity에는 참조할 수 있는 그 어떤 것도 없다.
참조 방향
상품 -> 고객
테스트 결과
Hibernate:
/* insert com.practice.practice.entity.Product
*/ insert
into
product (name, price, user_id)
values
(?, ?, ?)
Hibernate:
/* insert com.practice.practice.entity.User
*/ insert
into
users (name)
values
(?)
Hibernate:
/* update
com.practice.practice.entity.Product */ update product
set
name=?,
price=?,
user_id=?
where
id=?
User Entity가 외래 키의 주인인 경우
Product Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
User Entity
package com.practice.practice.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@Table(name = "users")
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "product_id")
private Product product;
public User(String name) {
this.name = name;
}
}
참조 방향
고객 -> 상품
양방향 관계
외래 키의 주인 정하기
양방향 관계에서 외래 키의 주인을 지정해 줄 때
"mappedBy"를 사용한다.
"mappedBy"의 속성값은 외래 키의 주인인
상대 Entity의 필드명을 의미한다.
Product Entity가 외래키의 주인인 경우
Product Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
"Product Entity"가 외래키의 주인이므로 "user_id" 컬럼을 가지고 있다.
User Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@Table(name = "users")
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(mappedBy = "user")
private Product product;
public User(String name) {
this.name = name;
}
}
mappedBy = "user"
를 하여 연관관계 주인의 필드명을 추가해 준다.
양방향 이므로
"Product"를 참조할 수 있다.
참조 방향
고객 -> 상품
상품 -> 고객
테스트 결과
Hibernate:
/* insert com.practice.practice.entity.Product
*/ insert
into
product (name, price, user_id)
values
(?, ?, ?)
Hibernate:
/* insert com.practice.practice.entity.User
*/ insert
into
users (name)
values
(?)
User Entity에 아래의 코드를 추가해 본다.
public void setProduct(Product product) {
this.product = product;
// 외래키를 추가 합니다.
product.setUser(this);
}
Hibernate:
/* insert com.practice.practice.entity.Product
*/ insert
into
product (name, price, user_id)
values
(?, ?, ?)
Hibernate:
/* insert com.practice.practice.entity.User
*/ insert
into
users (name)
values
(?)
Hibernate:
/* update
com.practice.practice.entity.Product */ update product
set
name=?,
price=?,
user_id=?
where
id=?
추가한 코드로 Hibernate에 update product 가 생겼다.
외래키가 다시 잘 들어온다.
User Entity에 추가했던 코드를 지우고 테스트 코드를 바꿔서 테스트
// 왜래 키의 주인인 Product Entity user 필드에 user 객체를 추가한다.
//user.setProduct(product); // -> 외래키에 null이 생겨 user entity에 추가코드를 줘서 테스트한 코드
product.setUser(user);
Hibernate:
/* insert com.practice.practice.entity.Product
*/ insert
into
product (name, price, user_id)
values
(?, ?, ?)
Hibernate:
/* insert com.practice.practice.entity.User
*/ insert
into
users (name)
values
(?)
Hibernate:
/* update
com.practice.practice.entity.Product */ update product
set
name=?,
price=?,
user_id=?
where
id=?
update product가 똑같이 있다.
외래키가 잘 들어오고 있다.
양방향이더라도 연관관계의 주인이 어디 있느냐에 따라
결과가 달라질 수 있다.
User Entity가 외래키의 주인인 경우
Product Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@NoArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@OneToOne(mappedBy = "product")
private User user;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
User Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Entity
@Getter
@Table(name = "users")
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "product_id")
private Product product;
public User(String name) {
this.name = name;
}
}
참조 방향
고객 -> 상품
상품 -> 고객
연관관계의 주인을 어느 Entity에 주느냐에 따라
코드의 효율이 달라진다고도 하니 많은 고민을 해야 한다.
내용 참고 - 스파르타 코딩클럽 -
'자바 탐구' 카테고리의 다른 글
JPA) Entity의 연관 관계 - @OneToMany - (0) | 2023.05.03 |
---|---|
JPA) Entity의 연관 관계 - @ManyToOne - (0) | 2023.05.03 |
JPA) 데이터베이스 테이블과 Entity의 연관 관계 표현 차이 (0) | 2023.05.02 |
인텔리제이) MySQL 연동하기 (0) | 2023.04.30 |
자바) Program, Process, Thread (0) | 2023.04.27 |