이번부터는 본격적으로 JPA를 활용하여 데이터 조작을 해볼 것이다
우선 위와 같이 패키지와 클래스들을 만들어 놓은 다음 하나하나씩 작성해 나갈 것이다
그리고 build.gradle 에 spring-boot-devtools를 추가시켜 줬다
developmentOnly 'org.springframework.boot:spring-boot-devtools'
https://velog.io/@bread_dd/Spring-Boot-Devtools
IntelliJ는 Devtools를 따로 설정해줘야 하는데 위에 블로그에 설명이 잘 되어 있다
eclipse는 따로 설정을 안 해줘도 아마 동작을 하는 걸로 알고 있다
그리고 설정 파일인 application.yml (application.properties) 파일에 jpa 설정을 추가해 준다
spring:
h2:
console:
enabled: true
thymeleaf:
cache: false
jpa:
show-sql: true
properties:
hibernate:
format_sql: true
위 설정을 보면 show-sql 은 sql문을 보여주라는 것이고 format_sql은 sql문을 가독성 있게 해 준다는 뜻이다
여기에는 설정이 안 되어 있지만 현재는 서버가 재시작될 때마다 테이블을 지웠다 새로 만드는데 그게 싫다면
ddl-auto 설정을 하면 원하는 데로 바꿀 수 있다
이제 최종 완성된 소스를 보여주면서 설명을 할 것이다
파일 구조는 위에 파일 트리를 보면서 참고하면 된다
Board.java
import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
public class Board {
@Id
@GeneratedValue
private int idx;
private String title;
private String content;
private String writer;
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
테이블과 매핑될 Board 클래스이다
lombok으로 getter, setter, constructor를 생성하는데 JPA에서는 NoArgsConstructor는 필수로 들어가 있어야 한다
lombok을 사용하지 않는 다면 아무것도 없는 빈 생성자를 하나 만들어 주면 된다
@Entity 애노테이션은 이 클래스가 Entity라는 걸 알려주기 위해 필수로 달아 줘야 한다
그럼 JPA는 DB에 Board라는 테이블을 생성하는데 우리가 선언해준 저 변수들이 속성이 되는 것이다
@Id 애노테이션은 idx가 기본키라는 걸 명시하는 것이다
BoardRepository.java
import com.example.jpaboard.domain.Board;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BoardRepository extends JpaRepository<Board, Integer> {
}
이 인터페이스는 별거 없이 JpaReoisitory를 상속받아서 객체가 되는 클래스인 Board와 Board 테이블에 기본키인 idx의 자료형을 래퍼 클래스 형태로 넣어 주면 된다
이 인터페이스를 이용해서 JPA를 custom 도 할 수 있지만 여기서는 기본적인 CRUD만 할 거기 때문에 이상태로만 생성해 준다
BoardService.java
import com.example.jpaboard.domain.Board;
import com.example.jpaboard.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository boardRepository;
public void register(Board board) {
boardRepository.save(board);
}
public List<Board> list() {
return boardRepository.findAll(Sort.by(Sort.Direction.DESC, "idx"));
}
public Board detail(int idx) {
return boardRepository.findById(idx).orElse(null);
}
public void update(Board board) {
boardRepository.save(board);
}
public void delete(int idx) {
boardRepository.deleteById(idx);
}
}
service 단에서는 BoardRepository를 이용해 데이터를 컨트롤해주면 되는데
등록, 목록, 상세보기, 수정, 삭제 이렇게 간단하게 구현 해 놨다
list에서 findAll을 할 때 idx를 기준으로 내림차순 정렬을 준 것이고 detail에서는 findById가 optional로 감싸져 있는 것을 Board로 바로 쓰기 위해 orElse를 준 것이다
update는 등록과 마찬가지로 save 메서드를 활용하는데 데이터가 없다면 insert가 되고 데이터가 있으면 update가 된다
BoardController.java
import com.example.jpaboard.domain.Board;
import com.example.jpaboard.service.BoardService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/board")
@RequiredArgsConstructor
public class BoardController {
private final BoardService boardService;
@GetMapping("/list")
public String list(Model model) {
model.addAttribute("board", boardService.list());
return "list";
}
@GetMapping("/detail/{idx}")
public String detail(@PathVariable int idx, Model model) {
model.addAttribute("board", boardService.detail(idx));
return "detail";
}
@GetMapping("/register")
public String registerGet() {
return "register";
}
@PostMapping("/register")
public String registerPost(Board board) {
boardService.register(board);
return "redirect:/board/list";
}
@GetMapping("/update/{idx}")
public String updateGet(@PathVariable int idx, Model model) {
model.addAttribute("board", boardService.detail(idx));
return "update";
}
@PostMapping("/update")
public String updatePost(Board board) {
boardService.update(board);
return "redirect:/board/list";
}
@GetMapping("/delete/{idx}")
public String delete(@PathVariable int idx) {
boardService.delete(idx);
return "redirect:/board/list";
}
}
컨트롤러는 사실 딱히 설명할 만한게 없다
list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.main {
width: 700px;
height: 700px;
margin: auto;
margin-top: 100px;
}
table {
text-align: center;
}
.register {
text-align: center;
display: inline-block;
border: 1px solid;
width: 80px;
height: 30px;
background-color: mistyrose;
text-decoration: none;
line-height: 30px;
}
</style>
</head>
<body>
<div class="main">
<a href="/board/register" class="register">글 쓰기</a>
<table>
<colgroup>
<col width="50">
<col width="300">
<col width="150">
<col width="200">
</colgroup>
<thead>
<tr>
<th>번호</th>
<th>제목</th>
<th>작성자</th>
<th>등록일</th>
</tr>
</thead>
<tbody>
<tr th:each="board : ${board}">
<td th:text="${board.idx}"></td>
<td><a th:href="@{/board/detail/{idx}(idx=${board.idx})}" th:text="${board.title}"></a></td>
<td th:text="${board.writer}"></td>
<td th:text="${#temporals.format(board.createdAt, 'yyyy-MM-dd HH:mm')}"></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
register.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.main {
width: 500px;
height: 700px;
margin: auto;
}
label {
display: inline-block;
width: 60px;
}
input {
display: inline-block;
width: 400px;
margin-bottom: 10px;
}
textarea {
width: 470px;
height: 400px;
}
button {
margin-top: 10px;
width: 70px;
background-color: cyan;
border: 1px solid;
}
</style>
</head>
<body>
<div class="main">
<form method="post">
<label for="writer">작성자 :</label>
<input type="text" name="writer" id="writer">
<label for="title">제목 :</label>
<input type="text" name="title" id="title">
<label for="content">내용 :</label>
<textarea name="content" id="content"></textarea>
<button type="submit">등록</button>
</form>
</div>
</body>
</html>
detail.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.main {
width: 500px;
height: 700px;
margin: auto;
}
label {
display: inline-block;
width: 60px;
}
input {
display: inline-block;
width: 400px;
margin-bottom: 10px;
}
textarea {
width: 470px;
height: 400px;
}
#update {
text-align: center;
display: inline-block;
border: 1px solid;
width: 80px;
height: 30px;
background-color: moccasin;
text-decoration: none;
line-height: 30px;
}
#delete {
text-align: center;
display: inline-block;
border: 1px solid;
width: 80px;
height: 30px;
background-color: lightpink;
text-decoration: none;
line-height: 30px;
}
</style>
</head>
<body>
<div class="main">
<label for="writer">작성자 :</label>
<input type="text" name="writer" id="writer" th:value="${board.writer}" disabled>
<label for="title">제목 :</label>
<input type="text" name="title" id="title" th:value="${board.title}" disabled>
<label for="content">내용 :</label>
<textarea name="content" id="content" th:text="${board.content}" disabled></textarea>
<a th:href="@{/board/update/{idx}(idx=${board.idx})}" id="update">수정</a>
<a th:href="@{/board/delete/{idx}(idx=${board.idx})}" id="delete">삭제</a>
</div>
</body>
</html>
update.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.main {
width: 500px;
height: 700px;
margin: auto;
}
label {
display: inline-block;
width: 60px;
}
input {
display: inline-block;
width: 400px;
margin-bottom: 10px;
}
textarea {
width: 470px;
height: 400px;
}
button {
margin-top: 10px;
width: 70px;
background-color: cyan;
border: 1px solid;
}
</style>
</head>
<body>
<div class="main">
<form method="post" th:action="@{/board/update}">
<input type="hidden" name="idx" id="idx" th:value="${board.idx}">
<label for="writer">작성자 :</label>
<input type="text" name="writer" id="writer" th:value="${board.writer}">
<label for="title">제목 :</label>
<input type="text" name="title" id="title" th:value="${board.title}">
<label for="content">내용 :</label>
<textarea name="content" id="content" th:text="${board.content}"></textarea>
<button type="submit">수정</button>
</form>
</div>
</body>
</html>
아무래도 화면 그리는게 제일 힘든 거 같다
디자인 쪽으로는 아무래도 재능이 영 없는 것 같다...ㅋ
이걸로 정말 간단한 게시판을 만들어 보았다
사실 이건 JPA 맛만 봤을 뿐 더 어렵고 많은 기능들이 있는데 그건 차차 공부하면서 정리할 생각이다
'프로그래밍 > JAVA' 카테고리의 다른 글
[JPA] QueryMethod 활용 (0) | 2021.07.17 |
---|---|
[Spring-Boot] JPA를 활용하여 게시판 페이징 처리 하기 (0) | 2021.07.16 |
[Spring-Boot] JPA를 활용하여 간단한 CRUD 게시판 만들기(2) (0) | 2021.07.14 |
[Spring-Boot] JPA를 활용하여 간단한 CRUD 게시판 만들기(1) (0) | 2021.07.14 |
[JAVA] swagger로 API문서 자동화 (0) | 2021.07.08 |