공부방

동적쿼리&트랜잭션 본문

스프링

동적쿼리&트랜잭션

코딩 화이팅 2023. 4. 21. 11:15

동적 SQL

  • Runtime시점에서 사용자의 입력 값에 따라 동적으로 SQL을 생성하여 실행하는 방식
  • JDBC나 다른 Framework 사용 시 어려움을 느낄 수 있음
  • MyBatis는 이를 편리하게 사용할 수 있게 도움을 줌
  • JSTL이나 XML기반의 텍스트 프로세서를 사용해본 사람에게는 친숙할 것이다.

첫 웹페이지에 리스트 나열하고 검색기능 만들기

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>게시판 목록</title>
<%@ include file="../common/bootstrap.jsp" %>
</head>
<body>
	<div class="container">
		<h2>게시글 목록</h2>
		<hr>
		<div>
			<table class="table text-center">
				<tr>
					<th>번호</th>
					<th>제목</th>
					<th>글쓰니</th>
					<th>조회수</th>
					<th>등록날짜</th>
				</tr>
				<c:forEach items="${list}" var="board">
					<tr>
						<td>${board.id}</td>
						<td><a href="detail?id=${board.id}">${board.title}</a></td>
						<td>${board.writer}</td>
						<td>${board.viewCnt}</td>
						<td>${board.regDate}</td>
					</tr>
				</c:forEach>
			</table>
			
			<form action="search" class="row">
				<div class="col-2">
					<label>검색 기준 :</label>
					<select name="key" class="form-select">
						<option value="none">없음</option>
						<option value="writer">글쓰니</option>
						<option value="title">제목</option>
						<option value="content">내용</option>
					</select>
				</div>
				<div class="col-5">
					<label>검색 내용 :</label>
					<input type="text" name="word" class="form-control">
				</div>
				<div class="col-2">
					<label>정렬 기준 :</label>
					<select name="orderBy" class="form-select">
						<option value="none">없음</option>
						<option value="writer">글쓰니</option>
						<option value="title">제목</option>
						<option value="view_cnt">조회수</option>
					</select>
				</div>
				<div class="col-2">
					<label>정렬 방향 :</label>
					<select name="orderByDir" class="form-select">
						<option value="asc">오름차순</option>
						<option value="desc">내림차순</option>
					</select>
				</div>
				<div class="col-1">
					<input type="submit" value="검색" class="btn btn-primary">
				</div>
			</form>
			
			<div class="d-flex justify-content-end">
				<a class="btn btn-outline-primary" href="writeform">글등록</a>
			</div>
		</div>
		
	</div>
</body>
</html>

새로운 검색 객체를 만들어준다.

SearchCondition.java
package com.ssafy.board.model.dto;

public class SearchCondition {
	private String key;
	private String word;
	private String orderBy;
	private String orderByDir; //JSP 만든 name과 이름을 동일시 해야 알잘로 넣어주더라...
	
	public SearchCondition() { //기본생성자는 습관처럼 만들자.
	}
	
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public String getWord() {
		return word;
	}
	public void setWord(String word) {
		this.word = word;
	}
	public String getOrderBy() {
		return orderBy;
	}
	public void setOrderBy(String orderBy) {
		this.orderBy = orderBy;
	}
	public String getOrderByDir() {
		return orderByDir;
	}
	public void setOrderByDir(String orderByDir) {
		this.orderByDir = orderByDir;
	}
}
===================================================================
BoardDao.java
package com.ssafy.board.model.dao;

import java.util.List;

import com.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;

public interface BoardDao {
	// 전체 게시글을 몽땅 들고 오쎄용
	public List<Board> selectAll();

	// ID에 해당하는 게시글 하나 가져오기
	public Board selectOne(int id);

	// 게시글 등록
	public void insertBoard(Board board);

	// 게시글 삭제
	public void deleteBoard(int id);

	// 게시글 수정
	public void updateBoard(Board board);

	// 조회수 증가
	public void updateViewCnt(int id);
	
    검색 기능을 추가해줬다.
	//검색기능
	public List<Board> search(SearchCondition condition);
	
}
====================================================================
BoardService.java
package com.ssafy.board.model.service;

import java.util.List;

import com.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;

