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)
댓글 남기기