[CSBlog] MyBatis Mapper를 활용해 Post 기능 구현하기

sorzzzzy·2022년 2월 8일
0

Spring Project

목록 보기
10/18
post-thumbnail

지난 시간에 MyBatis 와 데이터베이스를 연동하고, H2 Database 콘솔을 통해 테이블이 생성된 것까지 확인했다.

이제 Mapper 를 활용해 MyBatis 설정을 마무리해보자.


🏷 Mybatis 설정 - config.xml 파일 작성

mybatis.config-location=classpath:mybatis/mybatis-config.xml

application.properties 파일 마지막 줄에 MyBatis 의 설정 파일의 위치를 명시한다.


이제 명시한 설정 파일을 생성해야 한다.

resources 디렉토리 하위에 mybatis 디렉토리를 생성한 뒤,
그 하위에 mybatis-config.xml 파일을 생성한다.


✔️ mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <mappers>
        <mapper resource="mybatis/mapper/UserCommentMapper.xml"/>
        <mapper resource="mybatis/mapper/PostMapper.xml"/>
    </mappers>
</configuration>

<mappers></mappers> 태그 안에 두가지의 xml 파일이 있는데, 차례대로 댓글과 게시물을 의미한다.

작성한 파일의 위치에 맞게 디렉토리와 파일을 생성해준다.


🏷 Mapper 이해하기

MyBatis Mapper 는 위와 같이 xml 파일과 DTO, VO 를 통해 데이터베이스와 서비스 계층에 접근할 수 있다.

<mappers></mappers> 태그 안에는 여러개의 <mapper></mapper> 태그가 들어갈 수 있다.
Mapper 인터페이스(=Repository) 내의 메소드의 이름과 id 가 일치해야 한다.


✔️ PostMapper.xml

<?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.codepresso.blog.repository.PostRepository">
    <insert id="save"></insert>

    <select id="findAll" resultType="com.codepresso.blog.vo.Post"></select>

    <select id="findOne" resultType="com.codepresso.blog.vo.Post"></select>

    <update id="updatePost"></update>

    <delete id="deleteById"></delete>
</mapper>

mappercom.codepresso.blog.repository.PostRepository 를 가리키고 있다.
위에서부터 차례로 게시물 작성, 모든 게시물 조회, 특정 게시물 조회(id기반), 게시물 수정, 게시물 삭제이다.
알맞은 SQL 문을 태그 사이에 작성하면 된다.


🏷 게시물 기능 구현

이제 본격적으로 게시물 기능을 구현해보자!


✔️ com.codepresso.blog.vo.Post.java 클래스

package com.codepresso.blog.vo;

import java.util.Date;


public class Post {

	private Long id;

	private String userName;

	private String title;

	private String postContent;

	private Date regDate;

	private Date updtDate;
	
	public Post() {
		
	}
	
	public Post(Long id, String title, String postContent) {
		this.id = id;
		this.title = title;
		this.postContent = postContent;
	}
	
	public Post(String userName, String title, String postContent) {
		this.userName = userName;
		this.title = title;
		this.postContent = postContent;
		this.regDate = new Date();
		this.updtDate = new Date();
	}

	public Post(Long id, String userName, String title, String postContent) {
		this.id = id;
		this.userName = userName;
		this.title = title;
		this.postContent = postContent;
		this.regDate = new Date();
		this.updtDate = new Date();
	}

	public Post(Long id, String userName, String title, String postContent, Date regDate, Date updtDate) {
		this.id = id;
		this.userName = userName;
		this.title = title;
		this.postContent = postContent;
		this.regDate = regDate;
		this.updtDate = updtDate;
	}
	
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getPostContent() {
		return postContent;
	}

	public void setPostContent(String postContent) {
		this.postContent = postContent;
	}

	public Date getRegDate() {
		return regDate;
	}

	public void setRegDate(Date regDate) {
		this.regDate = regDate;
	}

	public Date getUpdtDate() {
		return updtDate;
	}

	public void setUpdtDate(Date updtDate) {
		this.updtDate = updtDate;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("\nid : ").append(id).append("\n");
		sb.append("user : ").append(userName).append("\n");
		sb.append("title : ").append(title).append("\n");
		sb.append("postContent : ").append(postContent).append("\n");
		
		return sb.toString();
	}
}

Post 객체를 정의한다.


✔️ com.codepresso.blog.repository.PostRepository.java 인터페이스

package com.codepresso.blog.repository;