//사용자 친화적으로
public interface BoardService {
	//게시글 전체 조회
	public List<Board> getBoardList();
	
	//게시글 상세조회 (클릭시 읽는거)
	public Board readBoard(int id); 
	
	//게시글 작성
	public void writeBoard(Board board);
	
	//게시글 삭제 
	public void removeBoard(int id);
	
	//게시글 수정
	public void modifyBoard(Board board);
	
    여기도 검색 버튼 추가
	//검색 버튼을 눌렀을 때 처리하기 위한 메서드
	public List<Board> search(SearchCondition condition);
	
}
====================================================================
BoardServiceImpl.java
package com.ssafy.board.model.service;

import java.nio.charset.CodingErrorAction;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ssafy.board.model.dao.BoardDao;
import com.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;

@Service
public class BoardServiceImpl implements BoardService {
	
	private BoardDao boardDao;
	//Dao 인스턴스를 주입 해준다. (서7유지나)
	@Autowired
	public void setBoardDao(BoardDao boardDao) {
		this.boardDao = boardDao;
	}

	@Override
	public List<Board> getBoardList() {
		System.out.println("모든 게시글을 가지고 왔습니다.");
		return boardDao.selectAll();
	}

	@Override
	public Board readBoard(int id) {
		System.out.println(id+"번의 글을 읽습니다.");
		boardDao.updateViewCnt(id);
		return boardDao.selectOne(id);
	}
	
	@Transactional
	@Override
	public void writeBoard(Board board) {
		System.out.println("게시글을 작성합니다.");
//		boardDao.insertBoard(board);
		boardDao.insertBoard(board);
	}

	@Transactional
	@Override
	public void removeBoard(int id) {
		System.out.println(id+"번의 글을 삭제합니다.");
		boardDao.deleteBoard(id);
	}

	@Transactional
	@Override
	public void modifyBoard(Board board) {
		System.out.println("게시글을 수정합니다.");
		boardDao.updateBoard(board);
	}

