💁♀️ 동적쿼리(Dynamic Query)란,
Parameter 값에 의해 쿼리를 동적으로 변환 해주는 기능
ojdbc8.jar
/ mybatis-3.5.11.jar
파일 삽입ojdbc8.jar
/ mybatis-3.5.11.jar
파일 추가📌 Ref. mybatis.jar 파일 다운로드
mybatis-3.5.11.jar
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
username=C##GREEDY
password=GREEDY
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--connection-info.properties 불러오기 -->
<!--아래 value에 직접 입력하지않고 ${}를 이용하여 작성(보안을 위해 분리하여 사용)-->
<properties resource="connection-info.properties"/>
<!-- 별칭 설정 -->
<!-- type="클래스경로" alias="별칭"-->
<typeAliases>
<typeAlias type="com.greedy.common.SearchCriteria" alias="SerchCriteria"/>
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
// package의 명칭을 작성하여 해당 패키지 하위에 있는 모든 파일을 mapper로 사용
<package name="com.greedy.section01.xml"/>
// SelectBuilderMapper인터페이스를 사용하기 위해 section02 패키지도 mapper로 등록
<package name="com.greedy.section02.provider"/>
</mappers>
</configuration>
public class Template {
private static SqlSessionFactory sqlSessionFactory; // 정적 메모리에 SqlSessionFactory 생성 (static)
public static SqlSession getSqlSession() {
if(sqlSessionFactory == null) { // SqlSessionFactor가 null일 때만 생성 (싱글톤패턴)
String resource = "mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
return sqlSessionFactory.openSession(false);
}
}
/* 필드 */
private int menuCode;
private String menuName;
private int menuPrice;
private int categoryCode;
private String orderableStatus;
/* 기본 생성자, 모든 매개변수가 있는 생성자 */
/* getter & setter */
/* toString */
💁 검색의 기준이 되는 클래스 (Criteria : 기준)
/* 필드 */
private String condition; >>> 검색 조건
private String value; >>> 검색하려는 단어
/* 기본 생성자, 모든 매개변수가 있는 생성자 */
/* getter & setter */
/* toString */
🙋 잠깐 ! Mapper.java와 Mapper.xml이 동일한 패키지명을 갖고 있는지 확인 !
- com.greedy.section01.xml
💁 나열된 클래스 및 파일 순서가 곧 작성 순서이므로, 흐름 잘 파악하기 🔥중요🔥
(자세한 작성 순서는 [ ] 안에 있는 설명을 참고할 것)
public class Application {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
do {
System.out.println("===== Mybatis 동적 SQL =====");
System.out.println("1. if 확인하기");
System.out.println("2. choose(when, otherwise) 확인하기");
System.out.println("3. foreach 확인하기");
System.out.println("4. tream(where, set) 확인하기");
System.out.println("9. 종료하기");
System.out.print("메뉴 선택 : ");
int no = sc.nextInt();
switch(no) {
case 1 : ifSubMenu(); break;
case 2 : chooseSubMenu(); break;
case 3 : foreachSubMenu(); break;
case 4 : trimSubMenu(); break;
case 9 : System.out.println("프로그램을 종료합니다."); return;
}
} while(true);
}
private static void ifSubMenu() { // 외부에서 사용하지 않고 반환값이 없으므로, private static void
Scanner sc = new Scanner(System.in);
MenuService menuService = new MenuService(); // [MenuService 클래스 생성]
do {
System.out.println("===== if 서브 메뉴 =====");
System.out.println("1. 원하는 금액대에 적합한 추천 메뉴 목록 보여주기");
System.out.println("2. 메뉴 이름 혹은 카테고리명으로 검색하여 메뉴 목록 보여주기");
System.out.println("9. 이전 메뉴로");
System.out.print("메뉴 선택 : ");
int no = sc.nextInt();
switch(no) {
case 1 : menuService.selectMenuByPrice(inputPrice()); break; // [여기에 inputPrice() 생성 및 MenuService에 selectMenuByPrice() 생성]
case 2 : menuService.searchMenu(inputSearchCriteria()); break; // [여기에 inputSearchCriteria() 생성 및 MenuService에 searchMenu() 생성]
case 9 : return;
}
} while(true);
}
private static int inputPrice() {
Scanner sc = new Scanner(System.in);
System.out.print("검색하실 가격의 최대 금액을 입력해주세요 : ");
return sc.nextInt(); // MenuService의 selectMenuByPrice()의 인자로 전달될 것
}
private static SearchCriteria inputSearchCriteria() { // [SearchCriteria 클래스 생성]
Scanner sc = new Scanner(System.in);
System.out.print("검색 기준을 입력해주세요 (name or category) : ");
String condition = sc.nextLine();
System.out.print("검색어를 입력해주세요 : ");
String value = sc.nextLine();
return new SearchCriteria(condition, value);
}
private static void chooseSubMenu() {
Scanner sc = new Scanner(System.in);
MenuService menuService = new MenuService();
do {
System.out.println("===== choose 서브 메뉴 =====");
System.out.println("1. 카테고리 상위 분류별 메뉴 보여주기(식사, 음료, 디저트)");
System.out.println("9. 이전 메뉴로");
System.out.print("메뉴 선택 : ");
int no = sc.nextInt();
switch(no) {
case 1 : menuService.searchMenuBySupCategory(inputSupCategory()); break; // [여기에 inputSupCategory() 생성 및 MenuService에 searchMenuBySupCategory() 생성]
case 9 : return;
}
} while(true);
}
private static SearchCriteria inputSupCategory() {
Scanner sc = new Scanner(System.in);
System.out.print("상위 분류를 입력해주세요(식사, 음료, 디저트) : ");
String value = sc.nextLine();
return new SearchCriteria("category", value);
}
private static void foreachSubMenu() {
Scanner sc = new Scanner(System.in);
MenuService menuService = new MenuService();
do {
System.out.println("===== foreach 서브 메뉴 =====");
System.out.println("1. 랜덤한 메뉴 5개를 추출해서 조회하기");
System.out.println("9. 이전 메뉴로");
System.out.print("메뉴 선택 : ");
int no = sc.nextInt();
switch(no) {
case 1 : menuService.searchMenuByRandomMenuCode(createRandomMenuCodeList()); break;
case 9 : return;
}
} while(true);
}
private static List<Integer> createRandomMenuCodeList() {
Set<Integer> set = new HashSet<>(); // set은 중복값을 저장하지않는다는 특징을 가지고 있으므로 알아서 걸러줌
while(set.size() < 5) { // 5개가 담기는 순간 while문 종료
/* 현재 메뉴 번호는 1~21번 사이*/
int temp = ((int) (Math.random() * 21)) + 1;
set.add(temp);
}
/* Set과 List는 컬렉션의 후손이므로 변환 필요없이 바로 전달 가능 */
List<Integer> list = new ArrayList<>(set);
return list;
}
private static void trimSubMenu() {
Scanner sc = new Scanner(System.in);
MenuService menuService = new MenuService();
do {
System.out.println("===== tream(where, set) 서브 메뉴 =====");
System.out.println("1. 메뉴 혹은 카테고리 코드로 검색, 단 메뉴와 카테고리 둘 다 일치하는 경우도 검색하며, 검색 조건이 없는 경우 전체 검색");
System.out.println("2. 원하는 메뉴 정보만 수정하기");
System.out.println("9. 이전 메뉴로");
System.out.print("메뉴 선택 : ");
int no = sc.nextInt();
switch(no) {
case 1 : menuService.searchMenuByNameOrCategory(inputSearchCriteriaMap()); break;
case 2 : menuService.modifyMenu(inputChangeInfo()); break;
case 9 : return;
}
} while(true);
}
private static Map<String, Object> inputSearchCriteriaMap() {
Scanner sc = new Scanner(System.in);
System.out.print("검색할 조건을 입력하세요(category or name or both or null) : ");
String condition = sc.nextLine();
Map<String, Object> criteria = new HashMap<>();
if("category".equals(condition)) {
System.out.print("검색할 카테고리 코드 입력 : ");
int categoryValue = sc.nextInt();
criteria.put("categoryValue", categoryValue);
} else if("name".equals(condition)) {
System.out.print("검색할 메뉴 이름 입력 : ");
String nameValue = sc.nextLine();
criteria.put("nameValue", nameValue);
} else if("both".equals(condition)) {
System.out.print("검색할 카테고리 코드 입력 : ");
int categoryValue = sc.nextInt();
sc.nextLine();
System.out.print("검색할 메뉴 이름 입력 : ");
String nameValue = sc.nextLine();
criteria.put("categoryValue", categoryValue);
criteria.put("nameValue", nameValue);
}
return criteria;
}
private static Map<String, Object> inputChangeInfo() {
Scanner sc = new Scanner(System.in);
System.out.print("변경할 메뉴 코드 입력 : ");
int code = sc.nextInt();
System.out.print("변경할 메뉴 이름 입력 : ");
sc.nextLine();
String name = sc.nextLine();
System.out.print("변경할 카테고리 코드 입력 : ");
int categoryCode = sc.nextInt();
System.out.print("판매 여부 결정(Y/N) : ");
sc.nextLine();
String orderableStatus = sc.nextLine();
Map<String, Object> criteria = new HashMap<>();
criteria.put("code", code);
criteria.put("name", name);
criteria.put("categoryCode", categoryCode);
criteria.put("orderableStatus", orderableStatus);
return criteria;
}
}
public class MenuService {
/* 직접 DynamicSqlMapper인터페이스를 생성하여 필드에 작성 */
private DynamicSqlMapper mapper;
public void selectMenuByPrice(int price) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(DynamicSqlMapper.class);
/* 기본 자료형으로는 조건문의 값을 비교하기 어렵다. Map의 key 혹은 DTO의 getter를 시용해서 값을 확인한다. */
Map<String, Integer> map = new HashMap<>();
map.put("price", price);
/* price(기본 자료형)가 아닌 map 전달 */
List<MenuDTO> menuList = mapper.selectMenuByPrice(map); // [MenuDTO생성 및 DynamicSqlMapper인터페이스에 selectMenuByPrice() 생성]
/* Result View */
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
/* close */
sqls.close();
}
public void searchMenu(SearchCriteria searchCriteria) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(DynamicSqlMapper.class);
List<MenuDTO> menuList = mapper.searchMenu(searchCriteria);
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
sqls.close();
}
public void searchMenuBySupCategory(SearchCriteria searchCriteria) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(DynamicSqlMapper.class);
List<MenuDTO> menuList = mapper.searchMenuBySupCategory(searchCriteria);
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
sqls.close();
}
public void searchMenuByRandomMenuCode(List<Integer> randomMenuCodeList) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(DynamicSqlMapper.class);
/* Map에 집어넣기 */
Map<String, List<Integer>> criteria = new HashMap<>();
criteria.put("randomMenuCodeList", randomMenuCodeList);
List<MenuDTO> menuList = mapper.searchMenuByRandomMenuCode(criteria);
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
sqls.close();
}
public void searchMenuByNameOrCategory(Map<String, Object> searchCriteria) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(DynamicSqlMapper.class);
List<MenuDTO> menuList = mapper.searchMenuByNameOrCategory(searchCriteria);
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
sqls.close();
}
public void modifyMenu(Map<String, Object> changeInfo) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(DynamicSqlMapper.class);
int result = mapper.modifyMenu(changeInfo);
/* INSERT이므로 트랜잭션 관리 */
if(result > 0) {
sqls.commit();
System.out.println("메뉴 정보 변경에 성공하였습니다.");
} else {
sqls.rollback();
System.out.println("메뉴 정보 변경에 실패하였습니다.");
}
sqls.close();
}
}
public interface DynamicSqlMapper {
/* [1-1] 원하는 금액대에 적합한 추천 메뉴 목록 표시 */
List<MenuDTO> selectMenuByPrice(Map<String, Integer> map);
/* [1-2] 메뉴 이름 혹은 카테고리명으로 검색하여 메뉴 목록 표시 */
List<MenuDTO> searchMenu(SearchCriteria searchCriteria);
/* [2-1] 카테고리 상위 분류별 메뉴 표시 */
List<MenuDTO> searchMenuBySupCategory(SearchCriteria searchCriteria);
/* [3-1] 랜덤한 메뉴 5개를 추출해서 조회 */
List<MenuDTO> searchMenuByRandomMenuCode(Map<String, List<Integer>> criteria);
/* [4-1] 메뉴 혹은 카테고리 코드로 검색 */
List<MenuDTO> searchMenuByNameOrCategory(Map<String, Object> searchCriteria);
/* [4-2] 원하는 메뉴 정보만 수정 */
int modifyMenu(Map<String, Object> changeInfo);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
mapper를 DAO 인터페이스와 동일한 패키지에 두고 DAO 인터페이스의 풀네임을 namespace로 지정한다.
또한 매퍼 인터페이스와 XML의 이름이 동일해야한다.
작성한 메소드의 이름과 id가 일치하며 리턴 타입이 일치하는 쿼리문을 모두 작성해야 한다.
-->
<mapper namespace="com.greedy.section01.xml.DynamicSqlMapper">
<!-- 조회한 컬럼과 DTO를 매핑(연결)시키기 위한 설정으로 resultMap을 선언한다. -->
<resultMap type="com.greedy.common.MenuDTO" id="menuResultMap">
<id property="menuCode" column="MENU_CODE"/> <!-- property에는 DTO의 필드 명칭을 넣는 것 --> <!-- 사이에 입력할 값이 없으면 그냥 /로 닫기 가능 -->
<result property="menuName" column="MENU_NAME"/> <!-- result에는 id에 입력하지않은 나머지 모두 입력 -->
<result property="menuPrice" column="MENU_PRICE"/>
<result property="categoryCode" column="CATEGORY_CODE"/>
<result property="orderableStatus" column="ORDERABLE_STATUS"/>
</resultMap>
<select>
<if>
<!-- <![CDATA[]]>
<select id="selectMenuByPrice" parameterType="hashmap" resultMap="menuResultMap"> <!-- parameterType은 Mybatis api참고 -->
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
WHERE ORDERABLE_STATUS = 'Y'
<if test="price gte 0 and price lte 10000"> <!-- gte_greater than equal(>=), lte_less than equal(<=) -->
<![CDATA[
AND MENU_PRICE < #{ price }
]]> <!-- <![CDATA[]]>로 감싸면 이스케이프 ('<'등을 인식 못 할 때 연산자로 사용하기 위함) -->
</if>
<if test="price gt 10000 and price lte 20000"> <!-- if로 감싼 것은 그 조건일 때만 WHERE/AND가 실행됨 -->
AND MENU_PRICE BETWEEN 10000 AND #{ price }
</if>
<if test="price gt 20000 and price lte 30000">
AND MENU_PRICE BETWEEN 20000 AND #{ price }
</if>
<if test="price gt 30000">
AND MENU_PRICE BETWEEN 30000 AND #{ price }
</if>
ORDER BY MENU_CODE
</select>
<select>
<if>
<select id="searchMenu" parameterType="SerchCriteria" resultMap="menuResultMap"> <!-- 등록해둔 별칭으로 parameterType 설정 -->
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
<if test="condition == 'category'"> <!-- : condition이 category일 때만 JOIN 동작 -->
JOIN TBL_CATEGORY USING (CATEGORY_CODE)
</if>
WHERE ORDERABLE_STATUS = 'Y'
<if test="condition == 'name'"> <!-- : 검색조건이 name일 경우, -->
AND MENU_NAME LIKE '%' || #{ value } || '%'
</if>
<if test="condition == 'category'"> <!-- : 검색조건이 category일 경우, -->
AND CATEGORY_NAME = #{ value }
</if>
ORDER BY MENU_CODE
</select>
<select>
<choose>
<when>
<otherwise>
<select id="searchMenuBySupCategory" parameterType="SerchCriteria" resultMap="menuResultMap">
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
WHERE ORDERABLE_STATUS = 'Y'
<choose>
<when test="value == '식사'">
AND CATEGORY_CODE IN (4, 5, 6, 7)
</when>
<when test="value == '음료'">
AND CATEGORY_CODE IN (8, 9, 10)
</when>
<otherwise>
AND CATEGORY_CODE IN (11, 12) <!-- 나머지인 '디저트'는 otherwise로 처리 -->
</otherwise>
</choose>
ORDER BY MENU_CODE
</select>
<select>
<foreach>
<select id="searchMenuByRandomMenuCode" parameterType="hashmap" resultMap="menuResultMap">
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
WHERE ORDERABLE_STATUS = 'Y'
AND MENU_CODE IN
<foreach collection="randomMenuCodeList" item="menuCode" open="(" separator="," close=")">
<!-- : collection="randomMenuCodeList에 있는 List<Integer>를 사용할 것", item="변수명", open="시작할 때 필요한 문자", separator="분리기준", close="끝날 때 필요한 문자" -->
#{ menuCode }
</foreach>
<!-- foreach(반복문)를 사용하여 List 출력 -->
</select>
<select>
<where>
<if>
<trim>
<select id="searchMenuByNameOrCategory" parameterType="hashmap" resultMap="menuResultMap">
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
<!-- WHERE절이 실행되지않으면 아래의 AND절이 문제 발생, 그렇다고 WHERE을 제거할 시, AND절만 실행할 때 문제 발생 -->
<!-- <if test="nameValue != null">
WHERE MENU_NAME LIKE '%' || #{ nameValue } || '%'
</if>
<if test="categoryValue != null">
AND CATEGORY_CODE = #{ categoryValue }
</if>
-->
<!-- 해결 방법 [1] <where> 태그를 사용한다.
where 태그는 앞에 where 구문이 없으면 자동으로 구문을 붙여준다.
또한 where 내부에 모두 쿼리문이 추가되지 않는 상황인 경우에는 where을 무시한다.
and나 or로 시작하게 되면 자동으로 해당 단어를 지워준다.
-->
<!-- 아래의 <trim>을 실행시키기 위해 잠시 주석
<where>
<if test="nameValue != null">
MENU_NAME LIKE '%' || #{ nameValue } || '%'
</if>
<if test="categoryValue != null">
AND CATEGORY_CODE = #{ categoryValue }
</if>
</where>
-->
<!-- 해결 방법 [2] <trim> 태그를 사용한다.
조회 조건에 and 혹은 or로 시작하지 않는 다른 문자로 시작하는 경우
where 엘리먼트가 기본적으로 처리하는 기능에 추가 규칙을 정의하기 위해 trim 엘리먼트를 제공한다.
-->
<trim prefix="WHERE" prefixOverrides="AND | OR">
<if test="nameValue != null">
MENU_NAME LIKE '%' || #{ nameValue } || '%'
</if>
<if test="categoryValue != null">
AND CATEGORY_CODE = #{ categoryValue }
</if>
</trim>
</select>
📌 Ref.
* prefix : 처리 후 엘리먼트의 내용이 있으면 가장 앞에 붙여주는 내용을 기술한다.
* prefixOverrides : 처리 후 엘리먼트의 내용 중 가장 앞에 해당하는 문자들이 있다면 자동으로 지워준다.
* suffix : 처리 후 엘리먼트 내에 내용이 있으면 가장 뒤에 붙여준다.
* suffixOverrides : 처리 후 엘리먼트 내용 중 가장 뒤에 해당 문자들이 있다면 자동으로 지워준다.
<update>
<if>
<set>
<trim>
<update id="modifyMenu" parameterType="hashmap"> <!-- DML 구문은 resultMap 작성 필요 X (이미 int로 결과값) -->
<!--
UPDATE
TBL_MENU
<if test="name != null and name !=''">
SET MENU_NAME = #{ name }
</if>
<if test='categoeyCode != null and categoryCode gt 0'>
, CATEGORY_CODE = #{ categoryCode }
</if>
<if test="orderableStatus != null and orderableStatus !=''">
, ORDERABLE_STATUS = #{ orderableStatus }
</if>
WHERE MENU_CODE = #{ code }
-->
<!-- 하지만, SET절의 일부만 실행될 시 SET또는 콤마(,)로 인해 문제 발생 -->
<!-- 해결 방법 [1] <set> 태그 사용
<set> 엘리먼트 내부에 내용이 있을 시 set 구문을 자동으로 추가해준다.
앞 또는 뒤에 붙은 콤마를 제거해준다. -->
<!-- 아래의 <trim>을 실행시키기 위해 잠시 주석
UPDATE
TBL_MENU
<set>
<if test="name != null and name !=''">
MENU_NAME = #{ name }
</if>
<if test='categoeyCode != null and categoryCode gt 0'>
, CATEGORY_CODE = #{ categoryCode }
</if>
<if test="orderableStatus != null and orderableStatus !=''">
, ORDERABLE_STATUS = #{ orderableStatus }
</if>
</set>
WHERE MENU_CODE = #{ code }
</update>
-->
<!-- 해결 방법 [2] <trim> 태그 사용 -->
UPDATE
TBL_MENU
<trim prefix="SET" prefixOverrides=","> <!-- : 앞에 SET이나 ,가 오게 되면 제거 -->
<if test="name != null and name !=''"> <!-- 값을 입력한 컬럼만 수정하기 위해 각각 if절 설정해둠 -->
MENU_NAME = #{ name }
</if>
<if test='categoeyCode != null and categoryCode gt 0'>
, CATEGORY_CODE = #{ categoryCode }
</if>
<if test="orderableStatus != null and orderableStatus !=''">
, ORDERABLE_STATUS = #{ orderableStatus }
</if>
</trim>
WHERE MENU_CODE = #{ code }
</update>
</mapper>
public class Application {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
do {
System.out.println("===== 구문 Builder API를 이용한 동적 SQL =====");
System.out.println("1. SelectBuilder 테스트");
System.out.println("2. SqlBuilder 테스트");
System.out.println("9. 프로그램 종료");
System.out.print("메뉴 입력 : ");
int no = sc.nextInt();
switch(no) {
case 1 : selectBuilderSubMenu(); break;
case 2 : sqlBuilderSubMenu(); break;
case 9 : System.out.println("프로그램을 종료합니다."); return;
}
} while(true);
}
private static void selectBuilderSubMenu() {
Scanner sc = new Scanner(System.in);
SelectBuilderService selectBuilderService = new SelectBuilderService(); // [SelectBuilderService 클래스 생성]
do {
System.out.println("===== SelectBuilder 서브 메뉴 =====");
System.out.println("1. SelectBuilder 기본 구문 사용하기");
System.out.println("2. SelectBuilder를 이용한 동적 SQL 사용하기");
System.out.println("9. 이전 메뉴");
System.out.print("메뉴 입력 : ");
int no = sc.nextInt();
switch(no) {
case 1 : selectBuilderService.testSimpleStatement(); break; // [SelectBuilderService에 testSimpleStatement()생성]
case 2 : selectBuilderService.testDynamicStatement(inputSearchCriteria()); break;
case 9 : return;
}
} while(true);
}
private static SearchCriteria inputSearchCriteria() {
Scanner sc = new Scanner(System.in);
System.out.print("검색 기준을 입력해주세요 (name or category) : ");
String condition = sc.nextLine();
System.out.print("검색어를 입력해주세요 : ");
String value = sc.nextLine();
return new SearchCriteria(condition, value);
}
private static void sqlBuilderSubMenu() {
Scanner sc = new Scanner(System.in);
SqlBuilderService sqlBuilderService = new SqlBuilderService();
do {
System.out.println("========== SqlBuilder 서브 메뉴 ==========");
System.out.println("1. 새로운 메뉴 추가하기");
System.out.println("2. 기본 메뉴 수정하기");
System.out.println("3. 마음에 들지 않는 메뉴 삭제하기");
System.out.println("9. 이전 메뉴로");
System.out.print("메뉴를 입력하세요 : ");
int no = sc.nextInt();
switch(no) {
case 1 : sqlBuilderService.registMenu(inputNewMenu()); break;
case 2 : sqlBuilderService.modifyMenu(inputModifyMenu()); break;
case 3 : sqlBuilderService.deleteMenu(inputMenuCode()); break;
case 9 : return;
}
} while(true);
}
private static MenuDTO inputNewMenu() {
Scanner sc = new Scanner(System.in);
System.out.print("등록할 메뉴 이름을 입력하세요 : ");
String name = sc.nextLine();
System.out.print("등록할 메뉴 가격을 입력하세요 : ");
int price = sc.nextInt();
System.out.print("등록할 카테고리를 입력하세요 : ");
int categoryCode = sc.nextInt();
System.out.print("판매 등록 여부를 입력하세요(Y/N) : ");
sc.nextLine();
String orderableStatus = sc.nextLine();
MenuDTO menu = new MenuDTO();
menu.setMenuName(name);
menu.setMenuPrice(price);
menu.setCategoryCode(categoryCode);
menu.setOrderableStatus(orderableStatus);
return menu;
}
private static MenuDTO inputModifyMenu() {
Scanner sc = new Scanner(System.in);
System.out.print("수정할 메뉴 코드를 입력하세요 : ");
int code = sc.nextInt();
System.out.print("수정할 메뉴 이름을 입력하세요 : ");
sc.nextLine();
String name = sc.nextLine();
System.out.print("수정할 메뉴 가격을 입력하세요 : ");
int price = sc.nextInt();
System.out.print("수정할 카테고리를 입력하세요 : ");
int categoryCode = sc.nextInt();
System.out.print("수정할 판매 등록 여부를 입력하세요(Y/N) : ");
sc.nextLine();
String orderableStatus = sc.nextLine();
return new MenuDTO(code, name, price, categoryCode, orderableStatus);
}
private static int inputMenuCode() {
Scanner sc = new Scanner(System.in);
System.out.print("삭제할 메뉴 번호를 입력하세요 : ");
int code = sc.nextInt();
return code;
}
}
💁 SelectBuilder 테스트 시작 !
public class SelectBuilderService {
private SelectBuilderMapper mapper; // [mybatis-config.xml파일에 mapper 새로 등록 후 사용]
public void testSimpleStatement() {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(SelectBuilderMapper.class);
List<MenuDTO> menuList = mapper.selectAllMenu();
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
sqls.close();
}
public void testDynamicStatement(SearchCriteria searchCriteria) {
SqlSession sqls = getSqlSession();
mapper = sqls.getMapper(SelectBuilderMapper.class);
List<MenuDTO> menuList = mapper.searchMenuByCondition(searchCriteria);
if(menuList != null && !menuList.isEmpty()) {
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
} else {
System.out.println("검색 결과가 존재하지 않습니다.");
}
sqls.close();
}
}
public interface SelectBuilderMapper {
@Results(id = "menuResultMap", value= { // 어떤 컬럼에 어떤 값을 넣을지 작성
@Result(id = true, property = "menuCode", column = "MENU_CODE"), // id = true라는 특정값을 줌
@Result(property = "menuName", column = "MENU_NAME"),
@Result(property = "menuPrice", column = "MENU_PRICE"),
@Result(property = "categoryCode", column = "CATEGORY_CODE"),
@Result(property = "orderableStatus", column = "ORDERABLE_STATUS")
})
@SelectProvider(type=SelectBuilderProvider.class, method="selectAllMenu") // 사용할 클래스명, 메소드명 명시 [SelectBuilderProvider 클래스 생성]
List<MenuDTO> selectAllMenu();
// SelectBuilderProvider를 호출하여 SQL구문 반환받기
@ResultMap("menuResultMap") // : id 값으로 자동 매핑하겠다.
@SelectProvider(type=SelectBuilderProvider.class, method="searchMenuByCondition")
List<MenuDTO> searchMenuByCondition(SearchCriteria searchCriteria);
}
public class SelectBuilderProvider { // 개신기하당
/* SQL문을 문자열 형태로 반환하도록 반환 타입을 String으로 지정한다. */
/* SelectBuilderMapper에서 호출하여 사용할 예정 */
public String selectAllMenu() {
return new SQL() // SQL객체를 생성하여 SELECT할 컬럼과 테이블, 조건을 SQL객체에 설정해주는 과정
.SELECT("MENU_CODE")
.SELECT("MENU_NAME")
.SELECT("MENU_PRICE")
.SELECT("CATEGORY_CODE")
.SELECT("ORDERABLE_STATUS")
.FROM("TBL_MENU")
.WHERE("ORDERABLE_STATUS = 'Y'").toString(); // 문자열로 만들어 반환
}
public String searchMenuByCondition(SearchCriteria searchCriteria) {
SQL sql = new SQL();
sql
.SELECT("MENU_CODE")
.SELECT("MENU_NAME")
.SELECT("MENU_PRICE")
.SELECT("CATEGORY_CODE")
.SELECT("ORDERABLE_STATUS")
.FROM("TBL_MENU");
if("category".equals(searchCriteria.getCondition())) {
sql
.JOIN("TBL_CATEGORY USING(CATEGORY_CODE)")
.WHERE("ORDERABLE_STATUS = 'Y'")
.AND()
.WHERE("CATEGORY_NAME = #{ value }");
} else if ("name".equals(searchCriteria.getCondition())){
/* 가변인자를 이용하면 자동 AND로 처리하기 때문에 OR를 사용해야 하는 경우는 .OR() 메소드를 사용해야함 */
sql
.WHERE("ORDERABLE_STATUS = 'Y'", "MENU_NAME LIKE '%' || #{value} || '%'");
// WHERE절에 가변인자를 넣을 수 있음 (인자 여러 개 나열 가능)
}
return sql.toString(); // String으로 변환하여 반환
}
}
💁 SqlBuilder 테스트 시작 !
public class SqlBuilderService {
private SqlBuilderMapper mapper;
public void registMenu(MenuDTO menu) {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(SqlBuilderMapper.class);
int result = mapper.registMenu(menu);
if(result > 0) {
System.out.println("신규 메뉴 등록에 성공하셨습니다.");
sqlSession.commit();
} else {
System.out.println("신규 메뉴 등록에 실패하셨습니다.");
sqlSession.rollback();
}
sqlSession.close();
}
public void modifyMenu(MenuDTO menu) {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(SqlBuilderMapper.class);
int result = mapper.modifyMenu(menu);
if(result > 0) {
System.out.println("메뉴 정보 수정에 성공하셨습니다.");
sqlSession.commit();
} else {
System.out.println("메뉴 정보 수정에 실패하셨습니다.");
sqlSession.rollback();
}
sqlSession.close();
}
public void deleteMenu(int code) {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(SqlBuilderMapper.class);
int result = mapper.deleteMenu(code);
if(result > 0) {
System.out.println("메뉴 삭제에 성공하셨습니다.");
sqlSession.commit();
} else {
System.out.println("메뉴 삭제에 실패하셨습니다.");
sqlSession.rollback();
}
sqlSession.close();
}
}
public interface SqlBuilderMapper {
@InsertProvider(type=SqlBuilderProvider.class, method="registMenu") // [SqlBuilderProvider 클래스 생성]
int registMenu(MenuDTO menu);
@UpdateProvider(type=SqlBuilderProvider.class, method="modifyMenu")
int modifyMenu(MenuDTO menu);
/* Map이나 getter가 있는 DTO가 아닌 기본 자료형 값을 전달해야 하는 경우에는 @Param 어노테이션을 이용한다.
* 또는 전달해야 하는 값이 2개 이상인 경우도 @Param 어노테이션을 이용할 수 있다.
* 단, Provider 메소드의 매개변수 선언부는 없어야 한다. */
@DeleteProvider(type=SqlBuilderProvider.class, method="deleteMenu")
int deleteMenu(@Param("code") int code); // @Param("code") : 파라미터를 "code"로 지칭할 것이다
}
public class SqlBuilderProvider {
public String registMenu(MenuDTO menu) {
SQL sql = new SQL();
sql
.INSERT_INTO("TBL_MENU")
.VALUES("MENU_CODE", "SEQ_MENU_CODE.NEXTVAL") // (컬럼명, 삽입할 값 이름)
.VALUES("MENU_NAME", "#{ menuName }")
.VALUES("MENU_PRICE", "#{ menuPrice }")
.VALUES("CATEGORY_CODE", "#{ categoryCode }")
.VALUES("ORDERABLE_STATUS", "#{ orderableStatus }");
return sql.toString();
}
public String modifyMenu(MenuDTO menu) {
SQL sql = new SQL();
/* 비어있지 않은 값만 업데이트되는 동적 쿼리로 작성 */
sql
.UPDATE("TBL_MENU");
if(menu.getMenuName() != null && !"".equals(menu.getMenuName())) {
sql.SET("MENU_NAME = #{ menuName }");
}
if(menu.getMenuPrice() > 0) { // null은 의미가 없고 유효한 값인지 판단하기 위해 > 0 입력
sql
.SET("MENU_PRICE = #{ menuPrice }");
}
if(menu.getCategoryCode() > 0) {
sql
.SET("CATEGORY_CODE = #{ categoryCode }");
}
if(menu.getOrderableStatus() != null && !"".equals(menu.getOrderableStatus())) {
sql
.SET("ORDERABLE_STATUS = #{ orderableStatus }");
}
sql.WHERE("MENU_CODE = #{ menuCode }");
return sql.toString();
}
public String deleteMenu() { // 매개변수부에 int code를 선언하지 않음 !!!!!!!!!
SQL sql = new SQL();
sql
.DELETE_FROM("TBL_MENU")
.WHERE("MENU_CODE = #{ code }"); // @Param("code")라고 명시했으므로 code로 값을 입력
return sql.toString();
}
}