Jam's story
[Spring] Spring과 MyBatis 본문
MyBatis
ORM 프레임워크중에
- 마이바티스
- 하이버네이트
- JPA 등등
p.565
- 스프링 4.x 에는 mybatis (ibatis 2.0) 연동기능포함 X
- 스프링 3.x
- mybatis 를 스프링과 연동하는 과정 (방법)
- mybatis-spring 모듈 추가 - pom.xml
- mybatis-spring - DB연결, 트랜잭션
- mybatis
- SqlSessionFactoryBeaan 을 이용해서 SqlSessionFactory 설정 - rootContext.xml
- mybatis-spring 모듈의 핵심기능 클래스
- SqlSessionFactoryBean
- mapperLocations 속성
- SqlSessionTemplate
- SqlSessionFactoryBean
- mybatis-spring 모듈의 핵심기능 클래스
- 트랜잭션 설정
- mybatis 를 통해서 DAO 구현
- ++++ SqlSession 이용해서 구현 ++++
- 매퍼동적생성 이용해서 구현
- mybatis-spring 모듈 추가 - pom.xml
1️⃣ pom.xml
<!-- 11. p563 mybatis-spring, mybatis 모듈 2개 추가 -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
<!-- 12. -->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
2️⃣[SqlSession]FactoryBean을 이용해서 SqlSessionFactory 설정
root.context.xml
<!-- mybatis-spring.jar 모듈의 핵심 빈 객체 - Mapper XML 파일 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations">
<list>
<value>classpath:org/doit/web/newlecture/dao/mapper/NLNoticeDao.xml</value>
</list>
</property>
</bean>
<!-- DAO 구현하는 빈 객체 등록 -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"></constructor-arg>
</bean>
3️⃣NLNoticeDao.xml
형식
<mapper namespace="org.doit.web.newlecture.dao.인터페이스명">
<select id="함수명">쿼리</select>
<delete id="함수명">쿼리</delete>
${param1} 그대로
#{param1} 자료형에 맞게 변환
if - mybatis 의 동적쿼리
${} 쿼리에 넣을 때 혹은 LIKE
#{} 문자열 넣을 때
이제 NLNoticeDao.java 파일이 필요가 없이 인터페이스만 있으면 된다.
resultType : 결과값의 type.
keyProperty : 다음 쿼리에 대입될 변수명.
order : 실행시점.
<?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="org.doit.web.newlecture.dao.NoticeDao">
<!-- 샘플
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
-->
<!-- id 속성값은 메서드명 -->
<!-- String field, String query -->
<!-- ${ param1 } ${ param2 } -->
<!-- #{ param1 } #{ param2 } 전달된 값을 자료형에 맞게 변환 -->
<select id="getCount" resultType="Integer">
SELECT COUNT(*) CNT
FROM NOTICES
WHERE ${ param1 } LIKE '%${ param2 }%'
</select>
<!-- 10:05 수업시작 -->
<!-- (int page, String field, String query) -->
<!-- param1 param2 param3 -->
<!-- List<Notice> -->
<select id="getNotices" resultType="org.doit.web.newlecture.vo.Notice">
SELECT *
FROM
(
SELECT ROWNUM NUM, N.*
FROM (
SELECT *
FROM NOTICES
WHERE ${ param2 } LIKE '%${ param3 }%'
ORDER BY REGDATE DESC
) N
)
WHERE NUM BETWEEN 1 + (#{ param1 }-1)*15 AND 15 + (#{ param1 }-1)*15
</select>
<!-- 매개변수의 이름을 그대로 사용가능 -->
<!--
<delete id="delete">
DELETE FROM NOTICES
WHERE SEQ= #{ seq }
</delete>
-->
<!-- update( Notice notice ) notice.getTitle() , -->
<!-- 문제점 첨부된 파일이 없는 경우에는 , FILESRC= :filesrc X -->
<!-- mybatis의 동적 SQL -->
<update id="update" parameterType="org.doit.web.newlecture.vo.Notice">
UPDATE NOTICES
SET TITLE=#{ title }, CONTENT=#{content}
<if test="filesrc != null">
, FILESRC=#{ filesrc }
</if>
WHERE SEQ=#{seq}
</update>
<!-- (String seq) -->
<select id="getNotice" resultType="org.doit.web.newlecture.vo.Notice">
<![CDATA[
SELECT *
FROM NOTICES
WHERE SEQ = #{ seq }
]]>
</select>
<!-- insert(Notice notice) -->
<insert id="insert">
<selectKey resultType="String" order="BEFORE" keyProperty="seq">
SELECT MAX(TO_NUMBER(SEQ))+1
FROM NOTICES
</selectKey>
INSERT INTO NOTICES
( SEQ, TITLE, CONTENT, WRITER, REGDATE, HIT, FILESRC)
VALUES
( #{ seq } , #{ title }, #{ content } , #{ writer }, SYSDATE, 0, #{filesrc, javaType=String, jdbcType=VARCHAR} )
</insert>
<update id="hitUp">
update notices
set hit = hit + 1
where seq = #{ seq }
</update>
<select id="getHit" resultType="Integer">
select hit
from notices
where seq = #{ seq }
</select>
</mapper>
4️⃣NoticeDao.java -인터페이스
package org.doit.web.newlecture.dao;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.doit.web.newlecture.vo.Notice;
import org.springframework.transaction.annotation.Transactional;
public interface NoticeDao {
public int getCount(String field, String query) throws ClassNotFoundException, SQLException ;
public List<Notice> getNotices(int page, String field, String query) throws ClassNotFoundException, SQLException;
@Delete("DELETE FROM notices WHERE seq = #{ seq }")
public int delete(String seq) throws ClassNotFoundException, SQLException;
public int update( Notice notice ) throws ClassNotFoundException, SQLException;
public Notice getNotice(String seq) throws ClassNotFoundException, SQLException;
public int insert(Notice notice) throws ClassNotFoundException, SQLException ;
// 트랜잭션 테스트 용도의 메서드 추가 -> NLMemberShipService 클래스 분리
//public void insertAndPointUpOfMember(Notice notice, String id) throws ClassNotFoundException, SQLException;
// 트랜잭션 격리성을 테스트 용도 메서드 추가
public void hitUp( String seq);
public int getHit( String seq);
} // class
5️⃣CustomerController
package org.doit.web.controllers;
import java.io.File;
import java.io.FileInputStream;
import java.security.Principal;
import java.util.List;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.ibatis.session.SqlSession;
import org.doit.web.newlecture.dao.NoticeDao;
import org.doit.web.newlecture.service.MemberShipService;
import org.doit.web.newlecture.vo.Notice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
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.multipart.commons.CommonsMultipartFile;
@Controller
@RequestMapping("/customer/*")
public class CustomerController {
//@Autowired
//private NoticeDao noticeDao;
@Autowired
private SqlSession sqlSession;
@Autowired
private MemberShipService memberShipService;
// 디폴트 생성자
public CustomerController( ) {
super();
}
// 생성자 1
public CustomerController(NoticeDao noticeDao) {
super();
//this.noticeDao = noticeDao;
}
// getter, setter
/*
public NoticeDao getNoticeDao() {
return noticeDao;
}
public void setNoticeDao(NoticeDao noticeDao) {
this.noticeDao = noticeDao;
}
*/
// [p287 ]
// 20/noticePathVariable.htm
// 30/noticePathVariable.htm
@RequestMapping("members/{seq}/{deptno}/noticePathVariable.htm")
public String noticePathVariable(
@PathVariable("seq") String seq
, @PathVariable("deptno") int deptno
, HttpServletRequest request) throws Exception{
System.out.println("> seq : " + seq +" , deptno : " + deptno);
return "redirect:/customer/notice.htm";
}
// 컨트롤러 메서드 선언
// download.htm
//?
//p=customer/upload 경로
//&
//f=다운로드할 파일명
@RequestMapping("download.htm")
public void download (
HttpServletResponse response
, HttpServletRequest request
, @RequestParam("p") String p
, @RequestParam("f") String f
) throws Exception{
// 이창익(서버) -> 함세강(클라이언트 즉, 브라우저)
// 아이스크림 응답 -> 먹었다.
// 표지 [ 상자 :아이스크림 ] 응답 -> X 저장 대화상자 띄운다.
// 응답 헤드 X
String fname = f;
response.setHeader("Content-Disposition","attachment;filename="+ new String(fname.getBytes(), "ISO8859_1"));
//String fullPath = request.getServletContext().getRealPath( p + "/" + fname);
String fullPath = request.getSession().getServletContext().getRealPath( p + "/" + fname);
FileInputStream fin = new FileInputStream(fullPath);
ServletOutputStream sout = response.getOutputStream(); // 응답 스트림
byte[] buf = new byte[1024];
int size = 0;
while((size = fin.read(buf, 0, 1024)) != -1) {
sout.write(buf, 0, size);
}
fin.close();
sout.close();
} // download
@RequestMapping("noticeDel.htm")
public String noticeDel( String seq , String filesrc , HttpServletRequest request ) throws Exception {
// 1. 첨부파일이 있을 경우엔 첨부파일을 upload 폴더 삭제
// 서버 배포 실제 경로
String uploadRealPath = request.getSession().getServletContext().getRealPath("/customer/upload");
/* [1] 첨부파일을 select 알아야 왔다.
Notice notice = this.noticeDao.getNotice(seq);
String filesrc = notice.getFilesrc();
if( filesrc != null ) {
File delFile = new File( uploadRealPath , filesrc);
delFile.delete();
}
*/
File delFile = new File( uploadRealPath , filesrc);
if( delFile.exists() ) {
delFile.delete();
}
// 2.
// int rowCount = this.noticeDao.delete(seq);
NoticeDao mybatisNoticeDao = this.sqlSession.getMapper( NoticeDao.class );
int rowCount = mybatisNoticeDao.delete(seq);
if( rowCount == 1 ) return "redirect:notice.htm";
else return "redirect:noticeDetail.htm?seq="+seq;
} // notices
// /customer/noticeEdit.htm?seq=1
@RequestMapping( value = {"noticeEdit.htm"}, method = RequestMethod.GET )
public String noticeEdit( String seq , Model model) throws Exception {
// Notice notice = this.noticeDao.getNotice(seq);
NoticeDao mybatisNoticeDao = this.sqlSession.getMapper( NoticeDao.class );
Notice notice = mybatisNoticeDao.getNotice(seq);
model.addAttribute("notice", notice);
// return "noticeEdit.jsp";
return "customer.noticeEdit";
} // noticeEdit
// /customer/noticeEdit.htm?seq=2 + POST 파라미터 ( title, content )
@RequestMapping( value = {"noticeEdit.htm"}, method = RequestMethod.POST )
public String noticeEdit(
Notice notice // 커맨드 객체 = 수정된 제목, 내용 + 새로 추가된 첨부파일
, @RequestParam("o_filesrc") String ofilesrc
, HttpServletRequest request
) throws Exception {
// 첨부된 파일 O => upload폴더 저장 + notice.setFilesrc("새로 첨부된 파일명");
// " X X 원래 filesrc 유지
// 1. 첨부된 파일의 유무 확인 후에 파일 저장.
CommonsMultipartFile multipartFile = notice.getFile();
String uploadRealPath = request.getSession().getServletContext().getRealPath("/customer/upload");
if( !multipartFile.isEmpty() ) { // 첨부된 파일이 있다면
// 원래 첨부된 파일 삭제.
File delFile = new File( uploadRealPath , ofilesrc);
if( delFile.exists() ) {
delFile.delete();
}
String originalFilename= multipartFile.getOriginalFilename();
String filesystemName = getFileNameCheck(uploadRealPath, originalFilename);
File dest = new File( uploadRealPath, filesystemName);
multipartFile.transferTo(dest);
String filesrc = filesystemName;
notice.setFilesrc(filesrc);
} else {
notice.setFilesrc(ofilesrc);
}
// 2. 수정된 제목, 내용 만 update
// int rowCount = this.noticeDao.update(notice); // title, content
NoticeDao mybatisNoticeDao = this.sqlSession.getMapper( NoticeDao.class );
int rowCount = mybatisNoticeDao.update(notice);
if (rowCount==1) {
return "redirect:noticeDetail.htm?seq=" + notice.getSeq();
} else {
return "redirect:noticeDetail.htm?seq=" + notice.getSeq()+"&error";
}
} // noticeEdit
// doGet()/ doPost()
// request.getMethod() GET/POST 확인
@RequestMapping( value = {"noticeReg.htm"}, method = RequestMethod.GET )
public String noticeReg( ) throws Exception {
// return "noticeReg.jsp";
return "customer.noticeReg";
} // noticeReg
@RequestMapping( value = {"noticeReg.htm"}, method = RequestMethod.POST)
public String noticeReg(
Notice notice
, HttpServletRequest request
, Principal principal
) throws Exception { // 커맨드 객체
// 1. 첨부된 파일의 유무 확인 후에 파일 저장.
CommonsMultipartFile multipartFile = notice.getFile(); // getter
// 서버 배포 실제 경로
String uploadRealPath = null;
if( !multipartFile.isEmpty() ) { // 첨부된 파일이 있다면
uploadRealPath = request.getSession().getServletContext().getRealPath("/customer/upload");
System.out.println("> uploadRealPath : " + uploadRealPath);
String originalFilename= multipartFile.getOriginalFilename(); // 함세강 "a.txt" -> a_1.txt
String filesystemName = getFileNameCheck(uploadRealPath, originalFilename);
File dest = new File( uploadRealPath, filesystemName);
multipartFile.transferTo(dest); // 파일을 저장
// originalFilename, filesystemName Notices테이블의 컬럼이 2개 존재
String filesrc = filesystemName;
notice.setFilesrc(filesrc);
} // if
// 2. Notices 테이블에 공지사항 INSERT
// notice.setWriter("kenik");
// 인증받은 객체 정보 얻어와서 name을 얻어오면 될 것 같아요.
/* [1]
UserDetails user = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
notice.setWriter( user.getUsername() );
*/
notice.setWriter( principal.getName() );
/* 트랜잭션 테스를 위해서 주석 처리한 부분
int rowCount = this.noticeDao.insert(notice);
if( rowCount == 1 ) {
return "redirect:notice.htm";
}else {
return "noticeReg.jsp?error";
}
*/
// 트랜잭션 처리가 필요...
// this.noticeDao.insertAndPointUpOfMember(notice, "kenik");
this.memberShipService.insertAndPointUpOfMember(notice, principal.getName());
return "redirect:notice.htm";
} // noticeReg
// 11:25 수업 시작
// a.txt /upload 저장
// a-3.txt
private String getFileNameCheck(String uploadRealPath, String originalFilename) {
int index = 1;
while( true ) {
File f = new File(uploadRealPath, originalFilename);
if( !f.exists() ) return originalFilename;
// upload 폴더에 originalFilename 파일이 존재한다는 의미 asdfasf.txt (4자리)
String fileName = originalFilename.substring(0, originalFilename.length() - 4 ); // asdfasf
String ext = originalFilename.substring(originalFilename.length() - 4 ); // .txt
// asdfasf-3.txt
originalFilename = fileName+"-"+(index)+ext;
index++;
} // while
}
/**
// [1]
// ?title=xxxx&content=yyyy
@RequestMapping( value = {"noticeReg.htm"}, method = RequestMethod.POST)
public String noticeReg( Notice notice ) throws Exception { // 커맨드 객체
notice.setWriter("kenik");
int rowCount = this.noticeDao.insert(notice);
if( rowCount == 1 ) {
return "redirect:notice.htm";
}else {
return "noticeReg.jsp?error";
}
} // noticeReg
*/
/*
// 제목 title , 내용 content
// [1]
@RequestMapping( value = {"noticeReg.htm"}, method = RequestMethod.POST)
public String noticeReg( String title, String content) throws Exception {
// String title = request.getParameter("title");
Notice notice = new Notice();
notice.setWriter("kenik"); // 로그인 한 아이디
notice.setTitle(title);
notice.setContent(content);
int rowCount = this.noticeDao.insert(notice);
if( rowCount == 1 ) {
// 리다이렉트 : 공지사항 목록
// JSP : 리다이렉트 - response.sendRedirect("list.do");
// 스프링에서 리다이렉트 ? redirect: 접두어를 붙인다.
return "redirect:notice.htm";
}else {
return "noticeReg.jsp?error"; // 글쓰기 페이지로 이동할 때 ?error 있으면 글쓰기 실패하고 다시 왔다.
}
} // noticeReg
*/
/*
[1]
// noticeReg 컨트롤러 메서드 선언 + GET 방식
@RequestMapping("noticeReg.htm")
public String noticeReg( HttpServletRequest request ) throws Exception {
String requestMethod = request.getMethod(); // GET, POST
if(requestMethod.equals("GET" )) {
return "noticeReg.jsp";
}else {
return null;
}
} // noticeReg
*/
// [3]
// /customer/notice.htm?page=1&field=title&query=hh
@RequestMapping("notice.htm")
public String notices(
// ?page=1&field=title&query=hh
@RequestParam( value = "page", defaultValue = "1" ) int page
, @RequestParam( value = "field", defaultValue = "title" ) String field
, @RequestParam( value = "query", defaultValue = "" ) String query
, Model model
) throws Exception {
// NLNoticeDao 사용
//List<Notice> list= this.noticeDao.getNotices( page, field, query);
NoticeDao mybatisNoticeDao = this.sqlSession.getMapper( NoticeDao.class );
List<Notice> list= mybatisNoticeDao.getNotices(page, field, query);
model.addAttribute("list", list);
model.addAttribute("test", "Hello, Spring MVC World!");
// return "notice.jsp";
return "customer.notice"; // 스프링 타일즈 뷰리절브
} // notices
/*
// [2]
// /customer/notice.htm?page=1&field=title&query=hh
@RequestMapping("notice.htm")
public ModelAndView notices(
// ?page=1&field=title&query=hh
String page
, String field
, String query
, HttpServletRequest request
, HttpServletResponse response) throws Exception {
int _page = 1;
String _field = "title", _query="";
if ( page != null && !page.equals("") ) { _page = Integer.parseInt( page ); }
if ( field != null && !field.equals("") ) { _field = field; }
if ( query != null && !query.equals("") ) { _query = query; }
ModelAndView mv = new ModelAndView();
mv.addObject("test", "Hello, Spring MVC World!");
List<Notice> list= this.noticeDao.getNotices(_page, _field, _query);
mv.addObject("list", list);
mv.setViewName("notice.jsp");
return mv;
} // notices
*/
/*
[1]
// 공지사항 목록 [컨트롤러 메서드] <- NoticeController.java
@RequestMapping("notice.htm")
public ModelAndView notices(
HttpServletRequest request
, HttpServletResponse response) throws Exception {
String ppage = request.getParameter("page");
String pfield = request.getParameter("field");
String pquery = request.getParameter("query");
int page = 1;
String field = "title", query="";
if ( ppage != null && !ppage.equals("") ) { page = Integer.parseInt( ppage ); }
if ( pfield != null && !pfield.equals("") ) { field = pfield; }
if ( pquery != null && !pquery.equals("") ) { query = pquery; }
ModelAndView mv = new ModelAndView();
mv.addObject("test", "Hello, Spring MVC World!");
List<Notice> list= this.noticeDao.getNotices(page, field, query);
mv.addObject("list", list);
mv.setViewName("notice.jsp");
return mv;
} // notices
*/
@RequestMapping("noticeDetail.htm")
public String noticeDetail(
Model model
, HttpSession session
, String seq // ?seq=1
) throws Exception {
NoticeDao mybatisNoticeDao = this.sqlSession.getMapper( NoticeDao.class );
// this.noticeDao.hitUp(seq); // 조회수 증가
// Notice notice = this.noticeDao.getNotice(seq);
mybatisNoticeDao.hitUp(seq); // 조회수 증가
Notice notice = mybatisNoticeDao.getNotice(seq);
model.addAttribute("notice", notice);
// return "noticeDetail.jsp";
return "customer.noticeDetail"; // View Name ???.jsp X
}
/* [2]
// p 356 컨트롤러 메서드의 파라미터 타입 + 리턴타입
// /customer/noticeDetail.htm?seq=1
@RequestMapping("noticeDetail.htm")
public String noticeDetail(
Model model
, HttpSession session
, String seq // ?seq=1
) throws Exception {
// session.getAttribute("logonID");
// String seq = request.getParameter("seq"); X
Notice notice = this.noticeDao.getNotice(seq);
model.addAttribute("notice", notice);
return "noticeDetail.jsp";
}
*/
/*
[1]
// 공지사항 상세 컨트롤러 메서드 <- NoticeDetailController.java
@RequestMapping("noticeDetail.htm")
public ModelAndView noticeDetail(
HttpServletRequest request
, HttpServletResponse response) throws Exception {
String seq = request.getParameter("seq");
Notice notice = this.noticeDao.getNotice(seq);
ModelAndView mv = new ModelAndView();
mv.addObject("notice", notice);
mv.setViewName("noticeDetail.jsp");
return mv;
}
*/
} // class
'Spring' 카테고리의 다른 글
[Spring] MyBatis (0) | 2022.07.25 |
---|---|
[Spring] 스프링 5.0 의존성주입 (0) | 2022.07.24 |
[Spring] 스프링시큐리티 - 로그인 (0) | 2022.07.21 |
[Spring] 스프링 레거시 프로젝트 4.0버전 (0) | 2022.07.20 |
[Spring] 스프링 트랜잭션 격리성 (0) | 2022.07.19 |
Comments