본문 바로가기
Java Web Programming/6. Spring | MyBatis

[Spring/MyBatis] Spring + MyBatis 적용 연습 2 ! (+CDATA, GROUP BY, LIKE, INSERT, Sequence 시퀀스)

by 파프리카_ 2020. 11. 2.
728x90
반응형

MyBatis 설정

/mybatis.config

 

#{something} = "a"

⇒ ProductVO.setSomething("a")

 

* CDATA Section : Character Data 아래 영역은 xml tag가 아니라 SQL 문자임을 알려준다.

 

/product.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Sql Mapper (MyBatis Mapper) -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace 설정 -->
<mapper namespace="product">

	<sql id="selectProduct">
		<!-- product_no as productNO
		     : SQL의 column명과 productVO의 instance 변수명이 일치하지 않을 경우,
		       SQL에서 별칭으로 productVO의 instance 변수명을 기준으로 명시하면 됨
		-->
		SELECT product_no as productNO, name, maker, price
		FROM   spring_product
	</sql>
	
	<select id="getTotalProductCount" resultType="int">
		SELECT COUNT(*)
		FROM   spring_product
	</select>
	
	<select id="getMakerKindList" resultType="string">
		SELECT DISTINCT(maker)
		FROM   spring_product
	</select>
	
	<!-- resultType="productVO"
	     : spring-config.xml에서 typeAliasesPackage를 통해 준 별칭 -->
	<select id="getAllProductListOrderByPriceDesc" resultType="productVO">
		<include refid="selectProduct"></include>
		ORDER BY price DESC
	</select>

	<select id="findProductByNo" resultType="productVO" parameterType="string">
		<include refid="selectProduct"></include>
		WHERE product_no = #{value}
	</select>
	
	<select id="findProductListByMakerAndPrice" resultType="productVO" 
			parameterType="productVO">
		<include refid="selectProduct"></include>
		WHERE maker=#{maker} AND price > #{price}
	</select>
	
	<!-- CDATA : Character Data 아래 영역은 xml tag가 아니라 SQL 문자임을 알려준다. 
		 - 비교연산자로 < 를 쓸 때면, xml 태그로 인식하여, 
		   'add child'>'CDATA Section'을 통해 '<'가 들어간 쿼리 줄을 안에 넣어줌 -->
	<select id="findProductListByPrice" resultType="productVO" parameterType="int">
			<include refid="selectProduct"></include>
		<![CDATA[
			WHERE price < #{value}
		]]>
	</select>
	
	<select id="getProductListByGroupByMaker" resultType="map">
		SELECT   MAKER, COUNT(*) AS TOTAL_COUNT, MAX(price) AS MAX_PRICE
		FROM     spring_product
		GROUP BY MAKER
	</select>
	
	<!-- LIKE를 사용 -->
	<select id="findProductListLikeName" resultType="productVO" parameterType="string">
		<include refid="selectProduct"></include>
		WHERE name LIKE '%' || #{value} || '%'
	</select>
	
	<!-- INSERT -->
	<insert id="registerProduct" parameterType="productVO">
		INSERT INTO spring_product(product_no, name, maker, price) 
		VALUES(spring_product_seq.nextval, #{name}, #{maker}, #{price})
	</insert>
	
	<!-- 
		상품 등록 시점에 미리 상품 번호(product_no)를 시퀀스로 생성하여,
		productVO의 productNo 변수에 할당하여,
		spring_product table에 INSERT한다!
	 -->
	<insert id="registerProductVer2" parameterType="productVO">
		<!-- productVO 객체의 productNo에 nextval을 할당해준다. -->
		<selectKey keyProperty="productNo" resultType="string" order="BEFORE">
			SELECT spring_product_seq.nextval
			FROM   dual
		</selectKey>
		INSERT INTO spring_product(product_no, name, maker, price) 
		VALUES(#{productNo}, #{name}, #{maker}, #{price})
	</insert>
	
</mapper>

Spring 설정

1) DBCP 정의

2) MyBatis와 Spring 연동 설정 

- SqlSessionFactory 생성 (DBCP(datebase connection pool) 주입 *factory : DBCP와 SQL 정보가 들어있다.

- MyBatis에서 쓸 mapper location(path) 주입

3) typeAliasesPackage(패키지 별칭 타입) 설정  ex) org.kosta.model.vo.MemberVO -> memberVO 

(패키지 아래의 클래스는 모두 자동으로 소문자로 시작하는 클래스명으로 별칭을 주도록 설정)

