📖 연관관계 매핑 종류와 방향
연관관계를 맺는 두 엔티티 간에 생성할 수 있는 연관관계의 종류는 아래와 같습니다.
- One To One : 일 대 일(1:1)
- One To Many : 일 대 다(1:N)
- Many To One : 다 대 일(N:1)
- Many To Many : 다 대 다(N:M)
데이터베이스에서는 두 테이블의 연관관계를 설정하면 외래키(FK)를 통해 서로 조인해서 참조하는 구조로 생성되지만,
JPA를 사용하는 객체지향 모델링에서는 엔티티 간 참조 방향을 설정할 수 있습니다.
데이터베이스와 관계를 일치시키기 위해 양방향으로 설정해도 무관하지만, 비즈니스 로직 관점에서 봤을 때는 단방향 관계만 설정해도 해결되는 경우가 많습니다. 단방향과 양방향 관계에 대해 아래에 간단하게 정리하면 다음과 같습니다.
- 단방향 : 두 엔티티의 관계에서 한쪽의 엔티티만 참조하는 형식
- 양방향 : 두 엔티티의 관계에서 각 엔티티가 서로의 엔티티를 참조하는 형식
연관관계가 설정되면 한 테이블에서 다른 테이블의 기본값을 외래키로 갖게 됩니다.
이런 관계에서는 주인(Owner)이라는 개념이 사용됩니다. 일반적으로 외래키를 가진 테이블이 그 관계의 주인이 되며, 주인은 외래키를 사용할 수 있으나 상대 엔티티는 읽는 작업만 수행할 수 있습니다.
이전 예제에서 부서(Department)와 사원(Employee) 테이블의 관계는 1:N(일대다) 관계이며 예제에서는 일 대 다 관계에 대해 설명하겠습니다.
JPA에서 테이블간 조인을 할 경우 어노테이션을 이용하여 조인을 합니다.
* JPA 조인 : @(어노테이션)을 이용해서 조인함
* 부모(부서)-자식(사원) 관계
* 1) 1:N - @OneToMany(부서), @ManytoOne(사원)
* 2) 1:1 - @OneToOne(핸드폰), @OneToOne(사람)
* 3) N:M - @ManyToMany (x)
* 추천 : 1) 1 : N : @OneToMany(부서), @ManyToOne(사원)
* => 양방향 조인(불가피할 때 사용 -> 사용시 여러가지 문제 발생)
* 1-1) : 부서쪽에는 어노테이션 x, 사원에는 @ManyToOne 사용
* => 단방향 조인(추천)
📖 일대다, 다대일 매핑
부서 테이블과 사원 테이블은 부서 테이블의 입장에서는 일대다(하나의 부서에는 여러명의 사원 존재), 사원 테이블의 입장에서는 다대일 관계(사원은 하나의 부서에만 존재할 수 있음)로 볼 수 있습니다.
이러한 관계는 어떻게 구현해야 할지 직접 매핑하면서 알아보겠습니다 :)
📂 Employee.java
package com.example.jpacustomexam.model.exam04;
import com.example.jpacustomexam.model.BaseTimeEntity;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
@Entity
@Table(name = "TB_EMPLOYEE")
@SequenceGenerator(
name = "SQ_EMPLOYEE_GENERATOR"
, sequenceName = "SQ_EMPLOYEE"
, initialValue = 1
, allocationSize = 1
)
@Setter
@Getter
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DynamicInsert
@DynamicUpdate
public class Employee extends BaseTimeEntity {
// @Id : Primary Key 에 해당
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE
, generator = "SQ_EMPLOYEE_GENERATOR"
)
@Column(columnDefinition = "NUMBER")
private Integer eno;
@Column(columnDefinition = "VARCHAR2(255)")
private String ename;
@Column(columnDefinition = "VARCHAR2(255)")
private String job;
@Column(columnDefinition = "NUMBER")
private Integer manager;
@Column(columnDefinition = "VARCHAR2(255)")
private String hiredate;
@Column(columnDefinition = "NUMBER")
private Integer salary;
@Column(columnDefinition = "NUMBER")
private Integer commission;
// @Column(columnDefinition = "NUMBER")
// private Integer dno; // 공통 컬럼(생략, 자동으로 생성해줌)
// 단방향 조인
// 사용법 : @JoinColumn(name = "조인컬럼명")
// 양방향 조인 시 : N + 1 문제 발생
// 1) 즉시 로딩(EAGER) : 기본 설정 -> 지연 로딩(LAZY)
// (fetch = FetchType.LAZY)
// optional = false -> 조인방법(inner, outerjoin)
@ManyToOne
@JoinColumn(name = "dno")
private Department department;
}
우선 사원테이블에 단방향 조인을 설정합니다.
사용법은 @ManyToOne 어노테이션을 작성합니다.
그리고 조인할 컬럼을 설정해야하는데 @JoinColumn 어노테이션을 사용합니다. 예제에서는 dno(부서번호)가 외래키로 지정되어 있으므로 @JoinColumn(name = "dno")로 설정하였습니다.
📂 DepartmentRepository.java
DepartmentRepository 인터페이스를 새로 생성합니다.
package com.example.jpacustomexam.repository.exam04;
import com.example.jpacustomexam.model.exam04.Department;
import com.example.jpacustomexam.model.exam04.Employee;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
/**
* packageName : com.example.jpacustomexam.repository.exam04
* fileName : DepartmentRepository
* author : GGG
* date : 2023-10-19
* description : 부서 레포지토리 (CRUD)
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-19 GGG 최초 생성
*/
@Repository
public interface DepartmentRepository extends JpaRepository<Department, Integer> {
@Query(value = "select e from Employee e inner join e.department d ")
Page<Employee> selectJoinPage2(Pageable pageable);
}
객체쿼리를 사용하여 쿼리문을 작성합니다
📂 DepartmentService.java
DepartmentService 파일을 새로생성합니다.
package com.example.jpacustomexam.service.exam04;
import com.example.jpacustomexam.model.exam04.Employee;
import com.example.jpacustomexam.repository.exam04.DepartmentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@Service
public class DepartmentService {
@Autowired
DepartmentRepository departmentRepository;
// 단방향 조인 예제
public Page<Employee> selectJoinPage2(Pageable pageable){
Page<Employee> page = departmentRepository.selectJoinPage2(pageable);
return page;
}
}
📂 DepartmentController.java
DepartmentController 파일을 새로 생성합니다.
package com.example.jpacustomexam.controller.exam04;
import com.example.jpacustomexam.dto.DeptEmpDto;
import com.example.jpacustomexam.model.exam04.Employee;
import com.example.jpacustomexam.service.exam04.DepartmentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/exam04")
public class DepartmentController {
@Autowired
DepartmentService departmentService;
/** 단방향 조인 예제 */
@GetMapping("/dept/join/paging2")
public ResponseEntity<Object> selectJoinPage2(Pageable pageable) {
try {
Page<Employee> page
= departmentService.selectJoinPage2(pageable);
// todo : Map 자료구조 정보 저장 : 1) 부서 객체, 2) 페이징 정보 (3개)
Map<String, Object> response = new HashMap<>();
response.put("emp", page.getContent()); // 사원 객체
response.put("currentPage", page.getNumber()); // 현재페이지 번호
response.put("totalItems", page.getTotalElements()); // 전체 테이블 건수
response.put("totalPage", page.getTotalPages()); // 전체페이지 수
if (page.isEmpty() == false) {
// 성공
return new ResponseEntity<>(response, HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e){
log.debug(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
📌 실행결과
GET http://localhost:8000/exam04/dept/join/paging2
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 19 Oct 2023 01:37:45 GMT
Keep-Alive: timeout=60
Connection: keep-alive
{
"totalItems": 14,
"totalPage": 1,
"emp": [
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7369,
"ename": "SMITH",
"job": "CLERK",
"manager": 7902,
"hiredate": "1980-12-17 00:00:00",
"salary": 800,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7566,
"ename": "JONES",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-04-02 00:00:00",
"salary": 2975,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7782,
"ename": "CLARK",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-06-09 00:00:00",
"salary": 2450,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 10,
"dname": "ACCOUNTING",
"loc": "NEW YORK"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1987-07-13 00:00:00",
"salary": 3000,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7839,
"ename": "KING",
"job": "PRESIDENT",
"manager": null,
"hiredate": "1981-11-17 00:00:00",
"salary": 5000,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 10,
"dname": "ACCOUNTING",
"loc": "NEW YORK"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"manager": 7788,
"hiredate": "1987-07-13 00:00:00",
"salary": 1100,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7902,
"ename": "FORD",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1981-12-03 00:00:00",
"salary": 3000,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS"
}
},
{
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"eno": 7934,
"ename": "MILLER",
"job": "CLERK",
"manager": 7782,
"hiredate": "1982-01-23 00:00:00",
"salary": 1300,
"commission": null,
"department": {
"insertTime": "2023-10-19 01:37:39",
"updateTime": null,
"dno": 10,
"dname": "ACCOUNTING",
"loc": "NEW YORK"
}
}
],
"currentPage": 0
}
응답 파일이 저장되었습니다.
> 2023-10-19T103745.200.json
Response code: 200; Time: 623ms (623 ms); Content length: 4092 bytes (4.09 kB)
📖 양방향 조인
📂 Department.java 추가
// 양방향 조인 : 1) 순환참조 문제 - 해결 : @JsonManagedReference(부서)
// @JsonBackReference(사원)
// 2) N + 1 문제
// 사용법 : @OneToMany(mappedBy = "사원_연결속성")
@OneToMany(mappedBy = "department")
@JsonManagedReference
private Set<Employee> employee = new HashSet<>(); // 1:n(사원)
}
📂 Employee.java 추가
package com.example.jpacustomexam.model.exam04;
import com.example.jpacustomexam.model.BaseTimeEntity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
/**
* packageName : com.example.jpacustomexam.model.exam04
* fileName : Employee
* author : GGG
* date : 2023-10-19
* description :
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-19 GGG 최초 생성
*/
@Entity
@Table(name = "TB_EMPLOYEE")
@SequenceGenerator(
name = "SQ_EMPLOYEE_GENERATOR"
, sequenceName = "SQ_EMPLOYEE"
, initialValue = 1
, allocationSize = 1
)
@Setter
@Getter
@ToString(exclude = "department") // 순환참조 방지
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DynamicInsert
@DynamicUpdate
public class Employee extends BaseTimeEntity {
// @Id : Primary Key 에 해당
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE
, generator = "SQ_EMPLOYEE_GENERATOR"
)
@Column(columnDefinition = "NUMBER")
private Integer eno;
@Column(columnDefinition = "VARCHAR2(255)")
private String ename;
@Column(columnDefinition = "VARCHAR2(255)")
private String job;
@Column(columnDefinition = "NUMBER")
private Integer manager;
@Column(columnDefinition = "VARCHAR2(255)")
private String hiredate;
@Column(columnDefinition = "NUMBER")
private Integer salary;
@Column(columnDefinition = "NUMBER")
private Integer commission;
// @Column(columnDefinition = "NUMBER")
// private Integer dno; // 공통 컬럼(생략, 자동으로 생성해줌)
// 단방향 조인
// 사용법 : @JoinColumn(name = "조인컬럼명")
// 양방향 조인 시 : N + 1 문제 발생 - 조인이 안되고 각각 SQL문이 생성되는 현상
// 1) 즉시 로딩(EAGER) : 기본 설정 -> 지연 로딩(LAZY)
// (fetch = FetchType.LAZY)
// optional = false -> 조인방법(inner, outerjoin)
// 2) inner join fetch : 조인 쿼리 생성
// 3) application.properties :
// N + 1 sql -> where in (?,?....?) 값으로 변경하는 옵션
// spring.jpa.properties.hibernate.default_batch_fetch_size=1000
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "dno")
@JsonBackReference
private Department department;
}
📖 지연 로딩과 즉시로딩
JPA에서 지연로딩(lazy loading)과 즉시로딩(eager loading)은 중요한 개념중 하나입니다.
엔티티라는 객체의 개념으로 데이터베이스를 구현했기 때문에 연관관계를 가진 각 엔티티 클래스에는 연관관계가 있는 객체들이 필드에 존재하게 됩니다. 연관관계와 상관업싱 즉각 해당 엔티티의 값만 조회하고 싶거나 연관관계를 가진 테이블의 값도 조회하고 싶은 경우 등 여러 조건들을 만족하기 위해 등장한 개념이 지연로딩과 즉시로딩입니다.
📂 DepartmentService.java 추가
// 양방향 조인 예제 : fetch join
public List<Department> selectFetchJoin() {
List<Department> page = departmentRepository.selectFetchJoin();
return page;
}
📂 DepartmentRepository.java 추가
// 양방향 조인 : fetch join -> Paging 안됨
@Query(value = "select distinct d from Department d inner join fetch d.employee e ")
List<Department> selectFetchJoin();
📂 DepartmentController.java 추가
/** 양방향 조인 예제 */
// page=현재페이지번호(0 ~ n), size=전체페이지수
@GetMapping("/dept/fetch/join")
public ResponseEntity<Object> selectFetchJoin() {
try {
List<Department> page
= departmentService.selectFetchJoin();
if (page.isEmpty() == false) {
// 성공
return new ResponseEntity<>(page, HttpStatus.OK);
} else {
// 데이터 없음
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
} catch (Exception e){
log.debug(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
✅ 실행결과
GET http://localhost:8000/exam04/dept/fetch/join
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 19 Oct 2023 02:26:02 GMT
Keep-Alive: timeout=60
Connection: keep-alive
[
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7902,
"ename": "FORD",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1981-12-03 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1987-07-13 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7566,
"ename": "JONES",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-04-02 00:00:00",
"salary": 2975,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7369,
"ename": "SMITH",
"job": "CLERK",
"manager": 7902,
"hiredate": "1980-12-17 00:00:00",
"salary": 800,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"manager": 7788,
"hiredate": "1987-07-13 00:00:00",
"salary": 1100,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7902,
"ename": "FORD",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1981-12-03 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1987-07-13 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7566,
"ename": "JONES",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-04-02 00:00:00",
"salary": 2975,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7369,
"ename": "SMITH",
"job": "CLERK",
"manager": 7902,
"hiredate": "1980-12-17 00:00:00",
"salary": 800,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"manager": 7788,
"hiredate": "1987-07-13 00:00:00",
"salary": 1100,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 10,
"dname": "ACCOUNTING",
"loc": "NEW YORK",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7839,
"ename": "KING",
"job": "PRESIDENT",
"manager": null,
"hiredate": "1981-11-17 00:00:00",
"salary": 5000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7782,
"ename": "CLARK",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-06-09 00:00:00",
"salary": 2450,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7934,
"ename": "MILLER",
"job": "CLERK",
"manager": 7782,
"hiredate": "1982-01-23 00:00:00",
"salary": 1300,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7902,
"ename": "FORD",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1981-12-03 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1987-07-13 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7566,
"ename": "JONES",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-04-02 00:00:00",
"salary": 2975,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7369,
"ename": "SMITH",
"job": "CLERK",
"manager": 7902,
"hiredate": "1980-12-17 00:00:00",
"salary": 800,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"manager": 7788,
"hiredate": "1987-07-13 00:00:00",
"salary": 1100,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 10,
"dname": "ACCOUNTING",
"loc": "NEW YORK",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7839,
"ename": "KING",
"job": "PRESIDENT",
"manager": null,
"hiredate": "1981-11-17 00:00:00",
"salary": 5000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7782,
"ename": "CLARK",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-06-09 00:00:00",
"salary": 2450,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7934,
"ename": "MILLER",
"job": "CLERK",
"manager": 7782,
"hiredate": "1982-01-23 00:00:00",
"salary": 1300,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7902,
"ename": "FORD",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1981-12-03 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1987-07-13 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7566,
"ename": "JONES",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-04-02 00:00:00",
"salary": 2975,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7369,
"ename": "SMITH",
"job": "CLERK",
"manager": 7902,
"hiredate": "1980-12-17 00:00:00",
"salary": 800,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"manager": 7788,
"hiredate": "1987-07-13 00:00:00",
"salary": 1100,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 30,
"dname": "SALES",
"loc": "CHICAGO",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7900,
"ename": "JAMES",
"job": "CLERK",
"manager": 7698,
"hiredate": "1981-12-03 00:00:00",
"salary": 950,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7698,
"ename": "BLAKE",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-05-01 00:00:00",
"salary": 2850,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7844,
"ename": "TURNER",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-08 00:00:00",
"salary": 1500,
"commission": 0
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7499,
"ename": "ALLEN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-20 00:00:00",
"salary": 1600,
"commission": 300
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7521,
"ename": "WARD",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-02-22 00:00:00",
"salary": 1250,
"commission": 500
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7654,
"ename": "MARTIN",
"job": "SALESMAN",
"manager": 7698,
"hiredate": "1981-09-28 00:00:00",
"salary": 1250,
"commission": 1400
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 20,
"dname": "RESEARCH",
"loc": "DALLAS",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7902,
"ename": "FORD",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1981-12-03 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7788,
"ename": "SCOTT",
"job": "ANALYST",
"manager": 7566,
"hiredate": "1987-07-13 00:00:00",
"salary": 3000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7566,
"ename": "JONES",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-04-02 00:00:00",
"salary": 2975,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7369,
"ename": "SMITH",
"job": "CLERK",
"manager": 7902,
"hiredate": "1980-12-17 00:00:00",
"salary": 800,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7876,
"ename": "ADAMS",
"job": "CLERK",
"manager": 7788,
"hiredate": "1987-07-13 00:00:00",
"salary": 1100,
"commission": null
}
]
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"dno": 10,
"dname": "ACCOUNTING",
"loc": "NEW YORK",
"employee": [
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7839,
"ename": "KING",
"job": "PRESIDENT",
"manager": null,
"hiredate": "1981-11-17 00:00:00",
"salary": 5000,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7782,
"ename": "CLARK",
"job": "MANAGER",
"manager": 7839,
"hiredate": "1981-06-09 00:00:00",
"salary": 2450,
"commission": null
},
{
"insertTime": "2023-10-19 02:20:28",
"updateTime": null,
"eno": 7934,
"ename": "MILLER",
"job": "CLERK",
"manager": 7782,
"hiredate": "1982-01-23 00:00:00",
"salary": 1300,
"commission": null
}
]
}
]
응답 파일이 저장되었습니다.
> 2023-10-19T112602.200.json
Response code: 200; Time: 17ms (17 ms); Content length: 13967 bytes (13.97 kB)
📖 1 : 1 관계
2) 1:1 - @OneToOne(핸드폰), @OneToOne(사람)
- Person 과 Phone 모델 클래스 생성
📂 Person.java
package com.example.jpacustomexam.model.exam05;
import com.example.jpacustomexam.model.BaseTimeEntity;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
/**
* packageName : com.example.jpacustomexam.model.exam05
* fileName : Person
* author : GGG
* date : 2023-10-19
* description :
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-19 GGG 최초 생성
*/
@Entity
@Table(name="TB_PERSON")
@SequenceGenerator(
name = "SQ_PERSON_GENERATOR"
, sequenceName = "SQ_PERSON"
, initialValue = 1
, allocationSize = 1
)
@Getter
@Setter
//@ToString(exclude = "emp")
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DynamicInsert
@DynamicUpdate
public class Person extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_PERSON_GENERATOR")
@Column(columnDefinition = "NUMBER")
private Integer no; // 기본키
@Column(columnDefinition = "VARCHAR2(255)")
private String name;
@Column(columnDefinition = "VARCHAR2(255)")
private String job;
// 1:1 관계 설정 : @OneToOne(사람:부모), @OneToOne(폰:자식)
// 사용법 : @OneToOne(mappedBy = "자식 속성명")
@OneToOne(mappedBy = "person")
// 순환참조 해결
@JsonManagedReference
private Phone phone;
}
📂 Phone.java
package com.example.jpacustomexam.model.exam05;
import com.example.jpacustomexam.model.BaseTimeEntity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
/**
* packageName : com.example.jpacustomexam.model.exam05
* fileName : Phone
* author : GGG
* date : 2023-10-19
* description :
* 요약 :
* <p>
* ===========================================================
* DATE AUTHOR NOTE
* —————————————————————————————
* 2023-10-19 GGG 최초 생성
*/
@Entity
@Table(name="TB_PHONE")
@SequenceGenerator(
name = "SQ_PHONE_GENERATOR"
, sequenceName = "SQ_PHONE"
, initialValue = 1
, allocationSize = 1
)
@Getter
@Setter
//@ToString(exclude = "emp")
@ToString
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DynamicInsert
@DynamicUpdate
public class Phone extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_PHONE_GENERATOR")
@Column(columnDefinition = "NUMBER")
private Integer pno;
@Column(columnDefinition = "VARCHAR2(255)")
private String pname;
@Column(columnDefinition = "VARCHAR2(255)")
private String vender;
// 1:1 관계
// 사용법 : @JoinColumn(name ="부모 공통컬럼")
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "no")
@JsonBackReference
private Person person;
}
'Spring Boot > 스프링부트 예제' 카테고리의 다른 글
게시판 페이징 처리 (1) | 2023.10.23 |
---|---|
front + backend 게시판 CRUD 구현 (2) (0) | 2023.10.20 |
JPA 페이징 처리 (1) | 2023.10.18 |
JPA - JPQL(Java Persistence Query Language) (1) | 2023.10.17 |
JPA 상세조회, 저장함수, 수정함수, 삭제함수 (0) | 2023.10.17 |