import com.codepresso.blog.vo.Post;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface PostRepository {

    // 등록
    void save(@Param("post") Post post);

    // 여러 건 조회
    List<Post> findAll();

    // 특정 글 조회
    Post findOne(@Param("id") Long id);

    // 글 수정
    void updatePost(@Param("post") Post post);

    // 글 삭제
    void deleteById(@Param("id") Long id);
}

@Param 으로 전달한 파라미터를 잘 확인해야 한다.
PostMapper.xml 의 SQL 문에서 사용되기 때문이다.

JPA에 너무 익숙해진 나머지 @Mapper 대신 @Reopository 써놓고 뭐가 문제인지 모르고 있다가 시간을 많이 날렸다^^!


✔️ com.codepresso.blog.service.PostService.java 클래스

package com.codepresso.blog.service;

import com.codepresso.blog.repository.PostRepository;
import com.codepresso.blog.vo.Post;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class PostService {
    private PostRepository postRepository;


    public PostService(PostRepository postRepository) {
        this.postRepository = postRepository;
    }

    public void addPost(Post post){
        postRepository.save(post);
    }

    public List<Post> getPostList(){
        return postRepository.findAll();
    }

    public Post getPost(Long id){
        return postRepository.findOne(id);
    }

    public void updatePost(Post post){
        postRepository.updatePost(post);
    }

    public void deletePost(Long id){
        postRepository.deleteById(id);
    }
}

PostRepository 에 대한 의존성 주입을 하고, PostRepository 를 바탕으로 메소드를 정의한다.


✔️ com.codepresso.blog.controller.PostController.java 클래스

package com.codepresso.blog.controller;

import com.codepresso.blog.service.PostService;
import com.codepresso.blog.vo.Post;
import com.codepresso.blog.vo.Result;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class PostController {

    private PostService postService;

    public PostController(PostService postService) {
        this.postService = postService;
    }

    @GetMapping(value = "/posts")
    public List<Post> getPostList(){
        return postService.getPostList();
    }

    @GetMapping(value = "/post")
    public Post getPost(@RequestParam Long id){
        return postService.getPost(id);
    }

    @PostMapping(value = "/post")
    public Result addPost(@RequestBody Post post){
        postService.addPost(post);
        return new Result(200, "Success");
    }

    @PutMapping(value = "/post")
    public Result updatePost(@RequestBody Post post){
        postService.updatePost(post);
        return new Result(200, "Success");
    }

    @DeleteMapping(value = "/post")
    public Result deletePost(@RequestParam("id") Long id){
        postService.deletePost(id);
        return new Result(200, "Success");
    }
}

PostService 에 대한 의존성 주입을 한다.

각각의 메소드에 대해 HTTP Method를 위한 간소화된 어노테이션을 적용하고, Path Param을 사용하기 때문에 메소드 파라미터에 @RequestParam 어노테이션을 사용한다.

또한 데이터 형식이 JSON 이기 때문에, @RequestBody 라는 어노테이션을 사용한다.


이제 가장 중요한 PostMapper.xml 파일로 다시 돌아가서 비워뒀던SQL 문을 작성하자.

✔️ PostMapper.xml

<mapper namespace="com.codepresso.blog.repository.PostRepository">
    <insert id = "save">
        INSERT INTO POST(USER_NAME, TITLE, POST_CONTENT)
        VALUES (#{post.userName}, #{post.title}, #{post.postContent});
    </insert>

    <select id="findAll" resultType="com.codepresso.blog.vo.Post">
        SELECT *
        FROM POST;
    </select>

    <select id="findOne" resultType="com.codepresso.blog.vo.Post">
        SELECT *
        FROM POST WHERE ID = ${id}
    </select>

    <update id="updatePost">
        UPDATE POST SET TITLE = #{post.title}, POST_CONTENT = #{post.postContent} WHERE ID = #{post.id}
    </update>

    <delete id="deleteById">
        DELETE FROM POST WHERE ID = ${id}
    </delete>
</mapper>

PostRepository@Param 어노테이션으로 넘겨주었던 파라미터와 SQL문 안의 이름이 같아야 한다.
이것과 테이블 이름만 주의하면 어렵지 않게 작성할 수 있다!!

그래놓고 엄청나게 허덕였던 1인

profile
Backend Developer

2개의 댓글

comment-user-thumbnail
2023년 2월 3일

Result 클래스를 반환타입으로 하셨는데 Result 클래스는 어떻게 구성되어있나요

답글 달기
comment-user-thumbnail
2023년 2월 22일

글 잘 보고 있습니다. 좋은 정보 감사합니다!!

답글 달기