	서비스에서 추가한 인터페이스를 클래스에도 추가
	@Override
	public List<Board> search(SearchCondition condition) {
		return boardDao.search(condition);
	}
}
====================================================================
boardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssafy.board.model.dao.BoardDao">
	<resultMap type="Board" id="boardMap">
		<result column="id" property="id"/>
		<result column="writer" property="writer"/>
		<result column="title" property="title"/>
		<result column="content" property="content"/>
		<result column="view_cnt" property="viewCnt"/>
		<result column="reg_date" property="regDate"/>
	</resultMap>

	<!-- 전체글 조회 -->
	<select id="selectAll" resultType="Board">
		SELECT id, content, writer, title, view_cnt as viewCnt, date_format(reg_date, '%y-%m-%d %H:%i:%s') as regDate
		FROM board;
	</select>
	
	<!-- 상세글 조회 -->
	<select id="selectOne" parameterType="int"  resultMap="boardMap">
		SELECT id, content, writer, title, view_cnt, date_format(reg_date, '%y-%m-%d') as reg_date
		FROM board
		WHERE id = #{id};
	</select>
	
	<!-- 게시글 등록 -->
	<insert id="insertBoard" parameterType="Board" keyProperty="id" useGeneratedKeys="true">
		INSERT INTO board (id, title, writer, content)
		VALUES (#{id}, #{title}, #{writer}, #{content})
	</insert>
	
	<!-- 게시글 삭제 -->
	<delete id="deleteBoard" parameterType="int">
		DELETE FROM board
		WHERE id = #{id}
	</delete>
	
	<!-- 조회수 증가 -->
	<update id="updateViewCnt" parameterType="int">
		UPDATE board
		SET view_cnt = view_cnt+1
		WHERE id = #{id}
	</update>
	
	
	<!-- 게시글 수정 -->
	<!-- 지금 게시글 날짜는 등록 날짜만 존재한다. 나중에 수정날짜도 같이 관리하게 된다면의 상황을보자ㅏ... -->
	<update id="updateBoard" parameterType="Board">
		UPDATE board
		SET title = #{title}, content=#{content}, reg_date = now()
		WHERE id=#{id}
	</update>	
	
	<!-- 검색기능 -->
	<select id="search" resultType="Board" parameterType="SearchCondition" >
		SELECT id, content, writer, title, view_cnt as viewCnt, date_format(reg_date, '%y-%m-%d %H:%i:%s') as regDate
		FROM board
		<!-- 어떤 기준을 가지고 검색을 할거냐!!! -->
		<if test="key != 'none'">
			WHERE ${key} LIKE concat('%', #{word}, '%')
		</if>
		<!-- 어떤 기준으로 어떤 방향으로 정렬할래? -->
		<if test="orderBy != 'none'">
			ORDER BY ${orderBy} ${orderByDir}
		</if>
	</select>
</mapper>
====================================================================
BoardController.java
package com.ssafy.board.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;
import com.ssafy.board.model.service.BoardService;

@Controller
public class BoardController {
	
	@Autowired
	private BoardService boardService;
	
	
	@GetMapping("/")
	public String showIndex() {
		//보여줄게 없어서 리스트로 바로 점프를뛰자
		return "redirect:list";
	}
	
	
	@GetMapping("list")
	public String list(Model model) {
		List<Board> list = boardService.getBoardList();
		model.addAttribute("list", list);
		return "/board/list";
	}
	
	@GetMapping("writeform")
	public String writeform() {
		return "/board/writeform";
	}
	
	@PostMapping("write")
	public String write(Board board) {
		System.out.println("등록전 : "+board);
		boardService.writeBoard(board);
		System.out.println("등록후 : "+board);
//		return "redirect:list"; // 이렇게 하지말고...
		//게시글을 보고 싶다 상세보기로 바로 가고싶어...
		return "redirect:detail?id="+board.getId();
	}
	
	@GetMapping("detail")
	public String detail(Model model, int id) {
		Board board = boardService.readBoard(id);
		model.addAttribute("board", board);
		
		return "/board/detail";
	}
	
	@GetMapping("delete")
	public String delete(int id) {
		boardService.removeBoard(id);
		return "redirect:list";
	}
	
	@GetMapping("updateform")
	public String updateform(Model model, int id) {
		//지금 게시글 하나 가져오는건 readBoard 밖에 없다. 
		model.addAttribute("board", boardService.readBoard(id));
		return "/board/updateform";
	}
	
	@PostMapping("update")
	public String update(Board board) {
		boardService.modifyBoard(board);
		return "redirect:detail?id="+board.getId();
	}
	
    위에까지 추가한 검색 기능을 추가함.
//	검색기능 작성
	@GetMapping("search")
//	public String search(Model model, String key, String word, String or.....) //이거 지저분해 처리할게 많아보여...
	public String search(Model model, SearchCondition condition ) {
		model.addAttribute("list", boardService.search(condition));
		return "/board/list";
	}
}

게시글을 등록하면 바로 detail.jsp로 넘어가 상세페이지를 볼 수 있게 해주기

BoardController.java
package com.ssafy.board.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.ssafy.board.model.dto.Board;
import com.ssafy.board.model.dto.SearchCondition;
import com.ssafy.board.model.service.BoardService;

@Controller
public class BoardController {
	
	@Autowired
	private BoardService boardService;
	
	
	@GetMapping("/")
	public String showIndex() {
		//보여줄게 없어서 리스트로 바로 점프를뛰자
		return "redirect:list";
	}
		
	@GetMapping("list")
	public String list(Model model) {
		List<Board> list = boardService.getBoardList();
		model.addAttribute("list", list);
		return "/board/list";
	}
	
	@GetMapping("writeform")
	public String writeform() {
		return "/board/writeform";
	}
	
    이 부분에서 글을 등록하면 바로 detail페이지로 넘어가게 설정을 해준다.
	@PostMapping("write")
	public String write(Board board) {
		System.out.println("등록전 : "+board);
		boardService.writeBoard(board);
		System.out.println("등록후 : "+board);
//		return "redirect:list"; // 이렇게 하지말고...
		//게시글을 보고 싶다 상세보기로 바로 가고싶어...
		return "redirect:detail?id="+board.getId();
        밑에 update부분처럼 바로 그 부분으로 넘어게 해준다.
        이렇게만 설정하면 url에서 id=0으로 가면서 바로 안된다.
	}
	
	@GetMapping("detail")
	public String detail(Model model, int id) {
		Board board = boardService.readBoard(id);
		model.addAttribute("board", board);
		
		return "/board/detail";
	}
	
	@GetMapping("delete")
	public String delete(int id) {
		boardService.removeBoard(id);
		return "redirect:list";
	}
	
	@GetMapping("updateform")
	public String updateform(Model model, int id) {
		//지금 게시글 하나 가져오는건 readBoard 밖에 없다. 
		model.addAttribute("board", boardService.readBoard(id));
		return "/board/updateform";
	}
	
	@PostMapping("update")
	public String update(Board board) {
		boardService.modifyBoard(board);
		return "redirect:detail?id="+board.getId();
	}
	
//	검색기능 작성
	@GetMapping("search")
//	public String search(Model model, String key, String word, String or.....) //이거 지저분해 처리할게 많아보여...
	public String search(Model model, SearchCondition condition ) {
		model.addAttribute("list", boardService.search(condition));
		return "/board/list";
	}
}
===================================================================
boardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssafy.board.model.dao.BoardDao">
	<resultMap type="Board" id="boardMap">
		<result column="id" property="id"/>
		<result column="writer" property="writer"/>
		<result column="title" property="title"/>
		<result column="content" property="content"/>
		<result column="view_cnt" property="viewCnt"/>
		<result column="reg_date" property="regDate"/>
	</resultMap>

	<!-- 전체글 조회 -->
	<select id="selectAll" resultType="Board">
		SELECT id, content, writer, title, view_cnt as viewCnt, date_format(reg_date, '%y-%m-%d %H:%i:%s') as regDate
		FROM board;
	</select>
	
	<!-- 상세글 조회 -->
	<select id="selectOne" parameterType="int"  resultMap="boardMap">
		SELECT id, content, writer, title, view_cnt, date_format(reg_date, '%y-%m-%d') as reg_date
		FROM board
		WHERE id = #{id};
	</select>
	
    여기서 keyProperty="id" useGeneratedKeys="true"를 추가해서 id를 바로 찾아줄 수 있게
   	등록한 아이디로 바꿔 줄 수 있게 설정해준다.
	<!-- 게시글 등록 -->
	<insert id="insertBoard" parameterType="Board" keyProperty="id" useGeneratedKeys="true">
		INSERT INTO board (id, title, writer, content)
		VALUES (#{id}, #{title}, #{writer}, #{content})
	</insert>
	
	<!-- 게시글 삭제 -->
	<delete id="deleteBoard" parameterType="int">
		DELETE FROM board
		WHERE id = #{id}
	</delete>
	
	<!-- 조회수 증가 -->
	<update id="updateViewCnt" parameterType="int">
		UPDATE board
		SET view_cnt = view_cnt+1
		WHERE id = #{id}
	</update>	
	
	<!-- 게시글 수정 -->
	<!-- 지금 게시글 날짜는 등록 날짜만 존재한다. 나중에 수정날짜도 같이 관리하게 된다면의 상황을보자ㅏ... -->
	<update id="updateBoard" parameterType="Board">
		UPDATE board
		SET title = #{title}, content=#{content}, reg_date = now()
		WHERE id=#{id}
	</update>	
	
	<!-- 검색기능 -->
	<select id="search" resultType="Board" parameterType="SearchCondition" >
		SELECT id, content, writer, title, view_cnt as viewCnt, date_format(reg_date, '%y-%m-%d %H:%i:%s') as regDate
		FROM board
		<!-- 어떤 기준을 가지고 검색을 할거냐!!! -->
		<if test="key != 'none'">
			WHERE ${key} LIKE concat('%', #{word}, '%')
		</if>
		<!-- 어떤 기준으로 어떤 방향으로 정렬할래? -->
		<if test="orderBy != 'none'">
			ORDER BY ${orderBy} ${orderByDir}
		</if>
	</select>
</mapper>

Spring Tx

  • 데이터 무결성을 위해서 사용
  • 스프링에서 제공하는 트랜잭션 기능을 활용할 수 있음
  • jar or pom.xml을 이용하여 등록

 

'스프링' 카테고리의 다른 글

Swagger/CORS  (0) 2023.04.26
REST API  (0) 2023.04.25
MyBatis-Spring  (0) 2023.04.20
MyBatis  (0) 2023.04.19
File Upload&File Download  (0) 2023.04.18