250x250
Notice
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Open Source
- Thymeleaf
- AJAX
- jpa
- JavaScript
- Docker
- 문서
- error
- MySQL
- spring
- STS
- Tomcat
- 설정
- myBatis
- Eclipse
- ubuntu
- SpringBoot
- Exception
- IntelliJ
- JDBC
- MSSQL
- 오픈소스
- Python
- Source
- git
- PostgreSQL
- Core Java
- Spring Boot
- oracle
- maven
Archives
- Today
- Total
헤르메스 LIFE
[Spring Boot] 게시판 #2 - 간단한 게시판 CRUD 개발 본문
728x90
게시판을 작성해보려 합니다. 조금씩 살을 붙여나가 보려고 합니다.
https://hermeslog.tistory.com/704
개발환경
STS 4.17.2.RELEASE
OpenJDK Runtime Environment Zulu11.62+17-CA (build 11.0.18+10-LTS)
Spring Boot 2.7.9
lombok
devtools
postgresql 14.1
Gladle
Thymeleaf
T_BOARD_M 테이블
DROP TABLE IF EXISTS T_BOARD_M;
CREATE TABLE IF NOT EXISTS T_BOARD_M (
ID SERIAL NOT NULL,
TITLE VARCHAR(200) NOT NULL,
CONTENTS TEXT NOT NULL,
READ_CNT INTEGER NOT NULL DEFAULT 0 ,
CRT_ID VARCHAR(20),
CRT_DT TIMESTAMP,
MDF_ID VARCHAR(20),
MDF_DT TIMESTAMP,
CONSTRAINT PK_T_BOARD_M PRIMARY KEY (ID)
);
BoardController.java
package octopus.bbs.controller;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import octopus.base.anotation.LoginUser;
import octopus.base.model.CommonResult;
import octopus.base.model.ListResult;
import octopus.base.model.SingleResult;
import octopus.base.model.UserSessionDto;
import octopus.base.service.ResponseService;
import octopus.bbs.dto.BoardDto;
import octopus.bbs.service.BoardService;
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/bbs")
public class BoardController {
private final MessageSourceAccessor messageSourceAccessor;
private final BoardService boardService;
private final ResponseService responseService;
@GetMapping("/id/{id}")
public SingleResult<BoardDto> findCodeByCd(@PathVariable Long id) {
return responseService.getSingleResult(boardService.findById(id));
}
@GetMapping("/list")
public ListResult<BoardDto> findAll() {
return responseService.getListResult(boardService.findAll());
}
@PostMapping("/save")
public SingleResult<BoardDto> save(final @RequestBody BoardDto dto,
@LoginUser UserSessionDto userDto) {
dto.setCrtId(userDto.getUserId());
dto.setMdfId(userDto.getUserId());
log.debug("BoardDto :: {}", dto);
return responseService.getSingleResult(boardService.save(dto));
}
@PutMapping("/update")
public SingleResult<String> update(final @RequestBody BoardDto dto,
@LoginUser UserSessionDto userDto) {
dto.setMdfId(userDto.getUserId());
log.debug("dto :: {}", dto);
boardService.update(dto);
return responseService.getSingleResult(messageSourceAccessor.getMessage("msg.itIsSaved")); // 저장되었습니다.
}
@DeleteMapping("/id/{id}")
public CommonResult delete(@PathVariable Long id) {
boardService.delete(id);
return responseService.getSingleResult(messageSourceAccessor.getMessage("msg.itIsDeleted")); // 삭제되었습니다.
}
}
BoardService.java
package octopus.bbs.service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import octopus.bbs.dto.BoardDto;
import octopus.bbs.dto.TBoardM;
import octopus.bbs.repository.BoardRepository;
@Service
// @AllArgsConstructor
@RequiredArgsConstructor
@Slf4j
public class BoardService {
// @AllArgsConstructor를 사용하는 경우
// private CodeDRepository codeDRepository;
private final BoardRepository boardRepository;
private final ModelMapper modelMapper;
@Transactional // 선언적 트랜잭션을 사용
public BoardDto findById(Long id) {
Optional<TBoardM> board = boardRepository.findById(id);
log.debug("board :: {}", board.get());
if (board.isPresent()) {
boardRepository.updateCnt(id);
return modelMapper.map(board.get(), BoardDto.class);
} else {
return new BoardDto();
}
}
@Transactional(readOnly = true) // 선언적 트랜잭션을 사용
public List<BoardDto> findAll() {
List<BoardDto> list = boardRepository.findAll().stream()
.map(data -> new BoardDto(data))
.collect(Collectors.toList());
log.debug("list :: {}", list);
return list;
}
@Transactional
public BoardDto save(BoardDto dto) {
log.debug("BoardDto :: {}", dto);
TBoardM board = dto.toEntity();
log.debug("tCodeM :: {}", board);
TBoardM saveBoard = boardRepository.save(board);
return new BoardDto(saveBoard);
}
@Transactional
public void update(BoardDto dto) {
log.debug("BoardDto :: {}", dto);
Optional<TBoardM> board = boardRepository.findById(dto.getId());
log.debug("board :: {}", board.get());
board.get().updateBoard(dto);
}
@Transactional
public void delete(Long id) {
log.debug("삭제될 ID :: {}", id);
boardRepository.deleteById(id);
}
}
BoardRepository.java
package octopus.bbs.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import octopus.bbs.dto.TBoardM;
// @Repository : JpaRepository를 사용하면 @Repository를 사용하지 않아도 됨.
public interface BoardRepository extends JpaRepository<TBoardM, Long> {
String UPDATE_CNT = "UPDATE TBoardM a" +
" SET a.readCnt = a.readCnt + 1 " +
" WHERE a.id = ?1";
@Modifying(clearAutomatically = true, flushAutomatically = true)
// @Query(value=UPDATE_CNT, nativeQuery=true)
@Query(value = UPDATE_CNT)
int updateCnt(Long id);
}
TBoardM.java
package octopus.bbs.dto;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Proxy;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import octopus.base.model.BaseEntity;
@Getter // getter를 자동으로 생성합니다.
// @Setter // 객체가 무분별하게 변경될 가능성 있음
// @ToString(exclude = { "crtId", "crtDt", "mdfId", "mdfDt" }) // 연관관계 매핑된 엔티티 필드는 제거. 연관 관계 필드는 toString()에서 사용하지 않는 것이
// // 좋습니다.
@ToString(callSuper = true)
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 인자없는 생성자를 자동으로 생성합니다. 기본 생성자의 접근 제어자가 불명확함. (access =
// AccessLevel.PROTECTED) 추가
@DynamicInsert // insert 시 null 인필드 제외
@DynamicUpdate // update 시 null 인필드 제외
// @AllArgsConstructor // 객체 내부의 인스턴스멤버들을 모두 가지고 있는 생성자를 생성 (매우 위험)
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" }) // Post Entity에서 User와의 관계를 Json으로 변환시 오류 방지를 위한 코드
@Proxy(lazy = false)
@Entity // jpa entity임을 선언. 실제 DB의 테이블과 매칭될 Class
@Table(name = "T_BOARD_M")
public class TBoardM extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@Id // PK 필드임
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 제목
*/
// @Column(nullable = false, length = 200)
private String title;
/**
* 내용
*/
private String contents;
/**
* 조회수
*/
private Integer readCnt;
@Builder
public TBoardM(Long id, String title, String contents, Integer readCnt, String crtId,
String mdfId) {
Assert.hasText(title, "Title must not be empty");
Assert.hasText(crtId, "crtId must not be empty");
Assert.hasText(mdfId, "mdfId must not be empty");
this.id = id;
this.title = title;
this.contents = contents;
this.readCnt = readCnt;
super.crtId = crtId;
super.mdfId = mdfId;
}
/**
* 게시판 Update
*/
public void updateBoard(BoardDto dto) {
this.title = dto.getTitle();
this.contents = dto.getContents();
super.mdfId = dto.getMdfId();
}
}
BoardDto.java
package octopus.bbs.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import octopus.base.model.BaseDto;
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@Builder
@EqualsAndHashCode(callSuper = true) // true의 경우 부모 클래스 필드 값들도 동일한지 체크하며, false(기본값)일 경우 자신 클래스의 필드 값만 고려한다.
public class BoardDto extends BaseDto {
private static final long serialVersionUID = 1L;
private Long id;
private String title;
private String contents;
private Integer readCnt;
public TBoardM toEntity() {
return TBoardM.builder().title(title).contents(contents)
.crtId(getCrtId()).mdfId(getMdfId()).build();
}
public BoardDto(TBoardM board) {
this.id = board.getId();
this.title = board.getTitle();
this.contents = board.getContents();
this.readCnt = board.getReadCnt();
super.crtId = board.getCrtId();
super.crtDt = board.getCrtDt();
super.mdfId = board.getMdfId();
super.mdfDt = board.getMdfDt();
}
}
Spring Boot Start 옵션 : Run Configuration >> Spring Boot App >> Arguments Tab > VM arguments > -Dspring.profiles.active=local
테스트
https://hermeslog.tistory.com/704
728x90
'Spring Boot Framework' 카테고리의 다른 글
[Spring Boot] JPA에서 Boolean 처리 ( @Converter, @Convert ) (0) | 2023.04.25 |
---|---|
[Spring Boot] 게시판 #3 - 게시판 목록 + 페이징처리 (0) | 2023.04.25 |
[Spring Boot] 게시판 #1 - 개발환경 (0) | 2023.04.14 |
[Spring Boot] HandlerMethodArgumentResolver를 이용한 중복 Source 제거 방법 (0) | 2023.04.04 |
[Spring Boot] Jackson 직렬화 문제 (0) | 2023.04.01 |