JPA 연관 관계 매핑 기초
- 엔티티들은 대부분은 다른 엔티티들과 연관 관계를 맺는다.
- 데이터베이스 테이블은 외래키(FK)로 JOIN을 이용해서 관계 테이블을 참조
- 엔티티는 객체 참조를 이용해서 연관된 엔티티를 참조
- 연관 관계 매핑이란 데이터베이스 테이블의 외래 키(FK)를 객체의 참조와 매핑하는 것
즉, 데이터베이스 테이블의 외래키를 객체의 참조 관계로 매핑하는 것
[ 다대일의 단방향 연관 관계 ]
DB 테이블의 일(1), 다(N) 관계에서 외래키는 항상 다 쪽에 있다.
그러므로 객체 양방향 관계에서 연관 관계의 주인은 항상 다 쪽이다.
객체 간의 연관 관계에서는 단방향 관계이므로 member → team 조회는 가능하지만 반대의 경우는 할 수 없다. Member.team 필드를 통해서 팀을 알 수 있지만, 반대로 팀은 회원을 알 수 없다.
@ManyToOne 이란?
다일(N:1) 관계라는 매핑 정보이다.
Employee Department
홍길동 10
또치 20
도라에몽
홍길동, 또치는 10번 부서 / 도라에몽은 20번 부서
직원(Employee) 여러 명이 하나의 부서(Department)에 속하는 경우에 다대일 관계를 사용
@JoinColumn 이란?
@JoinColumn은 외래키를 매핑할 때 사용한다.
name 속성에는 매핑 할 외래키 이름을 지정한다.
회원과 팀 테이블은 TEAM_ID 외래키로 연관 관계를 맺으므로 이 값을 지정한다. 생략할 수 있다.
생략한다면 외래키를 찾을 때 기본 전략을 사용하게 된다.
기본 전략 : 필드명 + _ + 참조하는 테이블의 기본키 컬럼명
회원 객체의 Member.team 필드와 회원 테이블의 MEMBER.TEAM_ID 외래키 컬럼이 매핑되는 것이다.
public class Member {
@Id
@Column(name = "MEMBER_ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
// 연관 관계 매핑
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
}
@JoinColumn은 항상 FK에 거는 것이다 !!
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
}
[ 일대일의 연관 관계 ]
일대일 관계는 양쪽이 서로 하나의 관계만 맺는다.
ex) 회원은 하나의 사물함만 사용하고, 사물함도 하나의 회원에 의해서만 사용된다.
이때, ‘주 테이블 = 회원’, ‘대상 테이블 = 사물함’이다.
테이블은 주 테이블이든 대상 테이블이든 외래키 하나만 있으면 양쪽으로 조회할 수 있다.
일대일 관계는 주 테이블이나 대상 테이블 둘 중 어느 곳이나 외래키를 가질 수 있으며
주 테이블이 나 대상 테이블 중, 누가 외래키를 가질지 선택해야 한다.
주 테이블에 외래키가 있는 경우 주 객체 가 대상 객체를 참조하는 것처럼,
주 테이블에 외래키를 두고 대상 테이블을 참조한다.
외래키를 객체 참조와 비슷하게 사용할 수 있다.
주 테이블이 외래키를 가지고 있으므로, 주 테이블만 확인해도 대상 테이블과 연관 관계가 있는지 알 수 있다
JPATest
src/main/java
exam.app
HelloJPA01.java --- main 메서드
EntityTestApp01.java -- 입력
EntityTestApp02.java -- 출력
EntityTestApp03.java -- 문제
EntityTestApp04.java -- flush하는거 보여주려다 실패
EntityTestApp05.java -- 삭제
exam.entity
EntityTest01.java
EntityTest02.java
Person.java
EntityTest03.java
EntityTest04.java
Team.java
EntityTest05.java
src/main/resources
META-INF
persistence.xml
테이블 생성
Team.java
package exam.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
}
persistence.xml
<class>exam.entity.Team</class> -- 추가하기
조인
EntityTest05.java
한 팀에 여러명
Team이 1
EntityTest05가 다
package exam.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.Data;
@Data
@Entity
public class EntityTest05 {
@Id
private int id;
private String username;
//연관 관계 매핑
@ManyToOne
private Team t; //DB의 필드명은 t_TEAM_ID으로 만들어진다.
//자동 부여가 싫으면 @JoinColumn(name= "TEAM_ID") 이런식으로 필드명을 지정하면 된다.
public EntityTest05() {
}
public EntityTest05(String name, Team team) {
this.username = username;
t = team;
}
}
persistence.xml
<class>exam.entity.EntityTest05</class> -- 추가하기
저 이름이 저 이름이라는 거 한 번 더 체크 !!
즉시 로딩 (Eager Loading)
- 연관된 엔티티를 즉시 로딩된다.
- 부모 엔티티가 로딩될 때 연관된 모든 데이터도 함께 로딩된다.
- 부모 엔티티를 로드할 때 연관된 데이터도 함께 DB에서 조회되어, 한 번의 쿼리로 모든 데이터를 가져온다.
(지금 안 써도 Eager로 잡아버리면 다 로딩이 된다.)
지연 로딩 (Lazy Loading)
- 연관된 엔티티를 실제로 사용할 때까지 데이터를 로딩하지 않는다.
- 연관된 데이터는 해당 필드나 연관된 엔티티에 접근할 때,
즉 실제로 해당 데이터를 참조하거나 사용하려고 할 때 DB에서 로딩된다.
즉시 로딩 (Eager Loading)
Emp.java
package exam.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Data;
@Data
@Entity
public class Emp {
@Id
private int empno;
@Column(length = 14)
private String ename;
@Column(length = 30)
private String job;
private Integer mgr;
private java.sql.Date hiredate;
private int sal;
private Integer comm;
//연관 매핑
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "deptno")
private Dept deptno;
}
한 부서에 여러명이 들어갈 수 있으므로 다대일 관계 ---> ManyToOne으로 잡아줘야한다 !
Emp(다) --- Dept(1)
Dept.java
package exam.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Data;
@Data
@Entity
public class Dept {
@Id
private int deptno;
@Column(length = 20)
private String dname;
@ManyToOne
@JoinColumn(name = "loc_code", referencedColumnName = "loc_code")
private Locations loc_code;
}
Locations.java
package exam.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.Data;
@Data
@Entity
public class Locations {
@Id
@Column(columnDefinition = "char(2)")
private String loc_code;
@Column(length = 20)
private String city;
}
persistence.xml
<class>exam.entity.Emp</class>
<class>exam.entity.Dept</class>
<class>exam.entity.Locations</class>
추가하기
HelloJPA02.java
package exam.app;
import java.util.List;
import exam.entity.Emp;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import jakarta.persistence.Query;
public class HelloJPA02 {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("entitytest");
EntityManager manager = factory.createEntityManager();
Query query = manager.createQuery("select emp from Emp emp", Emp.class); //JPQL, Emp는 엔티티 클래스
List<Emp> list = query.getResultList();
for(Emp emp : list) {
System.out.println(emp.getEname());
}
System.out.println("데이터 총 개수 = " + list.size());
factory.close();
manager.close();
}
}
지연 로딩 (Lazy Loading)
Emp.java
//@ManyToOne(fetch = FetchType.EAGER)
@ManyToOne(fetch = FetchType.LAZY)
package exam.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.Data;
@Data
@Entity
public class Emp {
@Id
private int empno;
@Column(length = 14)
private String ename;
@Column(length = 30)
private String job;
private Integer mgr;
private java.sql.Date hiredate;
private int sal;
private Integer comm;
//연관 매핑
//@ManyToOne(fetch = FetchType.EAGER)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "deptno")
private Dept deptno;
}
LAZY로 잡으니까 EMP 하나만 불러오는 것을 확인할 수 있다 !!
HelloJPA03.java
package exam.app;
import java.util.List;
import exam.entity.Emp;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import jakarta.persistence.Query;
public class HelloJPA03 {
public static void main(String[] args) throws InterruptedException {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("entitytest");
EntityManager manager = factory.createEntityManager();
Query query = manager.createQuery("select emp from Emp emp", Emp.class); //JPQL, Emp는 엔티티 클래스
List<Emp> list = query.getResultList();
for(Emp emp : list) {
System.out.println("직원명 : " + emp.getEname());
Thread.sleep(1000);
if(emp.getDeptno() != null)
System.out.println("부서명 : " + emp.getDeptno().getDname());
else
System.out.println("부서명 : 없음");
}//for
System.out.println("데이터 총 개수 = " + list.size());
factory.close();
manager.close();
}
}
처음에 emp 테이블을 읽어온다.
RESEARCH 부서명을 찾는다.
SALES 부서명을 찾는다.
위에서 이미 찾아놔서 그대로 찍히고
ACCOUNTING 부서명을 찾는다.
이제 다 찾아봤으니 바로 찍힌다.
데이터를 읽어고 조인이 필요하면 그 때 조인하는게 LAZY이다 !!
fetch join
- 연관된 엔티티들을 한 번의 쿼리로 모두 가져온다.
- 여러 개의 연관된 엔티티들을 조인하여 한 번의 쿼리로 모든 데이터들을 가져온다.
HelloJPA04.java
package exam.app;
import java.util.List;
import exam.entity.Emp;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import jakarta.persistence.Query;
public class HelloJPA04 {
public static void main(String[] args) {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("entitytest");
EntityManager manager = factory.createEntityManager();
String fetchJoinJpql = "select distinct t "
+ "from Emp t left join fetch t.deptno "
+ "left join fetch t.deptno.loc_code";
Query query = manager.createQuery(fetchJoinJpql, Emp.class);
List<Emp> list = query.getResultList();
for(Emp emp : list) {
System.out.println(emp);
}
System.out.println("데이터 총 개수 = " + list.size());
factory.close();
manager.close();
}
}
Spring Data
Spring Data의 목적은 기본 데이터 저장소의 특수한 특성을 유지하면서
데이터 접근을 위한 친숙하고 일관된 Spring 기반의 프로그래밍 모델을 제공하는 프로젝트이다.
Spring Data는 데이터 접근 기술로서 relational and non-relational database, map-reduce 프레임워크,
클라우드 기반의 서비스를 쉽게 사용할 수 있도록 해준다.
Spring Data는 데이터베이스와 관련된 많은 하위 프로젝트(Spring Data JPA, Spring Data REST, …)를
포함하는 포괄적인 프로젝트이다.
Spring Data JPA
Spring Data JPA 는 Spring Framework에서 JPA를 편리하게 사용할 수 있도록 지원하는 프로젝트로서
CRUD 처리를 위한 공통 인터페이스를 제공한다.
Repository 개발 시 인터페이스만 작성해도 실행 시점에 Spring Data JPA가
구현(자식) 객체를 동적으로 생성해서 주입시키므로 데이터 접근 계층을 개발할 때
구현 클래스 없이 인터페이스만 작성 해도 개발을 완료할 수 있도록 지원한다.
Spring Data JPA를 사용하기 위해 'JpaRepository' 인터페이스를 상속한 Repository 인터페이스를 정의한다. 단지 인터페이스를 상속했을 뿐인데,
기본적인 메서드를 이미 정의한 상태이며 정의한 인터페이스를 구현할 필요도 없다.
Spring FW가 알아서 해준다.
SAVE는 두 가지 역할을 한다. 데이터가 없으면 저장해주고 있으면 업데이트 해준다.
Spring Starter Project
Chapter03JPA
잘 들어왔나 확인 !!
application.properties
spring.application.name=Chapter03JPA
# Server Port
server.port=8080
server.address=localhost
# MySQL
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Seoul
spring.datasource.username=root
spring.datasource.password=1234
# JPA
## 스키마 생성 - create(기존 테이블이 있으면 삭제 후 생성), update(변경된 부분만 반영)
spring.jpa.hibernate.ddl-auto=update
## DDL 생성 시 데이터베이스 고유의 기능을 사용하겠는가?
spring.jpa.generate-ddl=true
## api 호툴 시 실행되는 sql문을 콘솔에 보여줄 것인가?
spring.jpa.show-sql=true
## 사용할 데이터베이스
spring.jpa.database=mysql
## MySQL 상세 지정
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
Chapter03JpaApplication에서 실행하기 !!
굳이 hibernate.dialect를 안 써도 된다는 말 !!
#spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
주석걸어도된다 !!
HHH90000025: MySQLDialect는 'hibernate.dialect'를 사용하여 명시적으로 지정할 필요가 없습니다
(속성 설정을 제거하면 기본적으로 선택됩니다).
스프링 부트 3.2.x으로 버전업되면서 하이버네이트도 버전업되어
이제 MySQL 방언을 명시적으로 지정하지 않아도 된다.
Spring Starter Project
Chapter03JPA
src/main/java
com.example.demo
Chapter03JpaApplication.java (main 메서드)
BoardController.java
BoardService.java(인터페이스)
BoardServiceImpl.java
BoardDAO.java(인터페이스)
BoardDTO.java
BoardEntity.java
BoardController.java
다 같은 패키지 안에 있으므로 이제는 ComponentScan으로 해서 패키지 등록을 하지 않아도 된다 !!
@RestController는 반환되는 애들은 브라우저에 바로 뿌린다 !!
package com.example.demo;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BoardController {
}
BoardServiceImpl.java
@Service
package com.example.demo;
import org.springframework.stereotype.Service;
@Service
public class BoardServiceImpl implements BoardService {
}
BoardDAO.java
@Repository
package com.example.demo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BoardDAO extends JpaRepository<BoardEntity, Integer>{
}
BoardDTO.java
@Component
1인분 담당이에요 표시 !!
package com.example.demo;
import org.springframework.stereotype.Component;
@Component
public class BoardDTO {
}
BoardEntity.java
@Data
@Entity
@Table(name = "boardtbl")
package com.example.demo;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@Entity
@Table(name = "boardtbl")
public class BoardEntity {
@Id
@Column(name = "seq")
//@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int seq;
}
run as 해보면 워크벤치에서는 테이블 생성된 것을 확인할 수 있다 !!
원래는 콘솔 창에도 떠야되는데 왜 안 뜨는지는 모르겠,,,,,
BoardEntity.java
package com.example.demo;
import java.time.LocalDateTime;
import org.hibernate.annotations.UpdateTimestamp;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
@Data
@Entity
@Table(name = "boardtbl")
public class BoardEntity {
@Id
@Column(name = "seq")
//@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BOARD_SEQ_GENERATOR")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int seq;
@Column(name = "id", nullable = false, unique = true, length = 30)
private String id;
@Column(name = "name", nullable = false, length = 30)
private String name;
@Column(name = "subject")
private String subject;
@Column(name = "content")
private String content;
//private Timestamp logtime;
//@CreationTimestamp //엔티티가 생성되는 시점의 시간 등록 - inset할 때 자동으로 시간 등록
@UpdateTimestamp //update 할 때 자동으로 시간 등록
private LocalDateTime logtime = LocalDateTime.now();
}
application.properties
새로 만들게 잠깐만 여기 create로 바꾸기 !! 다시 update로 바꾸기 !!
# JPA
## 스키마 생성 - create(기존 테이블이 있으면 삭제 후 생성), update(변경된 부분만 반영)
spring.jpa.hibernate.ddl-auto=create
위에있는거를 정리한 것이다
Hibernate: drop table if exists boardtbl
Hibernate: create table boardtbl (
seq integer not null auto_increment,
logtime datetime(6),
id varchar(30) not null,
name varchar(30) not null,
content varchar(255),
subject varchar(255),
primary key (seq)) engine=InnoDB
Hibernate: alter table boardtbl add constraint UK6imivx4q8s3jquftc7gpisib3 unique (id)
Spring Starter Project
Chapter03JPA
src/main/java
com.example.demo
Chapter03JpaApplication.java (main 메서드)
BoardController.java
BoardService.java(인터페이스)
BoardServiceImpl.java
BoardDAO.java(인터페이스)
BoardDTO.java
BoardEntity.java
src/test/java
com.example.demo
Chapter03JpaApplicationTests.java
JPA_BoardRepositoryTest.java
JPA_BoardRepositoryTest.java
DB 내용 출력하기
package com.example.demo;
import java.util.List;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
//내장 DB인 H2를 수행하지 않겠다.
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
//DataJpaTest를 사용하면 자동으로 EmbededDatabase-H2를 사용하게 된다.
//MySQL과 같이 외부의 DB를 연결하려는 경우엔 이 어노테이션을 설정한다.
@DataJpaTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JPA_BoardRepositoryTest {
@Autowired
private BoardDAO boardDAO;
public void list1() {
List<BoardEntity> list = boardDAO.findAll();
list.stream().forEach(System.out::println);
}
}
임의로 데이터 집어넣기 !!
insert into boardtbl(id, name, content, subject) values('hong', '홍길동', '홍길동전', '착한의적');
insert into boardtbl(id, name, content, subject) values('ddochi', '또치', '아기공룡 둘리', '또치는 타조이고 여자다');
오름차순
JPA_BoardRepositoryTest.java
@Test
public void list2() {
List<BoardEntity> list = boardDAO.findAll(Sort.by("name").ascending());
list.stream().forEach(System.out::println);
}
선 긋기
@BeforeEach
public void solid() {
System.out.println("-".repeat(80));
System.out.println();
}
AOP처럼 자동으로 껴진다 !
내림차순
@Test
public void list3() {
List<BoardEntity> list = boardDAO.findAll(Sort.by("name").descending());
list.stream().forEach(System.out::println);
}
페이징 처리
데이터 추가로 넣기
insert into boardtbl(id, name, content, subject) values('conan', '코난', '명탐정 코난', '약은 함부러 먹으면 안된다.');
insert into boardtbl(id, name, content, subject) values('jjangu', '짱구', '짱구는 못말려', '떡잎방범대');
insert into boardtbl(id, name, content, subject) values('apple', '사과', '빨간 사과', '맛있다');
PageRequest.of(page, size)
@Test
public void list4() {
Page<BoardEntity> list = boardDAO.findAll(PageRequest.of(0, 2));
list.stream().forEach(System.out::println);
}
페이징 처리돼서 두 개만 불러온 것을 확인할 수 있다 !!
1페이지에서 2개 !
@AfterEach
public void solid2() {
System.out.println("=".repeat(80));
System.out.println();
}
@Test
public void list5() {
Page<BoardEntity> list = boardDAO.findAll(PageRequest.of(1, 2));
list.stream().forEach(System.out::println);
}
2페이지에서 2개 !
1페이지당 3개씩 꺼내는 걸로 했을 때 !
@Test
public void list4() {
Page<BoardEntity> list = boardDAO.findAll(PageRequest.of(0, 3));
list.stream().forEach(System.out::println);
}
@Test
public void list5() {
Page<BoardEntity> list = boardDAO.findAll(PageRequest.of(1, 3));
list.stream().forEach(System.out::println);
}
이름순으로 sort해서 첫 번째 페이지에서 3개만 출력 !
@Test
public void list6() {
Page<BoardEntity> list = boardDAO.findAll(PageRequest.of(0, 3, Sort.by("name")));
list.stream().forEach(System.out::println);
}
쿼리 메서드
위의 메서드들은 모든 엔티티에 대해 공통으로 쓰일 수 있는 메서드를 제공하지만,
사실 비즈니스 로직을 다루는 것은 그리 간단하지 않다.
조건을 지정하여 조회하거나, 제거하거나 저장할 수 있는 기능들을 커스터마이징 해야 한다.
Spring Data JPA는 Repository를 커스터마이징 하기 위해 쿼리 메서드 기능을 제공한다.
1. 메서드 이름으로 쿼리 생성 -> 간단한 쿼리 처리 시 좋음
2. @Query 안에 JPQL 정의 -> 복잡한 쿼리 처리 시 좋음
3. 메서드 이름으로 JPA NamedQuery 호출 (잘 안쓰임)
Spring Starter Project
Chapter03JPA
src/main/java
com.example.demo
Chapter03JpaApplication.java (main 메서드)
BoardController.java
BoardService.java(인터페이스)
BoardServiceImpl.java
BoardDAO.java(인터페이스)
BoardDTO.java
BoardEntity.java
src/test/java
com.example.demo
Chapter03JpaApplicationTests.java
JPA_BoardRepositoryTest.java
JPA_BoardRepository2Test.java
JPA_BoardRepository2Test.java
package com.example.demo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
//내장 DB인 H2를 수행하지 않겠다.
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
//DataJpaTest를 사용하면 자동으로 EmbededDatabase-H2를 사용하게 된다.
//MySQL과 같이 외부의 DB를 연결하려는 경우엔 이 어노테이션을 설정한다.
@DataJpaTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JPA_BoardRepository2Test {
@Autowired
private BoardDAO boardDAO;
@BeforeEach
public void solid() {
System.out.println("-".repeat(80));
System.out.println();
}
@AfterEach
public void solid2() {
System.out.println("=".repeat(80));
System.out.println();
}
@Test
@Order(1) //실행순서를 정한다, 숫자가 작을 수록 먼저 실행한다.
public void insert() {
BoardEntity boardEntity = new BoardEntity();
boardEntity.setId("banana");
boardEntity.setName("바나나");
boardEntity.setSubject("노란 바나나");
boardEntity.setContent("바나나는 맛있다~!~!~!");
boardDAO.save(boardEntity);
}
}
select * from boardtbl;
db에는 안 들어간다.
왜냐 애네는 자동으로 rollback을 해준다 !!
기본으로 Rollback(true) 이렇게 설정되어있다.
Rollback이 기본이다. DML문 수행한 후에 rollback을 하고싶지 않으면 false로 설정해주어야한다.
@Test
@Order(1) //실행순서를 정한다, 숫자가 작을 수록 먼저 실행한다.
//@Rollback(true) //Rollback이 기본이다. DML문 수행한 후에 rollback을 하고싶지 않으면 false로 설정해주어야한다.
@Rollback(false)
public void insert() {
BoardEntity boardEntity = new BoardEntity();
boardEntity.setId("banana");
boardEntity.setName("바나나");
boardEntity.setSubject("노란 바나나");
boardEntity.setContent("바나나는 맛있다~!~!~!");
boardDAO.save(boardEntity); //레코드가 없으면 입력(insert), 있으면 수정(update)
}
이제 데이터가 잘 들어가는 것을 확인할 수 있다 !!
boardDAO.save(boardEntity); //레코드가 없으면 입력(insert), 있으면 수정(update)
@Order(2) -- 앞에있는 @Order(1)을 수행한다음 수행하라는 의미
@Test
@Order(2)
public void list() {
List<BoardEntity> list = boardDAO.findAll();
list.stream().forEach(System.out::println);
}
전체를 한 번 더 실행하면 에러가 뜰 수밖에 없다 !! 왜냐 id는 유일한 키로 잡았으므로 바나나를 또 넣으려고 해서 그렇다
'Spring Boot' 카테고리의 다른 글
DAY 91 - JPA - 아이디 중복검사 / 회원가입 / 회원목록 / 페이징처리 (2024.11.14) (1) | 2024.11.14 |
---|---|
DAY 90 - JPA (2024.11.13) - 쿼리메서드 / 메인화면, 회원가입폼 (2024.11.13) (0) | 2024.11.14 |
DAY 88 - JPA (2024.11.11) (0) | 2024.11.12 |
DAY 86 - Spring Boot DB연결 + Thymeleaf (2024.11.07) (1) | 2024.11.08 |
DAY 85 - Thymeleaf (2024.11.06) (0) | 2024.11.06 |