▼ Backend/└ 게시판 만들기
Spring Boot (게시판) - 2 | 데이터베이스(MariaDB) 연동 및 JPA CRUD
Valar
2021. 10. 28. 10:29
반응형
[스프링 부트 (Spring Boot)/게시판 만들기] - 1 | 스프링 부트 프로젝트 만들기
[데이터베이스 (DATA BASE)/MariaDB] - MariaDB | 윈도우 MariaDB 설치 및 접속하기
구성환경
SpringBoot, Gradle, Thymeleaf, Jpa(JPQL), Jar, MariaDB
지난번 스프링 부트 프로젝트를 생성하였고, 이번 포스팅에서는 DBMS중 하나인 MariaDB에 스키마 생성 및 연동하는 부분을 진행한다.
MariaDB 설치가 안되어있는 경우 링크를 통해 설치 후 진행한다.
프로젝트내 static/database/script.sql이 있으니 테이블 생성 시 참고한다.
script.sql 다운로드
데이터베이스 생성하기
CREATE DATABASE IF NOT EXISTS `board_study`
테이블 생성하기
CREATE TABLE IF NOT EXISTS `board` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'PK',
`title` varchar(200) NOT NULL COMMENT '제목',
`content` text NOT NULL COMMENT '내용',
`read_cnt` int(11) NOT NULL DEFAULT 0 COMMENT '조회수',
`register_id` VARCHAR(100) NOT NULL COMMENT '작성자',
`register_time` DATETIME NULL DEFAULT NULL COMMENT '작성일',
`update_time` DATETIME NULL DEFAULT NULL COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='게시판';
Dependency 추가
프로젝트로 돌아와 JPA(JPA란?)를 사용하기 위한 Dependency 추가와 데이터베이스 설정을 한다.
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
Gradle 적용
이클립스 | 프로젝트 우 클릭 → Gradle → Refresh Gradle Project |
인텔리제이 | build.gradle에서 Load Gradle Changes (Ctrl+Shift+O) 또는 우측 Gradle 탭에서 Reload All Gradle Projects |
데이터베이스 설정(application.properties)
spring.datasource.driverClassName=org.mariadb.jdbc.Driver
spring.datasource.url=jdbc:mariadb://localhost:3306/데이터베이스명
spring.datasource.username=계정
spring.datasource.password=비밀번호
lombok(https://projectlombok.org/download) 설치
lombok (Getter, Setter 등의 반복 메서드를 자동으로 연결하고 도구를 빌드하여 Java를 향상하는 라이브러리)
lombok.jar를 다운로드 받은 후 실행한다.
① Specify location..를 통해 이클립스가 설치된 경로를 선택한다.
② install / update
③ Quit installer
설치가 완료되면 이클립스 설치 경로에 lombok.jar가 생성되고 eclipse.ini에 설정이 추가된다.
*STS의 경우 SpringToolSuite4.ini
이제 이클립스를 재시작한다.
재시작 후에 이클립스 상단 탭 Project -> Clean -> 해당 프로젝트 Clean
이제 JPA를 사용하기 위한 설정이 끝났으니, 서비스를 구현해본다.
Service까지 구현하는 패키지 구조는 아래와 같다.
이름 | 설명 |
BoardRequestDto.java | 게시판 요청 데이터를 담당 |
BoardResponseDto.java | 게시판 응답 데이터를 담당 |
BaseTimeEntity.java | 반복되는 날짜 데이터의 공통처리를 담당 |
Board.java | board 테이블의 @Entity |
BoardRepository.java | JpaRepository 구현체 |
BoardService.java | 게시판 @Service |
BoardRequestDto.java
게시물 등록, 게시물 수정, 게시물 상세 조회에 필요한 필드를 정의한다.
toEntity() 메서드는 Board Entity를 builder 하여 사용한다.
package com.board.study.dto.board;
import com.board.study.entity.board.Board;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
public class BoardRequestDto {
private Long id;
private String title;
private String content;
private String registerId;
public Board toEntity() {
return Board.builder()
.title(title)
.content(content)
.registerId(registerId)
.build();
}
}
BoardResponseDto.java
게시물 목록, 게시물 상세 조회에 필요한 필드를 정의한다.
Board Entity를 BoardResponseDto에 맞게 변환하는 생성자를 생성한다.
package com.board.study.dto.board;
import com.board.study.entity.board.Board;
import java.time.LocalDateTime;
import lombok.Getter;
@Getter
public class BoardResponseDto {
private Long id;
private String title;
private String content;
private int readCnt;
private String registerId;
private LocalDateTime registerTime;
public BoardResponseDto(Board entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
this.readCnt = entity.getReadCnt();
this.registerId = entity.getRegisterId();
this.registerTime = entity.getRegisterTime();
}
@Override
public String toString() {
return "BoardListDto [id=" + id + ", title=" + title + ", content=" + content + ", readCnt=" + readCnt +
", registerId=" + registerId + ", registerTime=" + registerTime + "]";
}
}
BaseTimeEntity.java
Entity에서 공통적으로 사용될 날짜 필드를 관리할 클래스를 정의한다.
꼭 날짜가 아니더라도 공통적으로 반복되는 필드를 정의하여 사용해도 된다.
package com.board.study.entity;
import java.time.LocalDateTime;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import lombok.Getter;
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
@CreatedDate
private LocalDateTime registerTime;
@LastModifiedDate
private LocalDateTime updateTime;
}
Board.java
Entity를 정의한다.
테이블의 모든 필드와 Builder 생성자를 구현한다.
혹시나 테이블명이 Class명과 다를 경우 @Entity(name = "테이블명") 을 설정한다.
package com.board.study.entity.board;
import com.board.study.entity.BaseTimeEntity;
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;
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Entity
public class Board extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
private int readCnt;
private String registerId;
@Builder
public Board(Long id, String title, String content, int readCnt, String registerId) {
this.id = id;
this.title = title;
this.content = content;
this.readCnt = readCnt;
this.registerId = registerId;
}
}
BoardRepository.java
JpaRepository를 상속받아 CRUD의 기능을 담당하는 인터페이스를 생성한다.
그리고 @Query을 사용한 JPQL 방식의 updateBoard() 메서드도 구현해본다.
이 방식으로 쿼리를 직접 작성하여 사용할 수도 있다.
package com.board.study.entity.board;
import com.board.study.dto.board.BoardRequestDto;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
public interface BoardRepository extends JpaRepository < Board, Long > {
String UPDATE_BOARD = "UPDATE Board " +
"SET TITLE = :#{#boardRequestDto.title}, " +
"CONTENT = :#{#boardRequestDto.content}, " +
"UPDATE_TIME = NOW() " +
"WHERE ID = :#{#boardRequestDto.id}";
@Transactional
@Modifying
@Query(value = UPDATE_BOARD, nativeQuery = true)
public int updateBoard(@Param("boardRequestDto") BoardRequestDto boardRequestDto);
}
BoardService.java
게시판 기능을 담당할 Service 클래스로 나중에 파일 및 페이징 처리를 추가할 예정이다.
지금은 간단한 CRUD 메서드만 작성한다.
package com.board.study.service;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.board.study.dto.board.BoardResponseDto;
import com.board.study.dto.board.BoardRequestDto;
import com.board.study.entity.board.BoardRepository;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class BoardService {
private final BoardRepository boardRepository;
@Transactional
public Long save(BoardRequestDto boardSaveDto) {
return boardRepository.save(boardSaveDto.toEntity()).getId();
}
@Transactional(readOnly = true)
public List < BoardResponseDto > findAll() {
return boardRepository.findAll().stream().map(BoardResponseDto::new).collect(Collectors.toList());
}
public BoardResponseDto findById(Long id) {
return new BoardResponseDto(boardRepository.findById(id).get());
}
public int updateBoard(BoardRequestDto boardRequestDto) {
return boardRepository.updateBoard(boardRequestDto);
}
public void deleteById(Long id) {
boardRepository.deleteById(id);
}
}
BoardStudyApplication
Application 클래스에 @EnableJpaAuditing을 추가해 Auditing 기능을 활성화한다.
package com.board.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@EnableJpaAuditing // JPA Auditing
@SpringBootApplication
public class BoardStudyApplication {
public static void main(String[] args) {
SpringApplication.run(BoardStudyApplication.class, args);
}
}
어노테이션에 대한 설명
이름 | 설명 |
@NoArgsConstructor | 파라미터가 없는 생성자를 생성한다. |
@AllArgsConstructor | 모든 인자를 가진 생성자를 생성한다. |
@RequiredArgsConstructor | 초기화 되지 않은 모든 final 필드, @NonNull로 마크돼있는 모든 필드들에 대한 생성자를 자동으로 생성해준다. |
@Getter | Class 내 모든 필드의 Getter method를 자동 생성한다. |
@Setter | Class 내 모든 필드의 Setter method를 자동 생성한다. |
@Entity | 실제 DB의 테이블과 매칭될 Class임을 명시한다. 즉, 테이블과 링크될 클래스임을 나타낸다. |
@MappedSuperclass | 이 클래스를 상속하는 엔티티에 매핑되는 테이블에 생성한다. |
@EntityListeners(AuditingEntityListener.class) | JPA 내부에서 엔티티 객체가 생성/변경되는 것을 감지하는 역할을 한다. |
@CreateDate | JPA에서 엔티티의 생성 시간을 처리한다. |
@LastModifiedDate | 최종 수정 시간을 자동으로 처리한다. |
@Id | 해당 테이블의 PK 필드를 나타낸다. |
@GeneratedValue(strategy = GenerationType.IDENTITY) | PK의 생성 규칙을 나타낸다. |
@Builder | 어느 필드에 어떤 값을 채워야 할지 명확하게 정하여 생성 시점에 값을 채워준다. |
@Transactional | 선언적 트랜잭션을 사용한다. |
@Modifying | @Query Annotation으로 작성 된 변경, 삭제 쿼리를 사용할때 사용한다. |
@Query | SQL을 JPQL로 작성할 수 있고, nativeQuery=true 옵션으로 네이티브 쿼리도 사용 가능하게 한다. |
테스트 코드 작성
화면을 구성하기 전, 기능들이 정상적으로 작동하는지 확인하기 위해 JUnit(테스트 프레임워크)를 이용해 테스트를 진행한다.
프로젝트 생성 당시 설명했었던 것처럼 src/test/java에 Test Class가 생성되는데, 추가로 생성해서 진행해도 되지만 여기서는 생성되어있는 BoardStudyApplicationTests.java를 사용한다.
package com.board.study;
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.board.study.dto.board.BoardResponseDto;
import com.board.study.dto.board.BoardRequestDto;
import com.board.study.service.BoardService;
@SpringBootTest
class BoardStudyApplicationTests {
@Autowired
private BoardService boardService;
@Test
void save() {
BoardRequestDto boardSaveDto = new BoardRequestDto();
boardSaveDto.setTitle("제목입니다.");
boardSaveDto.setContent("내용입니다.");
boardSaveDto.setRegisterId("작성자");
Long result = boardService.save(boardSaveDto);
if (result > 0) {
System.out.println("# Success save() ~");
findAll();
findById(result);
} else {
System.out.println("# Fail Save() ~");
}
}
void findAll() {
List < BoardResponseDto > list = boardService.findAll();
if (list != null) {
System.out.println("# Success findAll() : " + list.toString());
} else {
System.out.println("# Fail findAll() ~");
}
}
void findById(Long id) {
BoardResponseDto info = boardService.findById(id);
if (info != null) {
System.out.println("# Success findById() : " + info.toString());
updateBoard(id);
} else {
System.out.println("# Fail findById() ~");
}
}
void updateBoard(Long id) {
BoardRequestDto boardRequestDto = new BoardRequestDto();
boardRequestDto.setId(id);
boardRequestDto.setTitle("업데이트 제목");
boardRequestDto.setContent("업데이트 내용");
boardRequestDto.setRegisterId("작성자");
int result = boardService.updateBoard(boardRequestDto);
if (result > 0) {
System.out.println("# Success updateBoard() ~");
} else {
System.out.println("# Fail updateBoard() ~");
}
}
}
테스트 코드 실행
2가지 방법 중 선택하여 진행한다.
실행 시 @Test 어노테이션이 있는 메서드가 실행된다.
방법①
프로젝트 우 클릭 → Run As → JUnit Test
방법②
소스파일에서 우 클릭 → Run As → JUnit Test
JUnit 탭에서 진행 결과를 확인할 수 있다.
성공 로그가 출력되었고, 데이터베이스에서도 직접 확인해본다.
프로젝트 Import
압축을 풀고, 이클립스 import → General → Existing Projects into Workspace
Spring Boot (게시판) - 1 | 스프링 부트 프로젝트 만들기
Spring Boot (게시판) - 2 | 데이터베이스(MariaDB) 연동 및 JPA CRUD
Spring Boot (게시판) - 3 | 등록, 상세, 리스트 페이지 구현하기
Spring Boot (게시판) - 4 | 수정, 삭제 구현하기
반응형