1. 책입력 기능 만들기
01. 개요
- 책 정보를 입력하기 위한 기능을 만든다
- 생성 화면 /create에서 제목, 분류, 가격을 입력하고 저장 버튼을 클릭하면 서버가해야 하는 일을 정의하는 것이다
02. 책 매퍼 XML 생성
- 쿼리를 작성하는 XML파일을 만든다
- 마이바티스는 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="book"></mapper>
- namespace 항목은 쿼리가 여러 개일 때 이름 공간을 분리하는 역할을 한다
- 쿼리 .XML 파일은 보통 여러개 생성되기 때문에 이를 구별하는 용도로 사용
03. 책 입력 기능 쿼리 작성
INSERT INTO 테이블이름(키1, 키2) values(키1값, 키2값)
- 쿼리를 매퍼 XML에 옮긴다, <mapper namespace="book">코드와</mapper> 사이에 작성하면 된다
<?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="book">
<insert id="insert" parameterType="hashMap" useGeneratedKeys="true" keyProperty="book_id">
<![CDATA[
insert into book
(title, category, price)
values
(#{title}, #{category}, #{price})
]]>
</insert>
</mapper>
- <insert 태그는 MYBatis에서 데이터 입력(insert)을 나타내는 태그다.
- id 항목은 네임스페이스 안에서 쿼리를 구분하는 유일한 식별자 역할을 한다
- parameterType은 쿼리에 적용할 파라미터 타입이다, 우리는 Map 타입을 이용한ㄷ
- useGeneratedKeys와 keyProperty는 한 쌍이다, useGeneratedKey가 true로 설정되면 마이바티스는 insert 쿼리 실행 후 생성된 PK를 파라미터 객체의 keyProperty속성에 넣어준다.
- <![CDATA[ 항목은 원리(Raw) 문자열을 나타낸다. <![CDATA[안에 있는 문자열은 <등의 태그 문자가 있더라도 태그로 인식하지 않는다. ]]>로 원시 문자열을 끝낸다
- 마이바티스는 쿼리가 실행될 때 파라미터를 치환한다
- #{title}은 파라미터로 입력된 키를 값으로 치환한다, 예를 들어서 파라미터 map.get("title) == "제목" 형태가 마이바티스 쿼리 XML에 전달되면 MyBatis는 #{title}을 '제목'으로 자동 변환한다
- (#{title}, #{category}, #{price}) 이 코드가 ('제목', '분류', '가격')으로 치환된다
04. 책 DAO 클래스 생성
- 매퍼 XML을 실행히키는 DAO(Data Access Object) 클래스를 생성한다
- src/main/java 하위 항목 smaple.spring.yse 패키지를 우클릭해서 BookDao 클래스를 만들면 된다
package sample.spring.yse;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
SqlSessionTemplate sqlSessionTemplate;
}
05. 책 입력 기능 DAO 메소드 생성
- 책 데이터 입력 쿼리를 실행하는 DAO 메소드를 만든다
package sample.spring.yse;
import java.util.Map;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
@Autowired
SqlSessionTemplate sqlSessionTemplate;
public int insert(Map<String, Object> map) {
return this.sqlSessionTemplate.insert("book.insert", map);
}
}
- sqlSessionTemplate.insert 메소드의 첫번째 파라미터는 SQL Mapper의 id다.
- book_SQL.xml 파일에서 namespace="book", id="insert"였다
- MyBatis는 네임스페이스+id 조합으로 쿼리를 찾아서 실행한다
- 따라서 book.insert가 매퍼 쿼리 이름이 된다
- 참고로 매퍼 SQL 구조는 아래와 같다
- sqlSessionTemplate.insert 메소드의 두번째 인수는 쿼리에 전달할 데이터이다
- 예제에서는 Map 타입 데이터 map을 전달했다
- map은 insert(Map map) 메소드가 전달받은 파라미터이므로 전달받은 파라미터를 그대로 매퍼 XML에 전달한 셈이다
- 쿼리가 실행되고 나면 파라미터로 전달된 map 객체에 book 테이블의 PK인 book_id 항목이 생긴다
- 쿼리가 실행되기 전의 테이터는 이런식으로 담겨져있었다고 하자
{
"title" : "제목",
"category" : "IT",
"price" : 10000
}
{
"title" : "제목",
"category" : "IT",
"price" : 10000
"book_id" : 1
}
- 이 설정은 매퍼SQL에서 결정했다
- sqlSessionTemplate.insert 메소드의 반환ㄱ밧은 쿼리의 영향을 받은 행수(row count)다
- ISERT 쿼리의 경우 성공하면 1개의 행(row)이 생기므로 1, 실패하면 0이 된다
- BookDao 클래스의 insert 메소드는 XML 매퍼가 반환한 값을 바로 리턴한다
06. 책 서비스 클래스 생성
- 서비스 클래스는 비즈니스 클래스가 위치하는 곳이다
- Spring MVC 구조에서 서비스 클래스는 컨트롤러와 DAO를 연결하는 역할을 한다
package sample.spring.yse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
@Autowired
BookDao bookDao;
}
- @Service 어노테이션을 붙여서 스프링 서비스 클래스임을 알려준다
- BookService 인터페이스 사용을 선언한다
- 일반적으로 서비스 레이어는 인터페이스와 클래스를 함께 사용한다
- 스프링은 직접 클래스를 생성하는 것을 지양하고 인터페이스를 통해 접근하는 것을 권장하는 프레임워크 때문에
- 물론 아직 BookService 인터페이스는 생성되지 않은 상태이기 때무에 오류가 난다
- 일단 클래스를 생성을 완료하고 인터페이스를 생성하겠다
- 데이터베이스 접근을 위해 BookDao 인스턴스를 주입받는다
- 클래스 이름이 Impl로 끝나는 것은 Implement의 약자로 관습에 가깝다
- 인터페이스와 클래스를 함께 사용하는 경우 접미어로 Impl을 붙여두면 클래스인지 인터페이스인지구별하기가 쉬워진다
07. 책 서비스 인터페이스 생성
- 책 서비스 클래스에서 선언한 BookService 인터페이스를 생성
- 서비스 인터페이스는 직접 탐색기에서 생성하지 않고 클래스에서 생성하는 방법을 취한다
- 서비스 클래스 BookServiceInpl.java의 BookService 부분에 빨간줄이 가있을것이다
- 이후 팜업 메뉴가 나오면 create interface 'BokkService' 항목을 클릭
- 새 팝업에서 Source folder를 확인하고 Finish 버튼을 누른다
- 예제에서는 서비스 클래스와 같은 패키지 안에 둘 것이므로 경로는 수정하지 않는다
08. 책 입력 기능 서비스 클래스 메소드 생성
- BookDao.insert 메소드를 실행시키는 서비스 메소드를 작성한다
- 서비스 메소드는 BookServiceImpl 클래스에 작성하면 된다
package sample.spring.yse;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
@Autowired
BookDao bookDao;
@Override
public String create(Map<String, Object> map) {
int affectRowCount = this.bookDao.insert(map);
if(affectRowCount == 1) {
return map.get("book_id").toString();
}
return null;
}
}
- bookDao.insert 메소드를 실행시킨다
- affectRowCount 변수에는 영향 받은 행수가 담긴다, insert 구문은 입력이 성공합면 1, 실패하면 0을 반환
- 입력에 성공한 경우 map 인스턴스에 book 테이블의 PK인 book_id가 담겨져 있을것이다, book_id 값을 리턴
- 실패할 경우 null을 리턴해서 실했음을 호출한 곳에 알린다
09. 책 입력 기능 서비스 인터페이스 메소드 시그니쳐 생성
- @Ovjerride 어노테이션은 자바에서 기본으로 내장된 어노테이션으로 상위 인터페이스에 정의된 것을 재정의(Override) 한다는 뜻이다
- 이클립스에서 상위 인터페이스에 메소드 시그니쳐가 없을 경우 오류를 표시해 주면서 자동으로 인터페이스 시그니쳐를 만들어주는 기능이 있다
- 이 기능을 이용하기 위해 @Override 어노테이션을 사용
- 서비스 메소드 빨간 줄에 마우스를 올리고 create 'create' in super type 'BookService'를 클릭한다
10. 책 입력 기능 컨트롤러 서비스 빈 추가
- 책입력 기능 서비스를 호출하기 위햇 서비스 빈을 추가한다
- 클래스 선언 public class BookController { 바로 아래에 추가하면 된다
- 서비스를 호출하기 위해 BookService를 의존성을 주입한다
- 이 때 BookService 인터페이스가 사용되었음을 주의하자
11. 책 입력 기능 컨트롤러 메소드 추가
- 서비스를 이용해 책을 입력하는 컨트롤러 메소드를 만든다
package sample.spring.yse;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class BookController {
@Autowired
BookService bookservice;
@RequestMapping(value = "/create", method = RequestMethod.GET)
public ModelAndView create() {
return new ModelAndView("book/create");
}
@RequestMapping(value = "/create", method = RequestMethod.POST)
public ModelAndView createPost(@RequestParam Map<String, Object> map) {
ModelAndView mav = new ModelAndView();
String bookId = this.bookservice.create(map);
if (bookId == null) {
mav.setViewName("redirect:/create");
}else {
mav.setViewName("redirect:/detail?BookId="+bookId);
}
return mav;
}
}
- 데이터 변경(입력)이 얼어나므로 http 메소드는 POST 방식으로 처리한다
- @RequestParam 어노테이션을 통해 HTTP 파라미터를 map 변수에 자동으로 바인딩한다.
- HTTP 파라미터는 브라우저에서 서버로 전달하는 데이터를 말한다
- 책을입력할 때는 제목, 분류, 가격 등의 정보가 서버로 전달되는데, 이런 것을 HTTP 파라미터라고 한다
- 스프링은 객체 타입이나 스칼라 타입일 경우 특별한 어노테이션 없이도 HTTP 파라미터를 자바 메소드의 파라미터로 변환해서 컨트롤러 메소드를 호출한다
- 하지만 Map 타입의 경우는 예외여서 @RequestParam 어노테이션을 붙여야만 HTTP 파라미터의 값을 map 안에 바인딩해 준다
- 서비스 메소드를 호출
- 데이터 입력이 실패일 경우 다시 데이터를 입력받아야 하므로 생성 화면으로 리다이렉트 한다
- ModelAndView 객체는 setViewName 메소드를 통해 뷰의 경로를 지정할 수 있다
- 만약 뷰의 경로가 redirect:로 시작하면 스프링은 뷰 파일을 찾아가는 것이 아니라 웹 페이지의 주소를 변경한다
- 위 코드는 /create 경로로 웹페이지의 주소를 변경한다
- 데이터 입력이 성공하면 상세 페이지로 이동한다
- 브라우저에서 http://localhost:8080/create에 접속해 실제로 데이터를 입력해본다!
- 아직 상세 페이지는 만들지 ㅇ낳았기 때문에 데이터가 입력되더라도 브라우저에서는 http 404 not found 오류가난다
- 데이터 입력이 성공했는지 확인!