지난 시간에 MyBatis
와 데이터베이스를 연동하고, H2 Database
콘솔을 통해 테이블이 생성된 것까지 확인했다.
이제 Mapper
를 활용해 MyBatis
설정을 마무리해보자.
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 파일이 있는데, 차례대로 댓글과 게시물을 의미한다.
작성한 파일의 위치에 맞게 디렉토리와 파일을 생성해준다.
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>
이 mapper
는 com.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인
Result 클래스를 반환타입으로 하셨는데 Result 클래스는 어떻게 구성되어있나요