4) MyBatis와 Spring 연동 시, 생산성을 위해 SqlSessionTemplate 클래스 이용

- 생성자로 SqlSessionFactory 주입

- 개발 생산성을 위해 SqlSessionTemplate 클래스를 이용, SqlSessionTemplate은 선언적 방식의 트랜잭션 제어를 지원
    (= AOP 기반 Transaction 제어)

5) DAO bean 생성 (template을 생성자로!)

 

/spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- DBCP 정의 -->
	<bean id="dbcp" class="org.apache.commons.dbcp2.BasicDataSource">
		<property name="driverClassName" value="oracle.jdbc.OracleDriver"/>
		<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:xe"/>
		<property name="username" value="scott"/>
		<property name="password" value="tiger"/>
	</bean>
	
	<!-- MyBatis와 Spring 연동 설정-->
	
	<!-- 1. sqlSessionFactoryBean -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- DBCP(datebase connection pool) 주입 -->
		<property name="dataSource" ref="dbcp"/>
		<!-- MyBatis에서 쓸 mapper loation(path) 주입 -->
		<property name="mapperLocations" value="classpath:/mybatis/config/*.xml"/>
		<!-- Package에 별칭주기 : 
		     org.kosta.model.vo 패키지 아래의 클래스는 
		     모두 자동으로 소문자로 시작하는 클래스명으로 별칭을 주도록 설정
		     ex) org.kosta.model.ProductVO 는 productVO로!  -->
		<property name="typeAliasesPackage" value="org.kosta.model.vo"/>
	</bean>
	
	<!-- 2. 개발 생산성을 위해 SqlSessionTemplate 클래스를 이용
		 SqlSessionTemplate은 선언적 방식의 트랜잭션 제어를 지원
		 (= AOP 기반 Transaction 제어) -->
	<bean id="SqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactoryBean"/>
	</bean>

	<!-- 3. DAO 생성 
		: productDAOImpl bean 생성
		- 생성 시에 생성자에 SqlSessionTemplate을 DI한다-->
	<bean id="productDAO" class="org.kosta.model.dao.ProductDAOImpl">
		<constructor-arg ref="SqlSessionTemplate"/>
	</bean>
	
</beans>

SQL

1. spring_product TABLE 생성

CREATE TABLE spring_product(
    product_no  NUMBER        PRIMARY KEY,
    name        VARCHAR2(100) NOT NULL,
    maker       VARCHAR2(100) NOT NULL,
    price       NUMBER        NOT NULL
)

-- 시퀀스 생성
CREATE SEQUENCE spring_product_seq;

 

2. 테이블에 값 INSERT

INSERT INTO SPRING_PRODUCT VALUES (spring_product_seq.nextVAL, '바나나우유', '바나공장', 3000);
INSERT INTO SPRING_PRODUCT VALUES (spring_product_seq.nextVAL, '그릭요거트', '발효발효', 2500);
INSERT INTO SPRING_PRODUCT VALUES (spring_product_seq.nextVAL, '딸기요거트', '발효발효', 1500);
INSERT INTO SPRING_PRODUCT VALUES (spring_product_seq.nextVAL, '바나나크림치즈', '바나공장', 2000);
INSERT INTO SPRING_PRODUCT VALUES (spring_product_seq.nextVAL, '바나칩', '바나공장', 300);

 

> spring_product TABLE


/org.kosta.model.vo

ProductVO는 spring-config.xml에서 

<property name="typeAliasesPackage" value="org.kosta.model.vo"/> 로 설정하여, 

소문자로 시작하는 productVO로 별칭이 정해진다.

 

/ProductVO.java

package org.kosta.model.vo;
/*
 * ProductVO는 Spring-config.xml에서 
 * <property name="typeAliasesPackage" value="org.kosta.model.vo"/> 로 설정하여, 
 * 소문자로 시작하는 productVO로 별칭이 정해진다.
 */
public class ProductVO {
	private String productNo; //실제 DB 컬럼명은 product_no로 일치하지 않는다.
	/* product.xml에서 SQL과 productVO의 변수명을 일치시키기 위해, 
	 * (product.xml에서는 productVO.set[변수명]으로 값을 주입함)
	 * SQL문의 column명의 별칭을 productVO의 인스턴스 변수명을 기준으로 명시해준다.
	 */ 
	private String name;
	private String maker;
	private int price ;
	
