# Repository 인터페이스
- 기존의 DAO(Data Access Object) 와 동일한 개념
- 비즈니스 클래스에서는 Repository 를 이용하여 실질적인 데이터베이스 연동을 처리
# 쿼리 메서드
- JPA 를 이용해서 목록 기능을 구현할 때에는
JPQL( Java Persistence Query Language ) 를 이용
- JPQL 은 검색 대상이 테이블이 아닌 엔티티라는 것만 제외하면
기본 구조와 문법이 기존의 SLQ과 유사
- JPA 에서는 복잡한 JPQL 을 메서드로 처리할 수 있도록 '쿼리 메서드'라는 특별한 기능을 제공
- 쿼리 메서드를 이요할 때 가장 많이 사용하는 문법은 검색하려는 엔티티에서 특정 변수의 값만 조회하는 것
메서드 이름을 find 로 시작하면서 조회할 변수들을 적절하게 조합하면 됨
> find + 엔티티 이름 + By + 변수 이름(첫 글자 대문자) > camel 표기법
Ex) findBoardByTitle() : Board 엔티티에서 title 변수 값만 조회
- 쿼리 메서드를 작성할 때 엔티티 이름은 생략할 수 있다
이름이 생략되면 현재 사용하는 Repository 인터페이스에 선언된
타입 정보를 기준으로 엔티티 이름이 자동 적용
> public interface Repository extends CrudRepository<Board, Long> : 여기서 Board
find + By + 변수 이름
# 복잡한 쿼리를 작성하는 경우나 연관관계에 기반한 조인검색을 처리하기 위해서는 JPQL 을 사용
특정 데이터베이스에 종속적인 네이티브 쿼리를 사용해야 하는 경우에는,
@Query 을 적용
# QueryDSL
- JPA 에서는 @Query 를 이용해서 application 에서 사용할 쿼리를 관리
@Query 로 등록한 쿼리는 프로젝트가 로딩되는 시점에 파싱되기 때문에 고정된 SQL만 사용할 수 있다
Mybatis 처럼 동적으로 쿼리를 처리하려면 QueryDSL(Query Domain Specific Language )
board-mapping.xml
<select id="getBoardList" resultMap="boardResult">
SELECT *
FROM BOARD
WHERE 1 = 1
<if test="searchCondition == 'TITLE' ">
AND TITLE LIKE '%'||#{searchKeyword}||'%'
</if>
<if test="searchCondition == 'CONTENT'>
AND CONTENT LIKE '%'||#{searchKeyword}||'%'
</if>
ORDER BY SEQ DESC
</select>
1. QueryDSL 설정
- QueryDSL 관련 라이브러리 추가
https://mvnrepository.com 접속 > querydsl 검색
> Querydsl JPA : QueryDSL JPA 라이브러리
Query APT : 쿼리 타입(Q)를 생성할 때 필요한 라이브러리
------------------------------------------------------------------------------------------------
pom.xml 에 추가
<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-jpa -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.querydsl/querydsl-apt -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
</dependency>
------------------------------------------------------------------------------------------------
2. QueryDSL Plugin 추가
- QueryDSL 을 사용하려면 엔티티 클래스 기반으로 쿼리 타입이라는 쿼리용 클래스를 생성해야 한다
> pom.xml 파일에서 우클릭 -> [Maven]-[Add Plugin] 선택
Group Id : com.mysema.maven
Artifact Id : apt-maven-plugin
Version : 1.1.3
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions><!-- 만약 에러가 발생한다면 eclipse.ini에 –vm 옵션 설정을 하면 된다.-->
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration><!--경로설정은 자신의 경로에 맞게-->
<outputDirectory>src/main/querydsl</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
# 연관관계 매핑
- 관계형 데이터베이스에서 테이블 하나로 애플리케이션에서 사용하는 모든 데이터를 관리하는 것은 불가능 하다
따라서 관련된 데이터를 여러 테이블에 나누어 저장하고 테이블을 조인하여 데이터 처리
- 테이블이 관계를 맺듯이 엔티티 역시 다른 엔티티와 관계를 맺고 있으며
이 관계를 통해 데이터를 관리할 수 있다.
- 테이블은 PK 와 FK 를 기반으로 연관 관계를 맺지만,
객체는 참조 변수를 통해 연관관계를 맺기 때문에,
테이블의 연관과 엔티티의 연관이 정확하게 일치하지 않는다.
Ex) 어떤 회원이 탈퇴를 했을 때 그 회원이 작성한 게시글 까지 같이 사라짐
package com.web.domain;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(exclude = "boardList")
@Entity
@Table(name = "MYBOARD")
public class Board {
@Id
@GeneratedValue
private Long seq;
private String title;
// private String writer;
private String content;
@Temporal(TemporalType.TIMESTAMP)
private Date createDate;
private Long cnt;
// ------------------------------------------------------------------------------
// Member 생성 이후
@ManyToOne // 다대일(N:1) 관계 설정 내부 조인 nullable 속성 쓰지 않을 때
@JoinColumn(name = "MEMBER_ID", nullable = false) // FK Join : 외부 조인 nullable를 false로 하면 내부조인 됨
private Member member; // Member 객체와 연관관계 매핑
// 게시판 객체에 회원 객체를 설정할 때 회원이 소유한 게시글 컬렉션에 자신(게시글)도 자동 저장
public void setMember(Member member) {
this.member = member;
member.getBoardList().add(this);
}
}
package com.web.domain;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString(exclude = "member")
@Entity
@Table(name = "MYUSER")
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String password;
private String name;
private String role;
// # mappedBy
// - 양방향 연관관계에서 연관관계의 주인을 지정할 때 사용
// - 연관관계 주인 테이블에 FK가 있는 곳으로 지정
// # fetch
// - 회원 정보를 조회할 때 연관관계에 있는 게시판 정보도 같이 조회할 것인지를 결정
// - fetch 속성의 기본값을 LAZY
// - EAGER 로 설정하면 회원정보를 가져올 때 회원이 등록한 게시글 목록도 같이 조회
// @OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
// # 영속성 전이
// - CascadeType.All 을 적용하면, 회원 객체가 영속되거나 수정 또는 삭제 될 때
// 회원과 관련된 게시판도 같이 변경 또는 삭제
@OneToMany(mappedBy = "member", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private List<Board> boardList = new ArrayList<>();
}
package com.web.persistence;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.CrudRepository;
import com.web.domain.Board;
/*
* # CurdRepository
* - 기본적인 CRUD 기능을 제공
* - CurdRepository<T, ID>
* > T : 엔티티 클래스 타입
* ID : 식별자 타입(@Id 로 매핑한 식별자 변수의 타입)
*
* # 스프링 데이터 JPA 를 사용하는 경우 별도의 구현 클래스를 만들지 않고
* 인터페이스만 정의함으로써 기능을 사용할 수 있다
* 스프링 부트가 내부적으로 인터페이스에 대한 구현 객체를 자동으로 생성해 줌
*/
public interface Repository extends CrudRepository<Board, Long>{
// // 쿼리 메서드
// List<Board> findByTitle(String searchKeyword);
// // 포함된 글자
// List<Board> findByContentContaining(String searchKeyword);
// // OR
// List<Board> findByTitleContainingOrContentContaining(String title, String content);
// // 정렬
// List<Board> findByTitleContainingOrderBySeqDesc(String searchKeyword);
// // 페이징과 정렬 처리
// // import org.springframework.data.domain.Pageable;
//// List<Board> findByTitleContaining(String searchKeyword, Pageable paging);
// // Page<T> 타입 사용
// Page<Board> findByTitleContaining(String searchKeyword, Pageable paging);
}
package com.web.persistence;
import java.util.List;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import com.web.domain.Board;
/*
* # @Query
* - JQPL 사용시 검색대상은 테이블이 아니라 엔티티
*/
public interface Repository2 extends CrudRepository<Board, Long>{
// // 위치 기반 파라미터
// // - WHERE 절에 LIKE 연산자를 사용하고, 사용자 입력 값이 '?1' 에 바인딩 되어 검색
// // 엔티티(클래스) 이름과 table 자리에 들어가는 이름이 같아야 함
// @Query("SELECT b FROM Board b WHERE b.title LIKE %?1% ORDER BY b.seq DESC")
// List<Board> queryAnnotationTest(String searchKeyword); // 매개변수가 쿼리문의 ? 으로 들어감
//
// // 이름 기반 파라미터
// // - ":searchKeyword" 파라미터에 매개변수로 받은 searchKeyword 값이 바인딩 되도록
// // @Param 을 사용
// @Query("SELECT b FROM Board b WHERE b.title LIKE %:searchKeyword% ORDER BY b.seq DESC")
// List<Board> queryAnnotationTest2(@Param("searchKeyword") String searchKeyword);
//
// // 특정 변수만 조회
// // - 특정 변수 값만 조회할 때 검색결과로 엔티티 객체로 조회되는 것이 아니라
// // 여러 변수 값들이 조회됨
// // >> findByTitle() 메서드의 리턴 타입을 'List<Object[]>' 로 해야 함
// @Query("SELECT b.seq, b.title, b.writer, b.createDate "
// + "FROM Board b WHERE b.title LIKE %?1%"
// + "ORDER BY b.seq DESC")
// List<Object[]> queryAnnotationTest3(String searchKeyword);
//
// // 네이티브 쿼리 사용
// // 네이티브 쿼리는 일반 쿼리문이므로 테이블 이름 자리에 엔티티 이름과 맞출 필요가 없다
// // 하지만 @Query 속성으로 nativeQuery = true 써줘야 함
// // - 네이티브 쿼리를 사용하면 특정 데이터 베이스에 종속되는 문제가 있지만
// // 성능상 특정 데이터베이스에 최적화된 쿼리를 사용해야 하는 경우에 유용하다
// // - 쿼리문이 JPQL 이 아닌 nativeQuery임을 알려주는 것이 'nativeQuery = true' 이다
// @Query(value = "SELECT seq, title, writer, create_Date "
// + "FROM board WHERE title LIKE '%'||?1||'%' "
// + "ORDER BY seq DESC", nativeQuery = true)
// List<Object[]> queryAnnotationTest4(String searchKeyword);
//
// // 페이징 및 정렬 처리하기
// // - @Query 를 사용하더라도 페이징 처리를 위한 Pageable 인터페이스는
// // 쿼리 메서드와 동일하게 사용할 수 있다
// // Sort 를 이용하여 조회 결과에 대해서 정렬도 추가할 수 있다.
// @Query("SELECT b FROM Board b ORDER BY b.seq DESC")
// List<Board> queryAnnotationTest5(Pageable paging);
}
package com.web.persistence;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
import com.web.domain.Board;
public interface DynamicRepository extends CrudRepository<Board, Long>, QuerydslPredicateExecutor<Board>{
}
package com.web.persistence;
import org.springframework.data.repository.CrudRepository;
import com.web.domain.Member;
public interface MemberRepository extends CrudRepository<Member, String>{
}
package com.web;
import java.util.Date;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.web.domain.Board;
import com.web.persistence.Repository;
/*
* # 엔티티를 영속성 컨텍스트에 저장하기 위해서 JPA 의 persist() 메서드를 사용하지만,
* CrudRepository 인터페이스를 사용할 때에는 save() 메서드를 사용한다.
*
*/
@SpringBootTest
public class BoardRepositoryTest {
@Autowired
private Repository boardRepo;
// 등록 테스트
@Test
public void testInsertBoard() {
Board board = new Board();
board.setTitle("두 글");
board.setWriter("user2");
board.setContent("등록 테스트");
board.setCreateDate(new Date());
board.setCnt(0L);
boardRepo.save(board);
}
//----------------------------------------------------------------------
// 조회 테스트
@Test
public void testGetBoard() {
Board board = boardRepo.findById(1L).get();
System.out.println("---> " + board.toString());
}
//----------------------------------------------------------------------
// 수정 테스트
@Test
public void testUpdateBoard() {
System.out.println("- 1번 게시글 조회 -");
Board board = boardRepo.findById(2L).get();
System.out.println("1번 글 수정");
board.setWriter("작가 수정");
board.setContent("내용 수정");
board.setTitle("제목 수정");
boardRepo.save(board);
// 수정된 내용 확인
System.out.println("---> " + board.toString());
}
//----------------------------------------------------------------------
// 삭제 테스트
@Test
public void deleteBoard() {
boardRepo.deleteById(2L);
}
@Test
public void testGetBoard() {
Board board = boardRepo.findById(1L).get();
System.out.println("---> " + board.toString());
}
}
package com.web;
import java.util.Date;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import com.querydsl.core.BooleanBuilder;
import com.web.domain.Board;
import com.web.domain.QBoard;
import com.web.persistence.DynamicRepository;
import com.web.persistence.Repository;
import com.web.persistence.Repository2;
@SpringBootTest
public class DynamicQueryTest {
@Autowired
private DynamicRepository dr;
@Autowired
private Repository br;
@BeforeEach
public void dataPrepare() {
for(int i=0; i<200; i++) {
Board board = new Board();
board.setTitle("테스트 제목 " + i);
board.setWriter("user"+ i);
board.setContent(i+" 번째 내용");
board.setCreateDate(new Date());
board.setCnt(0L);
br.save(board);
}
}
@Test
public void testDynamicQuery() {
// 검색 조건
String searchCondition = "content";
String searchKeyword = "1 번";
// com.querydsl.core.BooleanBuilder 객체 이용하면 가변적인 파라미터 값에
// 동적으로 And 나 OR 에 해당하는 조건을 추가할 수 있다.
BooleanBuilder builder = new BooleanBuilder();
QBoard qBoard = QBoard.board;
if(searchCondition.equals("TITLE")) {
builder.and(qBoard.title.like("%" + searchKeyword + "%"));
} else if(searchCondition.equals("content")) {
builder.and(qBoard.content.like("%" + searchKeyword + "%"));
}
// 페이징 처리
Pageable paging = PageRequest.of(0, 5);
Page<Board> list = dr.findAll(builder, paging);
System.out.println("- 검색 결과 -");
for(Board board : list) {
System.out.println("---> " + board.toString());
}
}
}
package com.web;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import com.web.domain.Board;
import com.web.persistence.Repository;
@SpringBootTest
public class QueryMethodTest {
@Autowired
private Repository boardRepo;
// 쿼리 메서드 테스트용 데이터
@BeforeEach
public void dataPrepare() {
for(int i=0; i<200; i++) {
Board board = new Board();
board.setTitle("테스트 제목 " + i);
board.setWriter("user"+ i);
board.setContent(i+" 번째 내용");
board.setCreateDate(new Date());
board.setCnt(0L);
boardRepo.save(board);
}
}
// 쿼리 메서드
@Test
public void findByTitle() {
List<Board> list = new ArrayList<>();
list = boardRepo.findByTitle("테스트 제목 1");
System.out.println("- 검색 결과 -");
for(Board board : list) {
System.out.println("---> "+ board.toString());
}
}
// LIKE 연산자 사용
// - 특정 단어가 포함된 목록을 검색하려면 Like 연산자와 Containing 키워드를 사용
@Test
public void findByContentContaining() {
List<Board> list = boardRepo.findByContentContaining("10");
System.out.println("- 검색 결과2 포함 -");
for(Board board : list) {
System.out.println("---> "+ board.toString());
}
}
// 여러 조건 사용
// - 제목 혹은 내용에 들어가는 검색어가 동일하더라도 하나의 변수로 처리할 수 없음
// 즉, 원하는 검색값에 대한 매개변수를 다 넣어 줘야 함
@Test
public void findByTitleContainingOrContentContaining() {
List<Board> list = boardRepo.findByTitleContainingOrContentContaining("10", "10");
System.out.println("- 검색 결과3 포함, OR -");
for(Board board : list) {
System.out.println("---> "+ board.toString());
}
}
// 데이터 정렬
// - 데이터를 정렬 해서 조회할 때에는 'OrderBy' + 변수 + 'Asc or Desc' 사용
@Test
public void findByTitleContainingOrderBySeqDesc() {
List<Board> list = boardRepo.findByTitleContainingOrderBySeqDesc("10");
System.out.println("- 검색 결과4 정렬 -");
for(Board board : list) {
System.out.println("---> "+ board.toString());
}
}
// 페이징과 정렬 처리
// - 모든 쿼리 메서드는 마지막 파라미터로 페이징 처리를 위한 Pageable 인터페이스와
// 정렬을 처리하는 Sort 인터페이스를 추가할 수 있다.
@Test
public void findByTitleContaining() {
// - 첫 번쨰 인자 0 은 페이지 번호.
// 0 부터 시작하기 때문에 첫번째 페이지를 보고 싶으면 0 으로 설정
// - 두 번째 인자인 5는 검색할 데이터의 갯수
// - 페이징을 처리할 때 데이터를 정렬해서 출력하려면 Sort 클래스를 사용
Pageable pageable = PageRequest.of(1, 5); // >> 0번째 페이지에서 5개
Pageable pageable = PageRequest.of(1, 5, Sort.Direction.DESC,"seq");
List<Board> list = boardRepo.findByTitleContaining("10", pageable);
System.out.println("- 검색 결과5 정렬과 페이징(List) -");
for(Board board : list) {
System.out.println("---> "+ board.toString());
}
}
@Test
public void findByTitleContaining() {
Pageable pageable = PageRequest.of(1, 5, Sort.Direction.DESC,"seq");
Page<Board> pageInfo = boardRepo.findByTitleContaining("10", pageable);
System.out.println("- 검색 결과6 정렬과 페이징 Page<T> -");
System.out.println("PAGE SIZE : " + pageInfo.getSize());
System.out.println("TOTAL PAGES : " + pageInfo.getTotalPages());
System.out.println("TOTAL COUNT : " + pageInfo.getTotalElements());
System.out.println("NEXT : " + pageInfo.nextPageable());
List<Board> list = pageInfo.getContent();
for(Board board : list) {
System.out.println("---> "+ board.toString());
}
}
}
package com.web;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import com.web.domain.Board;
import com.web.persistence.Repository2;
@SpringBootTest
public class QueryAnnotationTest {
@Autowired
private Repository2 br;
@BeforeEach
public void dataPrepare() {
for(int i=0; i<200; i++) {
Board board = new Board();
board.setTitle("테스트 제목 " + i);
board.setWriter("user"+ i);
board.setContent(i+" 번째 내용");
board.setCreateDate(new Date());
board.setCnt(0L);
br.save(board);
}
}
@Test
public void testQueryAnnotationTest() {
List<Board> list = br.queryAnnotationTest("테스트 제목 10");
System.out.println("- 검색 결과 -");
for(Board board : list) {
System.out.println("-----> " + board.toString());
}
}
@Test
public void testQueryAnnotationTest2() {
List<Board> list = br.queryAnnotationTest("테스트 제목 10");
System.out.println("- 검색 결과2 -");
for(Board board : list) {
System.out.println("-----> " + board.toString());
}
}
@Test
public void testQueryAnnotationTest3() {
List<Object[]> list = br.queryAnnotationTest3("테스트 제목 10");
System.out.println("- 검색 결과3 특정 변수 검색 -");
for(Object[] row : list) {
System.out.println("-----> " + Arrays.toString(row));
}
}
@Test
public void testQueryAnnotationTest4() {
List<Object[]> list = br.queryAnnotationTest4("테스트 제목 10");
System.out.println("- 검색 결과4 nativeQuery -");
for(Object[] row : list) {
System.out.println("-----> " + Arrays.toString(row));
}
}
@Test
public void testQueryAnnotationTest5() {
Pageable paging = PageRequest.of(0, 3, Sort.Direction.DESC, "seq");
List<Board> list = br.queryAnnotationTest5(paging);
System.out.println("- 검색 결과4 nativeQuery -");
for(Board board : list) {
System.out.println("-----> " + board.toString());
}
}
}
package com.web;
import java.util.Date;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.web.domain.Board;
import com.web.domain.Member;
import com.web.persistence.MemberRepository;
import com.web.persistence.Repository;
@SpringBootTest
public class RelationMappingTest {
@Autowired
private Repository boardRepo;
@Autowired
private MemberRepository memberRepo;
// 다대일 연관관계 테스트 : 게시글 등록
@Test
public void testManyToOneInsert() {
Member member1 = new Member();
member1.setId("member1");
member1.setPassword("123");
member1.setName("name1");
member1.setRole("User");
memberRepo.save(member1);
Member member2 = new Member();
member2.setId("member2");
member2.setPassword("123");
member2.setName("name2");
member2.setRole("Admin");
memberRepo.save(member2);
for(int i=0; i<=3; i++) {
Board board = new Board();
board.setMember(member1);
board.setTitle("회원_1 이(가) 등록한 게시글"+i);
board.setContent("회원_1 이(가) 등록한 게시글 내용"+i);
board.setCreateDate(new Date());
board.setCnt(0L);
boardRepo.save(board);
}
for(int i=0; i<=3; i++) {
Board board = new Board();
board.setMember(member2);
board.setTitle("회원_2 이(가) 등록한 게시글"+i);
board.setContent("회원_2 이(가) 등록한 게시글 내용"+i);
board.setCreateDate(new Date());
board.setCnt(0L);
boardRepo.save(board);
}
}
@Test
public void testManyToOneSelect() {
Board board = boardRepo.findById(5L).get();
System.out.println("[ " + board.getSeq() + " 번 게시글 정보 ]");
System.out.println("제목 : " + board.getTitle());
System.out.println("내용 : " + board.getContent());
System.out.println("작성자 : " + board.getMember().getName());
System.out.println("작성자 권한 : " + board.getMember().getRole());
}
// 참조키에 값이 항상 설정된다면, 내부 조인으로 변경함
// @JoingColumn 에 nullable 속성
// - Member.member 변수와 매핑되는 MEMBER_ID 컬럼이 항상 참조로 값을 가진다는 의미
// 영속성 전이
@Test
public void testManyToOneInsert() {
Member member1 = new Member();
member1.setId("member1");
member1.setPassword("123");
member1.setName("name1");
member1.setRole("User");
// memberRepo.save(member1);
Member member2 = new Member();
member2.setId("member2");
member2.setPassword("123");
member2.setName("name2");
member2.setRole("Admin");
// memberRepo.save(member2);
for(int i=0; i<=3; i++) {
Board board = new Board();
board.setMember(member1);
board.setTitle("회원_1 이(가) 등록한 게시글"+i);
board.setContent("회원_1 이(가) 등록한 게시글 내용"+i);
board.setCreateDate(new Date());
board.setCnt(0L);
// boardRepo.save(board);
}
memberRepo.save(member1);
for(int i=0; i<=3; i++) {
Board board = new Board();
board.setMember(member2);
board.setTitle("회원_2 이(가) 등록한 게시글"+i);
board.setContent("회원_2 이(가) 등록한 게시글 내용"+i);
board.setCreateDate(new Date());
board.setCnt(0L);
// boardRepo.save(board);
}
memberRepo.save(member2);
}
// 삭제
@Test
public void testCascadeDelete() {
memberRepo.deleteById("member2");
}
}