TEST 용 SQL
-- 학교의 반
CREATE TABLE CLASS (
CLASS_ID BIGINT PRIMARY KEY IDENTITY ,
CLASS_NAME VARCHAR(50) NOT NULL
);
-- 반에 속해있는 학생들
CREATE TABLE STUDENT (
STUDENT_ID BIGINT PRIMARY KEY IDENTITY ,
STUDENT_NAME VARCHAR(50) NOT NULL,
CLASS_ID BIGINT ,
FOREIGN KEY (CLASS_ID) REFERENCES CLASS(CLASS_ID)
);
Insert 구문은 더 보기를 클릭
INSERT INTO CLASS(CLASS_NAME) VALUES('1반');
INSERT INTO CLASS(CLASS_NAME) VALUES('2반');
INSERT INTO CLASS(CLASS_NAME) VALUES('3반');
INSERT INTO CLASS(CLASS_NAME) VALUES('4반');
INSERT INTO CLASS(CLASS_NAME) VALUES('5반');
INSERT INTO CLASS(CLASS_NAME) VALUES('6반');
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('서준' , 1);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('지우' , 1);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('민서' , 1);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('윤서' , 2);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('지민' , 2);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('하린' , 2);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('이준' , 3);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('현우' , 3);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('유찬' , 3);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('시아' , 4);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('가은' , 4);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('서영' , 4);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('지원' , 5);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('진우' , 5);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('한결' , 5);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('태윤' , 6);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('우주' , 6);
INSERT INTO STUDENT(STUDENT_NAME, CLASS_ID) VALUES('은호' , 6);
ClassEntity
import com.example.reactpractice.entity.studentEntity.StudentEntity;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "CLASS")
public class ClassEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long class_id;
@Column(nullable = false)
private String class_name;
// mappedBy 는 join 하려는 entity 에서 설정한 명 , cascade 는 데이터의 일관성을 유지하기 위한 영속화 속성
@OneToMany(mappedBy = "classEntity" , cascade = CascadeType.ALL)
private List<StudentEntity> studentEntity;
}
@OneToMany 는 하나의 Entity 가 여러 개의 Entity 를 가지는 의미이다
Class 는 하나의 Entity 만 가지고 있으니 1:N 에서 1을 의미한다
예) 1반, 2반, 3반....
부모 격의 Table 에서는
mappedBy 를 이용하여 자식 격 테이블 지정해 준다
StudentEntity
import com.example.reactpractice.entity.classEntity.ClassEntity;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "STUDENT")
public class StudentEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long student_id;
@Column(nullable = false)
private String student_name;
@ManyToOne
// name 에는 studenet table 에서 사용하려는 외래키 컬럼 을 지정한다
// referencedColumnName 에는 join 하려는 외래키 대상 table 컬럼 을 지정한다
@JoinColumn(name = "class_id" , referencedColumnName = "class_id")
private ClassEntity classEntity;
}
@ManyToOne 은 여러 개의 Entity 를 가지는 N 을 나타낸다
예) 1반 아무개, 1반 아무개, 1반 아무개 ....
자식 격 Table 은 외래키를 이용하여 부모 격 Table 과 연결시킨다
STUDENT Talbe 에서 생성한 CLASS_ID 는
@JoinColumn 으로 선언해 주기 때문에
따로 @Column 으로 설정하지 않아도 된다
ClassRepository
public interface ClassRepository extends JpaRepository<ClassEntity , Long> {
}
TEST
import java.util.List;
@RestController
@RequestMapping("/api/*")
public class JoinTestController {
@Autowired
private ClassRepository classRepository;
@GetMapping("/joinTest")
public void joinTest() throws Exception{
// id 로 1반 조회
ClassEntity classEntity = classRepository.findById(1L).get();
// @OneToMany 로 join 한 StudentEntity 를 가져옴
List<StudentEntity> students = classEntity.getStudentEntity();
for(StudentEntity student : students){
System.err.println("student : " + student.getStudent_name() );
}
}
}
@OneToMany 일 때
fetch = FetchType.LAZY 속성이 default 로 적용되어
현재 쿼리 로그는 각각 쿼리가 실행된 것을 확인할 수 있다
fetch = FetchType.EAGER , LAZY 속성 사용하기
fetch = FetchType.EAGER 속성을 설정하면
설정된 주 Entity 를 조회할 때 관련된 Entity 가 함께 조회된다
@Data
@Entity
@Table(name = "CLASS")
public class ClassEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long class_id;
@Column(nullable = false)
private String class_name;
// mappedBy 는 join 하려는 entity 에서 설정한 명 , cascade 는 데이터의 일관성을 유지하기 위한 영속화 속성
// fetch = FetchType.EAGER 는 설정된 Entity 조회 시 관련된 Entity 도 같이 조회
@OneToMany(mappedBy = "classEntity" , cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<StudentEntity> studentEntity;
}
fetch = FetchType.EAGER 속성만 설정하고 조회를 실행하면
Join 해서 가져오는 것을 확인할 수 있다
반(ClassEntity)을 조회해서
학생(StudentEntity) 이름을 조회하느라 @OneToMany 에
fetch = FetchType.EAGER 속성을 설정한 건데 연결된 데이터의 양이 많다면
fetch = FetchType.LAZY 속성을 이용하는 게 좋다고 한다
@ManyToOne 은 fetch = FetchType.EAGER 속성이 default
참고하자
@ManyToOne 을 기준으로 조회
@ManyToOne 은 default 인 fetch = FetchType.EAGER 속성
@OneToMany 은 default 인 fetch = FetchType.LAZY 속성
@Autowired
private StundentRepository stundentRepository; // JpaRepository 상속만 받은 interface
@GetMapping("/joinTest")
public void joinTest() throws Exception{
// id 로 반 조회
StudentEntity student = stundentRepository.findById(2L).get();
// @OneToMany 로 join 한 StudentEntity 를 가져옴
ClassEntity classEntity = student.getClassEntity();
System.err.println("학생 ID 2번은 className : " + classEntity.getClass_name() + " 의 " + student.getStudent_name() + " 학생입니다.");
}
findById 로 조회할 때
fetch = FetchType.EAGER 속성으로 Join 으로 조회를 하고
조회한 학생의 반 정보(getClassEntity())를 조회할 때
fetch = FetchType.LAZY 속성으로 조회된 것을 볼 수 있다
@OneToOne 과 @OneToMany 개념을 착각해서
게시물을 작성했다가 다시 작성해서 올리느라
내용이 좀 두서없을 수 도 있다
Entity 의 대상이 1 인지 N 인지만 제대로 기준을 잡자
'개발 > JPA' 카테고리의 다른 글
[JPA] @OneToOne 사용하기 (1) | 2024.11.16 |
---|---|
[JPA] @GeneratedValue 사용하기 (0) | 2024.11.14 |
[JPA] Entity 설정하기 (0) | 2024.11.13 |
[JPA] DELETE 하기(Delete , DeleteById , DeleteAll) (0) | 2024.11.12 |
[JPA] INSERT , UPDATE 하기(Save , SaveAll) (0) | 2024.11.11 |
댓글