	public ProductVO() {
		super();
	}
	
	public ProductVO(String name, String maker, int price) {
		super();
		this.name = name;
		this.maker = maker;
		this.price = price;
	}

	public ProductVO(String productNo, String name, String maker, int price) {
		super();
		this.productNo = productNo;
		this.name = name;
		this.maker = maker;
		this.price = price;
	}

	public String getProductNo() {
		return productNo;
	}

	public void setProductNo(String productNo) {
		this.productNo = productNo;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getMaker() {
		return maker;
	}

	public void setMaker(String maker) {
		this.maker = maker;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return "ProductVO [productNo=" + productNo + ", name=" + name + ", maker=" + maker + ", price=" + price + "]";
	}
	
}

/org.kosta.model.dao

/ProductDAO.java <<interface>>

package org.kosta.model.dao;

import java.util.List;
import java.util.Map;

import org.kosta.model.vo.ProductVO;
/*
 * ProductDAOImpl의 method의 추상 메서드가 있음
 * (ProductDAOImpl가 override할 method)
 */
public interface ProductDAO {

	int getTotalProductCount();

	List<String> getMakerKindList();

	List<ProductVO> getAllProductListOrderByPriceDesc();

	ProductVO findProductByNo(String productNo);

	List<ProductVO> findProductListByMakerAndPrice(ProductVO pvo);

	List<ProductVO> findProductListByPrice(int price);

	List<Map<String, Object>> getProductListByGroupByMaker();

	List<ProductVO> findProductListLikeName(String word);

	void registerProduct(ProductVO paramVO);

	void registerProductVer2(ProductVO paramVO);
}

 

/ProductDAOImpl.java

package org.kosta.model.dao;

import java.util.List;
import java.util.Map;

import org.kosta.model.vo.ProductVO;
import org.mybatis.spring.SqlSessionTemplate;

public class ProductDAOImpl implements ProductDAO {
	//dbcp, sqlSessionFactoryBean 정보를 모두 담고 있는 SqlSessionTemplate 가져오기
	// sqlSessionFactoryBean : sqlSession생성
	// sqlSession : SQL 쿼리문 실행
	private SqlSessionTemplate template;

	//spring-config.xml으로부터 DI(주입)를 생성자로 설정한다.
	public ProductDAOImpl(SqlSessionTemplate template) {
		super();
		this.template = template;
	}
	
	// getTotalProductCount : 총 상품 수 반환 메서드
	@Override
	public int getTotalProductCount() {
		return template.selectOne("product.getTotalProductCount");
	}

	// getMakerKindList : maker 종류 리스트 반환 메서드
	@Override
	public List<String> getMakerKindList() {
		return template.selectList("product.getMakerKindList");
	}

	// getAllProductListOrderByPriceDesc 
	// : 가격 내림차순으로 상품 정보 리스트 반환 메서드
	@Override
	public List<ProductVO> getAllProductListOrderByPriceDesc() {
		return template.selectList("product.getAllProductListOrderByPriceDesc");
	}
	
	// findProductByNo : productNo로 상품정보 반환 메서드
	@Override
	public ProductVO findProductByNo(String productNo) {
		return template.selectOne("product.findProductByNo", productNo);
	}

	//findProductListByMakerAndPrice : maker과 price로 상품 정보 리스트로 반환 메서드
	@Override
	public List<ProductVO> findProductListByMakerAndPrice(ProductVO paramVO) {
		return template.selectList("product.findProductListByMakerAndPrice", paramVO);
	}

	//findProductListByPrice : price로 상품 정보 리스트로 반환 메서드
	@Override
	public List<ProductVO> findProductListByPrice(int price) {
		return template.selectList("product.findProductListByPrice", price);
	}

	//getProductListByGroupByMaker : 제조사별 상품수, 최고가 조회
	@Override
	public List<Map<String, Object>> getProductListByGroupByMaker() {
		// selectList 하면 자동으로, 
		// List의 각 값이 Map으로 변환되며, 
		// column 명이 Map의 key로, 값이 Map의 value(Object)로 들어간다.
		return template.selectList("product.getProductListByGroupByMaker");
	}

	//findProductListLikeName : 이름에 'word'요소가 들어가는 상품 정보 리스트로 반환 메서드
	@Override
	public List<ProductVO> findProductListLikeName(String word) {
		return template.selectList("product.findProductListLikeName", word);
	}

