1. DTO 만들기
패키지를 이렇게 구성해주고 도메인에 BoardDTO를 추가해준다.
domain package
실제 DB의 테이블과 매칭될 클래스
Entity 클래스, Core 클래스라고도 부른다.
@Entity, @Column, @Id 등을 이용하기도 한다.
VO ( Value Object )
데이터 그 자체로 의미를 담고 있는 객체
특정한 비즈니스 값을 담고 있다
데이터 베이스의 필드들을 속성으로 구성하고 Getter와 Setter 가 있음
DTO와 동일한 개념이지만
⭐Read-Only, 불변성
DTO ( Data Transfer Object )
전송되는 데이터를 담은 컨테이너 객체
Layer간 통신 용도로 오가는 객체
비지니스 로직까지 담아서 작업을 처리하는 용도로 사용하기도 함
( 로직을 아예 담지 않는다고도 나온다.
계속 검색해보니깐 사람마다 정의하거나 사용하는 방법이 아예 다른 듯.. )
ex) DB 데이터 ---DTO----> Service or Controller
DAO ( Data Access Object )
실제로 DB에 접근하는 객체
Service와 DB를 연결하는 고리
Lombok이 설치되어 있다면 이렇게 코드를 작성해줌
아니라면 Generate Getters and Setters
package com.board2.domain;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class BoardDTO {
private Long idx; //글번호(PK)
private String title; //제목
private String content; //내용
private String writer; //작성자
private int viewCnt; //조회수
private String noticeYn; //공지 여부
private String secretYn; //비밀글 여부
private String deleteYn; //삭제 여부
private LocalDateTime insertTime; //등록일
private LocalDateTime updateTime; //수정일
private LocalDateTime deleteTime; //삭제일
}
Getter, Setter를 Import 했을 때 Outline에 이렇게 Get, Set 메서드가 나와야함
속성만 나온다면 매칭이 되지 않기 때문에 Lombok을 직접 설치하고 STS4를 재실행 해줘야함
gradle이 버전 5이상이라면 lombok이 제대로 적용안되는 이슈도 있기 때문에
gradle 디펜던시 lombok부분에 lombok:1.18.10 이렇게 추가해서 써주는 게 좋음
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3'
implementation 'org.projectlombok:lombok'
compileOnly 'org.projectlombok:lombok:1.18.10' //gradle 5.0 이상이면 1.18.10을 추가해줘야 제대로 적용
testCompileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok:1.18.10' //1.18.10을 추가해줘야 제대로 적용
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testAnnotationProcessor 'org.projectlombok:lombok'
}
2. Mapper 인터페이스 만들기
package com.board2.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.board2.domain.BoardDTO;
@Mapper
public interface BoardMapper {
public int insertBoard(BoardDTO params);
public BoardDTO selectBoardDetail(Long idx);
public int updateBoard(BoardDTO params);
public int deleteBoard(Long idx);
public List<BoardDTO> selectBoardList();
public int selectBoardTotalCount();
public boolean cntPlus(Long idx);
}
@Mapper
인터페이스를 마이바티스용 mapper로 만들어줌
XML Mapper에서 이름과 일치하는 SQL문을 찾아서 실행해준다
insertBoard
게시글 등록
INSERT 쿼리를 호출하는 메서드
params 에 게시글 정보가 담김
selectBoardDetail
게시글 하나 조회
SELECT 쿼리를 호출하는 메서드
idx 에 게시글번호(pk)를 받고 WHERE 로 조회
updateBoard
게시글 수정
UPDATE 쿼리 호출
params에 게시글 정보가 담김
deleteBoard
삭제여부를 Y로 만들어서 노출되지 않게 하는 역할 (DB에서 안지움)
idx를 파라미터로 전달받고 WHERE로 검색한 후 Y값으로 변경
selectBoardList
게시글 목록 조회
SELECT 쿼리 호출
selectBoardTotalCount
삭제가 안된 글의 개수를 조회
나중에 페이지 처리용
cntPlus
조회수 카운터 용
결과값을 제대로 전달받기 위해 int로 리턴받음
3. XML Mapper 만들기
BoardMapper와 SQL문을 연결해줄거임
마이바티스 플러그인 설치하면 나오는 MyBatis XML Mapper 추가
( parameterType 같은 거 컨트롤 + 스페이스 자동완성 가능 )
밑에 source를 클릭해서 코드를 작성해줌
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.board2.mapper.BoardMapper">
<sql id="boardColumns">
idx
,title
,content
,writer
,view_cnt
,notice_yn
,secret_yn
,delete_yn
,insert_time
,update_time
,delete_time
</sql>
<insert id="insertBoard" parameterType="BoardDTO">
INSERT INTO tb_board(
<include refid="boardColumns" />
) VALUES (
#{idx}
,#{title}
,#{content}
,#{writer}
,0
,IFNULL(#{noticeYn}, 'N')
,IFNULL(#{secretYn}, 'N')
,'N'
,NOW()
,NULL
,NULL
)
</insert>
<select id="selectBoardDetail" parameterType="long" resultType="BoardDTO">
SELECT
<include refid="boardColumns"/>
FROM
tb_board
WHERE
delete_yn = 'N'
AND
idx = #{idx}
</select>
<update id="updateBoard" parameterType="BoardDTO">
UPDATE tb_board
SET
update_time=NOW()
,title = #{title}
,content = #{content}
,writer = #{writer}
,notice_yn = IFNULL(#{noticeYn}, 'N')
,secret_yn = IFNULL(#{secretYn}, 'N')
WHERE
idx=#{idx}
</update>
<update id="deleteBoard" parameterType="long">
UPDATE tb_board
SET
delete_yn = 'Y'
,delete_time = NOW()
WHERE
idx = #{idx}
</update>
<select id="selectBoardList" parameterType="BoardDTO" resultType="BoardDTO">
SELECT
<include refid="boardColumns"/>
FROM
tb_board
WHERE
delete_yn ='N'
ORDER BY
notice_yn ASC,
idx DESC,
insert_time DESC
</select>
<select id ="selectBoardTotalCount" parameterType="BoardDTO" resultType="int">
SELECT
COUNT(*)
FROM
tb_board
WHERE
delete_yn='N'
</select>
<update id="cntPlus" parameterType="long">
UPDATE tb_board
SET
view_cnt = view_cnt+1
WHERE
idx = #{idx}
</update>
</mapper>
인터페이스 경로 지정과 메서드 이름들을 잘 적었다면
컨트롤을 누르고 메서드 이름에 마우스를 올리면 클릭할 수 있게 바뀌는데
클릭하면 인터페이스의 해당 메서드로 이동한다.
<mapper>
namespace : 인터페이스가 위치한 경로 지정
<sql>
반복되는 쿼리문을 BoardColumns 라는 이름으로 저장해놓고
다른 쿼리문에서 필요할 때 <include refid="BoardColumns> 으로 참조해서 사용한다
#{ }
전달받은 파라미터를 표현하는 방법
4. 마이바티스 SELECT 컬럼과 DTO 속성 매핑
XML
notice_yn ( ' _ ' 를 사용)
=> 🐍스네이크 케이스🐍 사용
Java
noticeYn ( 소문자 + 대문자 )
=> 🐪카멜 케이스🐪 사용
마이바티스의 map-upderscore-to-comel-case 설정을 사용하면
자동으로 매핑 처리가 된다고 함
application.properties 에 설정 추가해줌
*사용할 스키마 이름을 제대로 써줘야함
이번에 사용할 스키마는 board2이므로
localhost:3306/board2? 로 수정해줌
#HikariCP 데이터 소스(DataSource)
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.jdbc-url=jdbc:mysql://localhost:3306/board2?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
spring.datasource.hikari.username=username
spring.datasource.hikari.password=password
spring.datasource.hikari.connection-test-query=SELECT NOW() FROM dual
#MyBatis snake case to camel case
mybatis.configuration.map-underscore-to-camel-case=true
5. DBConfiguration 수정
application.properties에 설정이 추가 되었으니
Configuration에서 해당 설정을 처리할 Bean을 만들어줘야함
package com.board2.configuration;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
@PropertySource("classpath:/application.properties")
public class DBConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Bean
@ConfigurationProperties(prefix="spring.datasource.hikari")
public HikariConfig hikariConfig() {
return new HikariConfig();
}
@Bean
public DataSource dataSource() {
return new HikariDataSource(hikariConfig());
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception{
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
//추가
factoryBean.setMapperLocations(applicationContext.getResources
("classpath:/mappers/**/*Mapper.xml"));
factoryBean.setTypeAliasesPackage("com.board2.domain");
factoryBean.setConfiguration(mybatisConfg());
return factoryBean.getObject();
}
@Bean
public SqlSessionTemplate sqlSession() throws Exception{
return new SqlSessionTemplate(sqlSessionFactory());
}
//추가
@Bean
@ConfigurationProperties(prefix="mybatis.configuration")
public org.apache.ibatis.session.Configuration mybatisConfg(){
return new org.apache.ibatis.session.Configuration();
}
}
setMapperLocation
getResource의 인자로 지정된
classpath:/mapper/**/*Mapper.xml 이런 패턴에 포함되는 XML Mapper를 인식함
*은 전체를 의미하는 애스터리스크라고 함
setTypeAliasesPackage
BoardMapper XML 에서 parameterType과 resultType을 사용하기 위해선
클래스의 전체 패키지 경로가 필요하다고 함
여기서 com.board2.domain 로 경로를 작성해주고
XML에서는 BoardDTO 와 같이 이름만 작성해줘도 됨
SetConfiguration
mybatisConfg에서 추가된 관련된 Bean을 설정 파일로 지정함
mybatisConfg
prefix 설정으로 application.properties에서 mybatis.configuration으로 시작하는
모든 설정을 읽어들여서 Bean으로 등록함
6. 테스트
Junit에서 테스트 해볼 코드를 작성해줌
여기서 실패하면 경로를 잘못 작성했거나 메소드 이름에 오타가 났을 경우인데
Junit 창 로그를 쭉 읽어보면 어디서 틀렸는지 단서가 나옴
1) DB에 등록하기
package com.board2;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.board2.domain.BoardDTO;
import com.board2.mapper.BoardMapper;
@SpringBootTest
public class MapperTest {
@Autowired
private BoardMapper boardMapper;
@Test
public void testInsert() {
BoardDTO params = new BoardDTO();
params.setTitle("1번 제목");
params.setContent("1번 내용");
params.setWriter("테스터");
int result = boardMapper.insertBoard(params);
System.out.println("result=============> "+result);
}
}
성공
2) 게시글 하나 조회하기
@Test
public void testSelectDetail() {
BoardDTO board = boardMapper.selectBoardDetail((long) 1);
try {
String boardJson = new ObjectMapper().writeValueAsString(board);
System.out.println("==============================");
System.out.println(boardJson);
System.out.println("==============================");
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
너무 길어서 여기까지만 캡쳐
board에 저장된 게시글 정보를 Jackson 라이브러리를 이용해서 Json으로 변경 후 콘솔에 출력함
3) 수정하기
@Test
public void testUpdate() {
BoardDTO params = new BoardDTO();
params.setTitle("2번 게시물");
params.setContent("2번 게시글 내용");
params.setWriter("2번 테스터");
params.setIdx((long) 2);
int result = boardMapper.updateBoard(params);
System.out.println("수정==========>"+result);
}
4) 삭제하기
실제로 삭제하는 것이 아니라 삭제를 Y값으로 수정해준다
@Test
public void testDelete() {
int result = boardMapper.deleteBoard((long) 1);
System.out.println("삭제==========>"+result);
}
5) 여러개 추가 후 목록 조회하기
삭제되지 않은 글만 조회하기
일단 글 48개 추가
@Test
public void testAdd() {
for(int i=3; i<=50; i++) {
BoardDTO params = new BoardDTO();
params.setTitle(i+"번 제목");
params.setContent(i+"번 내용");
params.setWriter(i+"번 작성자");
boardMapper.insertBoard(params);
}
}
목록 조회하기
@Test
public void testSelectAll() {
int boardTotalCount = boardMapper.selectBoardTotalCount();
if(boardTotalCount>0) {
List<BoardDTO> boardList = boardMapper.selectBoardList();
if(!boardList.isEmpty()) { //리스트가 비었나?
for(BoardDTO board:boardList) { //foreach문
System.out.println("==============================");
System.out.println(board.getTitle());
System.out.println(board.getContent());
System.out.println(board.getWriter());
System.out.println("==============================");
}
}
}
}
1번은 삭제 테스트 이후 삭제여부가 Y이므로 나오지 않음
VO, DTO
DAO, DTO, Entity, Controller, Service등 용어 정리
sql, include 반복되는 쿼리 묶기
과정 참고한 블로그
'Web > Spring' 카테고리의 다른 글
Spring boot - 게시판 만들기 4 ( 서비스, 컨트롤러 및 View 만들기 ) (0) | 2020.10.13 |
---|---|
Spring boot - 게시판 만들기 2 (MySQL 연동, 설정, Configuration 관련 용어정리) (2) | 2020.10.08 |
Spring boot - 게시판 만들기 1 ( 프로젝트 설정) (2) | 2020.10.08 |
MVC 패턴 (0) | 2020.10.07 |
Spring boot 게시판 만들기 (2) | 2020.10.06 |