위에 글도 보시면 좋아용
포트폴리오 삽질(..)중이라 블로그가 뜸하다
오늘 하루종일 삽질한 결과를 써보겠다
게시판을 구현하고 있다.
게시글 1개에 파일은 최대 10개까지 저장될 수 있다.
DB도 아래 필드와 동일하게 구현되어있다고 생각하자(=post테이블과 postfile테이블)
class Post{
Long id;
String title;
String content;
}
class PostFile{
Long postId;
String filePath;
String fileName;
}
@Mapper
@Repository
public interface PostRepository {
@Select("select * from post where id=#{postId}")
Post findById(Long postId);
}
@Mapper
@Repository
public interface PostFileRepository {
@Select("select * from postfile where postid=#{postId}")
List<PostFile> findById(Long postId);
}
게시글 상세보기를 일단 되는대로 구현해보자
저장된 post와 postfile을 가져오는데, postfile에서는 이름만 있어도되니까 이름만 걸러내서 데이터를 응답해준다
public Map<String, Object> detail(Long postId) {
Post post = postRepository.findById(postId);
PostDTO.Response postRes = new PostDTO.Response(post);
Map<String, Object> result = new HashMap<>();
result.put("post", postRes);
List<PostFile> postFiles = postFileRepository.findById(postId);
List<String> postFileNames = new ArrayList<>();
for (PostFile postFile : postFiles) {
postFileNames.add(postFile.getFileName());
}
result.put("postFileNames", postFileNames);
return result;
}
일단 post같은 경우엔 엔터티를 직접 view로 내려주고있다
향후 확장성을 고려해서 엔터티를 dto로 변환해서 내려주자
class PostDTO{
Long id;
String title;
String content;
List<String> fileNames;
}
그리고 굳이 DB에 2번 접근해야하나? JOIN으로 해결할 수 있지 않을까?
SELECT post.id, post.title, post.content, postfile.filename
FROM post LEFT JOIN postfile ON post.id=postfile.postid;
21번 게시글 같은 경우엔 파일이 6개가 있기때문에 6번 중복해서 나온다
id, title, content 중복은 어떻게 해결하지? 또 filename을 리스트로 가져올수는없을까???
검색해보니 아래와 같이 나온다
//Xml
//실행 : findPostAndFileById(Long postId);
<resultMap id="postAndFileMap" type="PostDTO">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
<collection property="fileNames" column="filename" javaType="List" ofType="String" select="findFileById"></collection>
</resultMap>
<select id="findPostAndFileById" resultMap="postAndFileMap" parameterType="Long">
select * from post where id=#{postId}
</select>
<select id="findFileById" resultType="String" parameterType="Long">
select filename from postfile where postid=#{postId}
</select>
//어노테이션
//실행 : findPostAndFileById(Long postId);
@Select("select * from post where id=#{postId}")
@Results(
id="id", value = {
@Result(property="title", column="title"),
@Result(property="content", column="content"),
@Result(property = "fileNames", javaType = List.class, many=@Many(select="findFileById"))
})
PostDTO findPostAndFileById(Long postId);
@Select("select filename from postfile where postid=#{postId}")
List<String> fileNames = findFileById(Long postId);
xml의 <id>, 어노테이션의 id를 사용하면 post처럼 중복되는 결과는 제외해서 성능을 향상시킨다고 한다
여기서 단점은 <collection>과 @Many에서 select문을 사용하면 N+1문제가 발생할 수 있다는 것!!
findPostAndFileById()를 실행하면 자동으로 findFileById()가 실행되고, List 결과를 만들어낸다.
다시말해,
findPostAndFileById()를 실행하고 (DB에 요청 1번)
리스트 결과를 만들기위해 파일이 있는만큼 findFileById()를 실행한다(파일이 6개니까 DB에 요청 6번..)
그러면 어떻게 해야하나..
Nested ResultMap을 사용하면 된다고한다
//Xml
//실행 : findPostAndFileById(Long postId);
//나는 List<String> 이라서 ResultMap에 한번에 몰아주겠다
<resultMap id="postAndFileMap" type="PostDTO">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
<collection property="fileNames" column="id" select="fileMap"></collection>
</resultMap>
<resultMap id="fileMap" type="String">
<result column="filename"></result> //property가 없는 이유는 List<String>이라 필드가 없어서
</resultMap>
<select id="findPostAndFileById" resultMap="postAndFileMap" parameterType="Long">
select post.*, postfile.filename
from post left join postfile on post.id=postfile.postid
where post.id=#{postId}
</select>
아쉽게도 <collection>에 대응되는 어노테이션이 @Many라서.. XML로만 사용해야될 것 같다
자 근데 여기서 또하나 문제
PostDTO는 이렇게 생겼었다. 그래서 resultMap에 하나하나 필드명 적어가면서 맞춰주고...
class PostDTO{
Long id;
String title;
String content;
List<String> fileNames;
}
만약 PostDTO가... 객체도 받게된다면?!
class PostDTO{
Post post;
List<String> fileNames;
}
(뭔가 ORM스럽게 변해가고있다)
<resultMap id="postMap" type="Post">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
</resultMap>
<resultMap id="fileMap" type="String">
<result column="filename"></result>
</resultMap>
<resultMap id="postAndFileById" type="PostDTO">
<association property="post" column="id" select="postMap"></association>
<collection property="fileNames" column="id" select="fileMap"></collection>
//column은 둘다 id이다 findPostAndFileById에서의 id로 맵핑한다
</resultMap>
<select id="findPostAndFileById" resultMap="postAndFileById" parameterType="Long">
select post.*, postfile.filename
from post left join postfile on post.id=postfile.postid
where post.id=#{postId}
</select>
association으로 받아오면된다!
resultMap으로 필드 하나하나 추가해주는건 어쩔수없는 것 같다
association은 has one 관계로 객체를 받을 때,
collection은 has many 관계로 컬렉션을 받을 때 사용하자
'Backend' 카테고리의 다른 글
[JDBC] 사용방법 (0) | 2022.01.26 |
---|---|
[MyBatis] String을 넘겼을때 Out of range value for column 오류 해결..? (0) | 2022.01.25 |
Spring + MariaDB + Mybatis 연동 및 실행해보기 (0) | 2021.12.22 |
홈페이지 최초접속 시 url에 jsessionid 자동으로 안붙게하기 (0) | 2021.12.20 |
Cookie와 Session, 그리고 Token (0) | 2021.12.09 |