sqlMapConfig.xml
Mabatis DB 연결 configuration 파일이다.
JNDI /jdbc/pool 은 server.xml 의 컨넥션 풀 설정의 이름값과 일치해야 한다.
<?xml version="1.0" encoding="UTF-8"?>
<!-- xml 지시어 -->
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 알리아스 설정 -->
<!-- typeAlias type="전체경로" alias="별칭" -->
<!-- <typeAliases>
<typeAlias type="emp.dto.EmpDTO" alias="e" />
</typeAliases> -->
<!-- db연결 참조코드 -->
<environments default="">
<environment id="">
<transactionManager type="JDBC" />
<dataSource type="JNDI">
<property name="data_source"
value="java:comp/env/jdbc/pool" />
</dataSource>
</environment>
</environments>
<!-- 실제 sql query -->
<mappers>
<!-- 샘플 설정 -->
<!-- <mapper resource="emp/mapper/emp.xml" /> -->
<mapper resource="mapper/board.xml"/>
<mapper resource="mapper/product.xml"/>
</mappers>
</configuration>
class MybatisService
mybais 연결된 SqlSession 생성 코드 이다.
session 자원 닫기 코드는 설정했는데, 스프링의 경우에는 스프링프레임워크가 자동으로 자원을 닫아주지만,
jsp 서블릿 MVC 모델 2는 프레임워크가 아니다. 따라서 session 을 닫는 설정을 해 주어야 한다.
package config;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MybatisService {
// SqlSessionFactoryBuilder => SqlSessionFactory
// => SqlSession
//SqlSession 객체 생성기
private static SqlSessionFactory factory;
static {
try {
// Java Resources의 src
Reader r = Resources.getResourceAsReader("config/sqlMapConfig.xml");
//SqlSessionFactory 생성기
factory = new SqlSessionFactoryBuilder().build(r);
r.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getFactory() {
return factory;
}
// session 자원 닫기
public static void sessionClose(SqlSession session){
if(session !=null) session.close();
}
}
product.xml
mybatis 데이를 가져올 쿼리 작성 xml 이다.
namespace="product" 는 유일해야 하며 다음과 같은
작성후 sqlMapConfig.xml 파일에서 <mapper resource="mapper/product.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="product">
<!-- id="태그의 식별자" resultType="sql 명령어의 리턴타입(레코드의 자료형)" 샵{변수} => 입력매개변수 -->
<!-- 최신상품 -->
<select id="listNewProduct" resultType="net.macaronics.web.dto.ProductVO">
<![CDATA[ select * from NEW_PRO_VIEW ]]>
</select>
<!-- 베스트상품 -->
<select id="listBestProduct" resultType="net.macaronics.web.dto.ProductVO">
<![CDATA[ select * from BEST_PRO_VIEW ]]>
</select>
<!-- 선택된상품 -->
<select id="getProduct" resultType="net.macaronics.web.dto.ProductVO">
<![CDATA[ select * from TBL_PRODUCT where pseq =#{pseq} ]]>
</select>
<!-- 종류별 상품 -->
<select id="listKindProduct" resultType="net.macaronics.web.dto.ProductVO">
<![CDATA[ select * from TBL_PRODUCT where kind =#{kind} ]]>
</select>
<!-- 메인 배너 상품 -->
<select id="bannerProduct" resultType="net.macaronics.web.dto.ProductVO">
<![CDATA[
select * from (
select rownum as num , pseq, name, price2, image from(
select pseq, name, price2, image
from tbl_product where kind=5 order by indate desc
) ) where num <= 4
]]>
</select>
<!-- 메인 배너 아래 세일 상품 5개 -->
<select id="mainOnSale" resultType="net.macaronics.web.dto.ProductVO">
<![CDATA[
select * from (
select rownum as num , pseq, name, price2, image from(
select pseq, name, price2, image
from tbl_product where kind=5 order by indate desc
) ) where num >= 4 and num < 9
]]>
</select>
</mapper>
DAO
ProductDAO
위와 같이 mybatis 설정과 코딩을 하였으면 다음과 같은 dao 클래스로 자바와 mybatis 간에 데이터를 전달 한는 코드를
작성 하면 된다. preparedstatement 를 사용한 것보다 5배이상 코드양이 줄어 든 것을 볼 수 있다.
또한, mybatis 이런 방식은 스프링과 별차이가 없다. 또한, 필자가 중복을 제거하기 위한 리팩토링을 해서 한줄로서 DB 에서 데이터를
가져 올 수 있게 하였다.
package net.macaronics.web.dao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import config.MybatisService;
import net.macaronics.web.dto.ProductVO;
public class ProductDAO {
//싱글톤
private static ProductDAO instacnce;
private ProductDAO(){
}
public static ProductDAO getInstance(){
if(instacnce==null){
instacnce =new ProductDAO();
}
return instacnce;
}
//mybatis 연결
SqlSession session;
//신상품 리스트 얻어오기
public List<ProductVO> listNewProduct(){
return commonProduct("product.listNewProduct", null);
}
//베스트상품 리스트 얻어오기
public List<ProductVO> listBestProduct(){
return commonProduct("product.listBestProduct", null);
}
//상품종류별 상품 리스트 얻어오기
public List<ProductVO> listKindProduct(String kind){
return commonProduct("product.listKindProduct", kind);
}
//매인 배너 상품 세일 중인 상품만 가져온다. 최신 4개
public List<ProductVO> bannerProduct(){
return commonProduct("product.bannerProduct", null);
}
// 메인 배너 아래 세일 상품 5개
public List<ProductVO> mainOnSale(){
return commonProduct("product.mainOnSale", null);
}
//상품번호로 상품 정보 한개 얻어오기
public ProductVO getProduct(String pseq){
List<ProductVO> list=commonProduct("product.getProduct", pseq);
//ProductVO product=new ProductVO();
return list.get(0);
}
//리스트 상품 리스트 공통
public List<ProductVO> commonProduct(String mybatisSql , String param){
List<ProductVO> list =new ArrayList<>();
try{
session=MybatisService.getFactory().openSession();
list=session.selectList(mybatisSql, param);
}catch(Exception e){
e.printStackTrace();
}finally{
//자원 닫기
MybatisService.sessionClose(session);
}
return list;
}
}
DTO
package net.macaronics.web.dto;
import com.sun.jmx.snmp.Timestamp;
public class ProductVO {
private int pseq; //product_seq 시퀀스 객체로 자동 일련번호 부여
private String name; //상품명
private String kind ; //char(1) 상품 종류
private int price1 ; // number DEFAULT 0, -- 원가
private int price2; // number default 0, -- 판매가
private int price3; // number DEFAULT 0, -- 판매가-원가
private String content; //VARCHAR2(3000) null, -- 상품 내용
private String image; // VARCHAR2(150) DEFAULT 'default.jpg',
private String useyn; // char(1) DEFAULT 'y', -- 상품 사용유무 체크 y: 사용가능 n: 사용불가능
private String bestyn; // char(1) DEFAULT 'n', -- 베스트상품인지 여부 체크 y:베스트 상품 n:베스트 상품 아님
private Timestamp indate ; //date default sysdate
Controller
총 3개의 클래스 를 작성을 한다. url 을 분리하는 작업이라 생각하면 될 것 같다.
그리고 각 페이지마다의 컨트롤 클래스 만들면 되겠다.
3개의 클래스 에서 메인 페이지 작업에 필요한 컨트롤 클래스 까지 4개 이다.
새로운 페이지를 만들시 컨트롤 클래스를 1나씩 추가해서 개발하면 될 것이다.
그리고 이 방식은 get 방식 post 방식 상관 없이 한 곳에서 처리 하는 작업이다.
REST ful 방식으로 하려면 다음과 같은 방법으로 개발을 하면 안된다.
Action
package net.macaronics.web.controll.action;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action {
public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
ActionFactory
package net.macaronics.web.controll.factory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.macaronics.web.controll.IndexAction;
import net.macaronics.web.controll.action.Action;
public class ActionFactory {
final Logger logger =LogManager.getLogger(ActionFactory.class);
//싱글톤설정
private static ActionFactory instance;
private ActionFactory(){
super();
}
public static ActionFactory getInstance(){
if(instance==null){
instance=new ActionFactory();
}
return instance;
}
//command 에서 넘어온 파라미터 값이 존재하면 실행
//즉 ,존재하면 url 만 실행된다.
public Action getAction(String command){
Action action=null;
logger.info("ActionFactory : {} ", command);
if(command.equals("index")) action=new IndexAction();
return action;
}
}
MacaronicsServlet
package net.macaronics.web.controll;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.macaronics.web.controll.action.Action;
import net.macaronics.web.controll.factory.ActionFactory;
@WebServlet("/MacaronicsServlet")
public class MacaronicsServlet extends HttpServlet {
final Logger logger =LogManager.getLogger(MacaronicsServlet.class);
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//요청시 보내 파라미터 command 값을 얻어온다.
String command=request.getParameter("command");
//파라미터 command 값이 제대로 전달되었는지 확인차 출력
logger.info("MacaronicsServlet 에서 요청을 받음을 확인 - command 값 : {} " ,command );
ActionFactory af =ActionFactory.getInstance();
Action action =af.getAction(command);
if(action!=null){
action.execute(request, response);
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//post 방식도 doGet() 에서만 요청에 대한 처리를 한다.
doGet(request, response);
}
}
IndexAction
package net.macaronics.web.controll;
import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.macaronics.web.controll.action.Action;
import net.macaronics.web.dao.ProductDAO;
import net.macaronics.web.dto.ProductVO;
public class IndexAction implements Action{
final Logger logger =LogManager.getLogger(IndexAction.class);
@Override
public void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String url ="/index.jsp";
// 데이터 베이스에서 상품 정보 얻어오는 비즈니스 로직
ProductDAO productDAO =ProductDAO.getInstance();
request.setAttribute("newProductList", productDAO.listNewProduct());//신상품
request.setAttribute("bestProductList", productDAO.listBestProduct()); //베스트상품
request.setAttribute("bannerProduct", productDAO.bannerProduct()); //배너 상품
request.setAttribute("mainOnSale", productDAO.mainOnSale());//메인 배너 아래 세일 상품 5개
//ProductVO pro =productDAO.getProduct("1");
//logger.info("IndexAction {}",pro.toString() );
RequestDispatcher dispatcher=request.getRequestDispatcher(url);
dispatcher.forward(request, response);
}
}
View
소스의 일부분만 올리겠다.
깃 허브에 소스가 있으니 참조하면 된다.
index.jsp
<ul class="nav nav-tabs aa-products-tab">
<li class="active"><a href="#newProduct" data-toggle="tab">신상품</a></li>
<li><a href="#bestProduct" data-toggle="tab">베스트상품</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<!-- Start men product category -->
<div class="tab-pane fade in active" id="newProduct">
<ul class="aa-product-catg">
<c:forEach items="${newProductList }" var="productVO">
<li>
<figure>
<a class="aa-product-img" href="MacaronicsServlet?command=product_detail&pseq=${productVO.pseq}"><img src="images/${productVO.image}" alt="신상품 이미지" width="250" height="300"></a>
<a class="aa-add-card-btn"href="#"><span class="fa fa-shopping-cart"></span>장바구니에 담기</a>
<figcaption>
<h4 class="aa-product-title"><a href="#">${productVO.name}</a></h4>
<span class="aa-product-price"><fmt:formatNumber pattern="#,### 원" value="${productVO.price2}"/></span>
</figcaption>
</figure>
<div class="aa-product-hvr-content">
<a href="#" data-toggle="tooltip" data-placement="top" title="Add to Wishlist"><span class="fa fa-heart-o"></span></a>
<a href="#" data-toggle="tooltip" data-placement="top" title="Compare"><span class="fa fa-exchange"></span></a>
<a href="#" data-toggle2="tooltip" data-placement="top" title="Quick View" data-toggle="modal" data-target="#quick-view-modal"><span class="fa fa-search"></span></a>
</div>
</li>
<!-- start single product item -->
</c:forEach>
</ul>
</div>
<!-- /신상품 end -->
시큐리티 설정
csrfguard.properties
org.owasp.csrfguard.unprotected.DailyShop=/dailyShop/* org.owasp.csrfguard.unprotected.Include=/include/* org.owasp.csrfguard.unprotected.MacaronicsServlet=/MacaronicsServlet org.owasp.csrfguard.unprotected.Css=*.css org.owasp.csrfguard.unprotected.JavaScript=*.js org.owasp.csrfguard.unprotected.Jpeg=*.jpeg org.owasp.csrfguard.unprotected.Jpg=*.jpg
실행 화면

제작 : macaronics.net - Developer Jun Ho Choi
소스 : https://github.com/braverokmc79/jsp_sin
루트 설정( http://macaronics.net/index.php/m01/jsp/view/1352) 및 server.xml 에서 DB 컨넥션 설정은 필수 설정이다.
















댓글 ( 4)
댓글 남기기