개발이야기/Spring Boot

Spring Boot JPA 게시판 - CRUD (with MySQL)

C.엠케이 2022. 4. 11. 23:33

Spring Boot 게시판 프로젝트를 시작하기 위해 Database 생성하고 끝냈었다.

 

 

MySQL 접속 및 database 생성

MySQL을 Docker 에 설치하였고, Spring boot 와 연동하기 전 database 를 생성해보자. Docker에 MySQL을 설치하는 방법은 아래 글을 참고! (Mac) Docker 에 MySql 설치하기 1. Docker 공식 홈페이지의 가이드를 확..

mkdevlab.tistory.com

 

게시판에 사용 할 Table 을 만들고, Spring Data JPA 를 이용해서 Create, Read, Delete 를 구현해보자.

 

오늘은 JUnit 을 이용해서 Table 에 잘 들어가고, 삭제가 되는 지 Test 만 진행해보고자 한다.

 

그럼 시작 !

 

0. application.properties 수정

# datasource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mkdevlab?serverTimezone=Asia/Seoul&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=password

# Resource and Thymeleaf Refresh
spring.devtools.livereload.enabled=true
spring.thymeleaf.cache=false

# JPA Properties
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.open-in-view=false
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

 

1. Spring Boot 프로젝트 생성

Type : Gradle

Java : 8

Packaging : Jar

 

Dependency 

  • Lombok
  • Thymeleaf
  • MySQL Driver
  • Spring Data JPA
  • Web
  • Spring Security (로그인도 같이 구현할 것이다)

 

2. 기초 Package 생성

  • config
  • controller
  • domain
  • dto
  • repository
  • service

 

3. domain - Board Entity 생성

 

package com.mkdevlab.springbootboard.domain;

import java.time.LocalDateTime;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Board {
	
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id; // PK
	
	private String title;
	
	private String content;
	
	private String writer;
	
	private int hits;
	
	private char deleteYn;
	
	private LocalDateTime createdDate = LocalDateTime.now();
	
	private LocalDateTime modifiedDate;

	@Builder
	public Board(String title, String content, String writer, int hits, char deleteYn) {
		this.title = title;
		this.content = content;
		this.writer = writer;
		this.hits = hits;
		this.deleteYn = deleteYn;
	}
}

@Getter

getter 메소드를 생성해주는 lombok 의 기능입니다.

 

@NoArgsConstructor(access = AccessLevel.PROTECTED)

기본 생성자를 생성해주는 어노테이션으로, access 옵션을 추가하여 접근 범위를 설정할 수 있다.

동일 패키지 내에서만 해당 클래스를 접근 할 수 있도록 PROTECTED 로 설정.

 

@Entity

해당 클래스가 Table의 역할을 한다는 것을 명시하는 어노테이션이다.

별도로 테이블명을 옵션으로 지정할 수 있지만, board 라는 테이블을 생성할 것으로 패스.

 

@Id

PK 역할을 하는 컬럼을 지정 한다고 생각하면 된다.

 

@GeneratedValue(strategy = GenerationType.IDENTITY)

PK 생성 전략을 설정하는 어노테이션이라고 합니다.

IDENTITY 는 MySQL의 auto increment 기능을 구현해준다.

 

@Setter

setter 를 추가하지 않는 것은 Entity 객체는 Table 과 같으므로, 각 변수 = 컬럼의 정보에 대해 무작정 setter 가 된다고 하면 해당 컬럼의 데이터가 언제 어떻게 들어갔는 지 알 수 없다. Entity에서는 사용하지 않거나, 특정 컬럼 정보에 한해서 어노테이션을 적용해주면 되는 것 같다.

 

4. repository - BoardRepository 생성

package com.mkdevlab.springbootboard.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.mkdevlab.springbootboard.domain.Board;

public interface BoardRepository extends JpaRepository<Board, Long>{

}

Repository 는 interface 이고, JpaRepository 라는 인터페이스를 상속 받아 JPA 내 구현체를 사용할 수 있다.

JpaRepository 를 상속 받을 때 Generic 으로 Entity 와, PK 컬럼의 데이터 타입을 넣어주면 된다.

(MyBatis 와 정말 많이 다르다!!)

 

5. Create, Read, Delete Test

package com.mkdevlab.springbootboard.board;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.mkdevlab.springbootboard.domain.Board;
import com.mkdevlab.springbootboard.repository.BoardRepository;

@SpringBootTest
public class BoardTests {

	@Autowired
	BoardRepository boardRepository;
	
	@Test
	void save() {
		
		// 게시글 파라미터 생성
		Board param = Board.builder()
				.title("3번 게시글 제목")
				.content("3번 게시글입니다.")
				.writer("mkdevlab")
				.hits(0)
				.deleteYn('N')
				.build();
		
		// 게시글 저장
		boardRepository.save(param);
		
		Board entity = boardRepository.findById((long) 3).get();
		assertThat(entity.getTitle()).isEqualTo("1번 게시글 제목");
		assertThat(entity.getContent()).isEqualTo("1번 게시글입니다.");
		assertThat(entity.getWriter()).isEqualTo("mkdevlab");
		
	}
	
	@Test
	void findAll() {
		
		long boardsCnt = boardRepository.count();
		
		List<Board> boards = boardRepository.findAll();
		
	}
	
	@Test
	void delete() {
		
		//게시글 조회
		Board entity = boardRepository.findById((long)3).get();
		
		//게시글 삭제
		boardRepository.delete(entity);
		
	}
	
}

 

jUnit Test 는 호출 할 메소드를 더블클릭하고, 마우스 오른쪽 클릭 -> Run As -> jUnit Test 클릭으로 실행 가능하다.

 

 

save()

 

소스코드를 붙여넣기 전 2번 정보 테스를 했다. PK 값이 2로 상승을 했기 때문에 findById 할 때 3을 기입하였다.

 

앞서 생성한 BoardRepository 의 save() 메소드를 사용하여 데이터를 저장할 수 있다.

Lombok 의 Builder 로 값들을 셋팅해주고 save() 메소드를 호출 해본다.

jUnit 테스트 결과에 assertThat 으로 인해 Failure 가 발생하는 것으로 보기 위해 일부러 다른 값을 넣어보았다.

 

그 결과...

Table 에 값이 잘 들어갔다!!

 

Failure 로 떨어진게 보이고, 아래 뭐가 다른지 표시되었다.

 

findAll()

Count 를 조회한 것과 board 테이블을 조건 없이 전체 조회 한 것으로 볼 수 있다.

 

delete()

잘 삭제 됐다.

 

 

끝.

 

 


Reference:

https://congsong.tistory.com/51?category=749196