	//registerProduct: 새로운 productVO INSERT 하기
	@Override
	public void registerProduct(ProductVO paramVO) {
		template.insert("product.registerProduct", paramVO);
	}

	//registerProductVer2: 상품 등록 시, 등록한 상품의 상품 번호 조회하기
	@Override
	public void registerProductVer2(ProductVO paramVO) {
		template.insert("product.registerProductVer2", paramVO);
	}
	
}


/test

/TestSpringMyBatis.java

package test;

import java.util.List;
import java.util.Map;

import org.kosta.model.dao.ProductDAO;
import org.kosta.model.vo.ProductVO;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpringMyBatis {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext factory
			= new ClassPathXmlApplicationContext("spring-config.xml");
		
		ProductDAO productDAO = (ProductDAO) factory.getBean("productDAO");
		
		// 1. 총 상품 수
		System.out.println("** 1. spring과 연동 테스트 - 총 상품 수 : "
				+ productDAO.getTotalProductCount() + " **"); 
		
		// 2. maker 종류를 리스트로 반환
		System.out.println("\n** 2. maker 종류를 리스트로 반환 **");
		List<String> list = productDAO.getMakerKindList();
		for (String maker:list) {
			System.out.println(maker);
		}
				
		// 3. 가격 내림차순으로 product 정보 반환
		System.out.println("\n** 3. 가격 내림차순으로 product 정보 반환 **");
		List<ProductVO> list2 = productDAO.getAllProductListOrderByPriceDesc();
		for (ProductVO vo:list2) 
			System.out.println(vo); 
		
		// 4.  productNo로 상품정보 반환
		System.out.println("\n** 4. productNo로 상품정보 반환 **");
		String productNo = "1";
		ProductVO pvo = productDAO.findProductByNo(productNo);
		System.out.println(pvo);
		
		// 5. maker가 '바나공장'이고, price가 1000을 초과하는 상품정보 반환
		System.out.println("\n** 5. maker가 '바나공장'이고, price가 1000을 초과하는 상품정보 반환 **");
		String maker = "바나공장";
		int price = 1000;
		ProductVO paramVO = new ProductVO(null, null, maker, price);
		List<ProductVO> list3 = productDAO.findProductListByMakerAndPrice(paramVO);
		for(ProductVO vo:list3)
			System.out.println(vo);
		
		
		// 6. price가 2000 미만은 상품정보 반환
		System.out.println("\n** 6. price가 2000 미만은 상품정보 반환 **");
		int price2 = 2000;
		List<ProductVO> list4 = productDAO.findProductListByPrice(price2);
		for (ProductVO vo:list4)
			System.out.println(vo);
		
		// 7. 상품정보 조회 : maker 별 상품 수, 최고가 조회
		System.out.println("\n** 7. 제조사별 상품수, 최고가 조회 **");
		List<Map<String, Object>> list5 = productDAO.getProductListByGroupByMaker();
		for (int i = 0; i < list5.size(); i++) {
			Map<String, Object> map = list5.get(i);
			System.out.println(map.get("MAKER") + " " + map.get("TOTAL_COUNT")
					+ " " + map.get("MAX_PRICE"));
		}
		
		// 8. 이름에 word가 들어가는 상품 정보 조회
		System.out.println("\n** 8. 이름에 word가 들어가는 상품 정보 조회 **");
		String word = "요거트";
		List<ProductVO> list6 = productDAO.findProductListLikeName(word);
		for (ProductVO vo:list6)
			System.out.println(vo);
		
		// 9. 상품 정보를 새로 INSERT 해보기
		System.out.println("\n** 9. 상품 정보를 새로 INSERT 해보기 **");
		productDAO.registerProduct(new ProductVO("요거트케이크", "발효발효", 4500));
		List<ProductVO> list7 = productDAO.getAllProductListOrderByPriceDesc();
		for(ProductVO vo:list7)
			System.out.println(vo);
		
		// 10. 상품 등록 시, 등록한 상품의 상품 번호 조회하기 (Oracle Sequence)
		System.out.println("\n** 10. 상품 등록 시, 등록한 상품의 상품 번호 조회하기 **");
		ProductVO paramVO2 = new ProductVO("바나나롤", "바나공장", 3500);
		System.out.println("DB에 INSERT 전 정보: "+paramVO2);
		productDAO.registerProductVer2(paramVO2);
		System.out.println("DB에 INSERT 후 정보: "+paramVO2);
		
		factory.close();
	}
}

[ 결과 ]

728x90
반응형