📍 엘리먼트 종류
1) <cache>
2) <cache-ref>
3) <resultMap>
4) <parameterMap>
5) <sql>
6) <select>
7) <insert>
8) <update>
9) <delete>
💁♀️ <cache> 엘리먼트란,
cache와 cache-ref 엘리먼트는 캐시를 설정하는 엘리먼트.
cache는 현재 네임스페이스에 대한 캐시 설정이고, cache-ref는 다른 네임스페이스에 대한 캐시 설정 참조를 할 때 사용.
💁 캐시(cache)란,
컴퓨터 과학에서 데이터나 값을 미리 복사해놓은 임시 장소.
캐시 접근 시간에 비해 원래 데이터를 접근하는 시간이 오래 걸리는 경우나, 값을 다시 계산하는 시간을 절약하고 싶은 경우 사용.
캐시에 데이터를 미리 복사해 놓으면 계산이나 접근 시간 없이 더 빠른 속도로 데이터에 접근 가능.
💁♀️ 디폴트 설정
1. 매퍼 XML의 모든 select 구문의 결과를 캐시
2. 매퍼 XML의 insert, update, delete는 모두 캐시를 삭제
3. 가장 오랫동안 사용하지 않은 캐시를 지우는 알고리즘(LRU-Least Recently Used)을 사용
4. 애플리케이션이 실행되는 동안 캐시를 유지. 특정 시점에 사라지거나 하지 않음
5. 캐시는 최대 1024개까지 저장
6. 캐시는 읽기/쓰기 모두 가능
<cache eviction="LRU" flushInterval="1000" size="512" readOnly="true"/>
<select id="selectCacheTest" resultType="java.lang.String">
SELECT
MENU_NAME
FROM TBL_MENU
</select>
📌 Ref. 사용 가능 속성
* eviction : 캐시 알고리즘 속성이며 디폴트는 LRU. (FIFO, SOFT, WEAK로도 설정 가능)
* flushInterval : 설정된 캐시를 얼마 동안 유지할지를 밀리초 단위로 설정하며, 양수이어야 함. (여기만 지정한다고 정확히 동작 X)
* size : 캐시에 저장할 객체의 수를 지정. 디폴트는 1024. (더 크게도 가능하지만 메모리가 충분한지 봐야함)
* readonly : 읽기만 가능한 경우 캐시 데이터의 변경 X.
💁♀️ <resultMap> 엘리먼트란,
데이터베이스 결과 데이터를 객체에 로드하는 방법을 정의하는 엘리먼트.
- resultMap엘리먼트는 마이바티스에서 가장 중요하고 강력한 엘리먼트
- ResultSet에서 데이터를 가져올때 작성되는 JDBC 코드를 대부분 줄여주는 역할을 담당
- ResultMap은 간단한 구문에서는 매핑이 필요하지 않고 복잡한 구문에서 관계를 서술하기 위해 필요
📌 Ref. 사용 가능 속성
* id : 매핑 구문에서 결과 매핑을 사용할 때 구분하기 위한 아이디
* type : 결과 매핑을 적용하는 대상 객체 타입
(매핑 구문의 결과 데이터를 저장할 자바 타입을 지정)
* extends : 자바의 상속처럼 기존에 정의된 매핑 결과를 상속받아 추가적인 매핑 정보로 확장할 때 사용
(부모 resultMap 아이디 사용)
* autoMapping : 결과 매핑을 자동 매핑을 할 것인지를 결정
(config에 설정한 전역 설정을 override. 디폴트는 unset.)
=> 오토 매핑 설정은 동일한 컬럼명이 있는 경우 위험성을 가지기 때문에 사용하지 않는 것이 안전
<resultMap type="com.greedy.common.MenuDTO" id="menuResultMap1">
<id property="code" column="MENU_CODE"/>
<result property="name" column="MENU_NAME"/>
<result property="price" column="MENU_PRICE"/>
<result property="categoryCode" column="CATEGORY_CODE"/>
</resultMap>
<resultMap type="com.greedy.common.MenuDTO" id="menuResultMap2" extends="menuResultMap1">
<!-- menuResultMap1을 확장하므로 그 외의 추가적인 속성만 기재 -->
<result property="orderableStatus" column="ORDERABLE_STATUS"/>
</resultMap>
<select id="selectResultMapTest" resultMap="menuResultMap1">
<!-- resultMap을 menuResultMap1으로 사용할 때는 ORDERABLE_STATUS가 null, menuResultMap2로 사용할 때는 출력-->
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
WHERE ORDERABLE_STATUS ='Y'
</select>
- <id> : primary key 컬럼을 매핑하기 위한 태그 (성능 향상)
- <result> : pk가 아닌 일반 컬럼에 매핑하기 위한 태그
- <constructor> : 인스턴스화 되는 클래스의 생성자에 결과를 삽입하기 위해 사용. <idArg>, <arg> 하위 엘리먼트 존재
- <association> : 복잡한 타입의 연관관계로 1:1 포함관계인 경우 사용
- <collection> : 복잡한 타입의 연관관계로 1:다 포함관계인 경우 사용
- <discriminator> : 조건에 따라 다른 resultMap이 매핑되도록 함
<resultMap type="com.greedy.common.MenuDTO" id="menuResultMap3">
<!-- id, result 엘리먼트를 이용하면 setter를 호출하기 때문에 property에 필드명을 지정하지만
생성자를 사용하는 경우 순서와 타입을 맞추어 기재해야함 -->
<constructor>
<idArg column="MENU_CODE" javaType="_int"/>
<arg column="MENU_NAME" javaType="string"/>
<arg column="MENU_PRICE" javaType="_int"/>
<arg column="CATEGORY_CODE" javaType="_int"/>
<arg column="ORDERABLE_STATUS" javaType="string"/>
</constructor>
</resultMap>
<select id="selectResultMapConstructorTest" resultMap="menuResultMap3">
SELECT
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
FROM TBL_MENU
WHERE ORDERABLE_STATUS ='Y'
</select>
<resultMap type="com.greedy.common.MenuAndCategoryDTO" id="menuAndCategoryResultMap">
<id property="code" column="MENU_CODE"/>
<result property="name" column="MENU_NAME"/>
<result property="price" column="MENU_PRICE"/>
<result property="orderableStatus" column="ORDERABLE_STATUS"/>
<association property="category" resultMap="categoryResultMap"/>
</resultMap>
<resultMap id='categoryResultMap' type="com.greedy.common.CategoryDTO">
<id property="code" column="CATEGORY_CODE"/>
<result property="name" column="CATEGORY_NAME"/>
<result property="refCategoryCode" column="REF_CATEGORY_CODE"/>
</resultMap>
<!-- 굳이 두 가지의 DTO를 따로 만들지 않아도 될 때 이 방법을 사용하는 것이 좋음 -->
<resultMap type="com.greedy.common.MenuAndCategoryDTO" id="menuAndCategoryResultMap">
<id property="code" column="MENU_CODE"/>
<result property="name" column="MENU_NAME"/>
<result property="price" column="MENU_PRICE"/>
<result property="orderableStatus" column="ORDERABLE_STATUS"/>
<association property="category" javaType="com.greedy.common.CategoryDTO">
<!-- private CategoryDTO category; 필드의 category를 입력 -->
<id property="code" column="CATEGORY_CODE"/>
<result property="name" column="CATEGORY_NAME"/>
<result property="refCategoryCode" column="REF_CATEGORY_CODE"/>
</association>
</resultMap>
<select id="selectResultMapAssociationTest" resultMap="menuAndCategoryResultMap">
SELECT
A.MENU_CODE
, A.MENU_NAME
, A.MENU_PRICE
, B.CATEGORY_CODE
, B.CATEGORY_NAME
, B.REF_CATEGORY_CODE
, A.ORDERABLE_STATUS
FROM TBL_MENU A
JOIN TBL_CATEGORY B ON (A.CATEGORY_CODE = B.CATEGORY_CODE)
WHERE A.ORDERABLE_STATUS = 'Y'
</select>
<resultMap id="categoryAndMenuResultMap" type="com.greedy.common.CategoryAndMenuDTO">
<id property="code" column="CATEGORY_CODE"/>
<result property="name" column="CATEGORY_NAME"/>
<result property="refCategoryCode" column="REF_CATEGORY_CODE"/>
<collection property="menuList" resultMap="menuResultMap"/>
</resultMap>
<resultMap type="com.greedy.common.MenuDTO" id="menuResultMap">
<id property="code" column="MENU_CODE"/>
<result property="name" column="MENU_NAME"/>
<result property="price" column="MENU_PRICE"/>
<result property="categoryCode" column="CATEGORY_CODE"/>
<result property="orderableStatus" column="ORDERABLE_STATUS"/>
</resultMap>
<resultMap id="categoryAndMenuResultMap" type="com.greedy.common.CategoryAndMenuDTO">
<id property="code" column="CATEGORY_CODE"/>
<result property="name" column="CATEGORY_NAME"/>
<result property="refCategoryCode" column="REF_CATEGORY_CODE"/>
<collection property="menuList" ofType="com.greedy.common.MenuDTO">
<id property="code" column="MENU_CODE"/>
<result property="name" column="MENU_NAME"/>
<result property="price" column="MENU_PRICE"/>
<result property="categoryCode" column="CATEGORY_CODE"/>
<result property="orderableStatus" column="ORDERABLE_STATUS"/>
</collection>
</resultMap>
<select id="selectResultMapCollectionTest" resultMap="categoryAndMenuResultMap">
SELECT
A.CATEGORY_CODE
, A.CATEGORY_NAME
, A.REF_CATEGORY_CODE
, B.MENU_CODE
, B.MENU_NAME
, B.MENU_PRICE
, B.CATEGORY_CODE
, B.ORDERABLE_STATUS
FROM TBL_CATEGORY A
LEFT JOIN TBL_MENU B ON (A.CATEGORY_CODE = B.CATEGORY_CODE)
ORDER BY A.CATEGORY_CODE
</select>
💁♀️ <sql> 엘리먼트란,
각 매핑 구문에서 공통으로 사용할 수 있는 SQL 문자열의 일부를 정의하고 재사용하기 위해 사용하는 엘리먼트
<sql id="columns">
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
</sql>
<select id="selectSqlTest" resultMap="menuResultMap2">
SELECT
<include refid="columns"/> <!-- 위의 컬럼들을 나열한 <sql>을 여기에 포함시키면 정상적으로 수행 (재활용) -->
FROM TBL_MENU
WHERE ORDERABLE_STATUS = 'Y'
</select>
💁♀️ insert, update, delete 엘리먼트는 사용하는 속성이 대부분 동일하지만 insert 엘리먼트는 좀 더 많은 속성을 정의할 수 있음
- 공통 속성
- id : 매핑 구문을 구분하는 아이디이다.
- parameterType : 파라미터의 타입을 지정한다. 이미 정의된 별칭을 사용할 수 있거나, 클래스의 full-name을 적어주면 된다.
- flushCache : 매핑 구문을 실행할 때 캐시를 지울지 여부를 설정한다.
- timeout : sql을 실행한 후 응답을 기다리는 최대 시간이다. 대게는 설정하지 않고 JDBC 드라이버 자체의 타임아웃 값을 그래도 사용한다.
- statementType : JDBC 구문 타입을 지정한다. STATEMENT, PREPARED, CALLABLE 중 하나를 쓸 수 있으며 기본값은 PREPARED이다.
- 추가 속성
- keyProperty : insert 구문의 하위 엘리먼트인 selectKey 엘리먼트에 의한 반환값을 설정할 프로퍼티를 지정한다.
- useGenerateKeys : 생성 키 값을 만들기 위해 JDBC의 getGenerateKeys메소드를 호출할지 결정하며 기본값은 false이다.
- keyColumn : 생성 키를 가진 테이블의 컬럼명을 설정한다. (키 컬럼이 테이블의 첫 번째 행이 아닌 데이터베이스에서만 필요하다)
<insert id="insertMenuTest" parameterType="com.greedy.common.MenuDTO">
INSERT
INTO TBL_MENU
(
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
)
VALUES
(
SEQ_MENU_CODE.NEXTVAL
, #{ name }
, #{ price }
, #{ categoryCode }
, #{ orderableStatus }
)
</insert>
<insert id="insertNewCategory">
INSERT
INTO TBL_CATEGORY
(
CATEGORY_CODE
, CATEGORY_NAME
, REF_CATEGORY_CODE
)
VALUES
(
SEQ_CATEGORY_CODE.NEXTVAL
, #{ category.name }
<!-- 전달받은 MenuAndCategoryDTO안에 있는 CategoryDTO에도 name이 있기 때문에 category.name로 반드시 표기 -->
, NULL
)
</insert>
<insert id="insertNewMenu"> <!-- 어렵넹^^ -->
<selectKey keyProperty="category.code" order="BEFORE" resultType="_int">
SELECT
SEQ_CATEGORY_CODE.CURRVAL
FROM DUAL
</selectKey>
INSERT
INTO TBL_MENU
(
MENU_CODE
, MENU_NAME
, MENU_PRICE
, CATEGORY_CODE
, ORDERABLE_STATUS
)
VALUES
(
SEQ_MENU_CODE.NEXTVAL
, #{ name }
, #{ price }
, #{ category.code }
, #{ orderableStatus }
)
</insert>
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" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<properties resource="connection-info.properties"/>
<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 name="com.greedy.section01.xmlmapper"/>
</mappers>
</configuration>
public class Template {
private static SqlSessionFactory sqlSessionFactory;
public static SqlSession getSqlSession() {
if(sqlSessionFactory == 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);
}
}
public class MenuDTO {
private int code;
private String name;
private int price;
private int categoryCode;
private String orderableStatus;
/* 기본 생성자, 모든 매개변수가 있는 생성자 */
/* getter & setter */
/* toString */
}
public class CategoryAndMenuDTO {
private int code;
private String name;
private Integer refCategoryCode;
private List<MenuDTO> menuList; // 하나의 카테고리를 출력할 때 거기에 해당하는 메뉴를 조회하기 위해 필드에 생성
/* 기본 생성자, 모든 매개변수가 있는 생성자 */
/* getter & setter */
/* toString */
}
public class CategoryDTO {
private int code;
private String name;
private Integer refCategoryCode;
/* 기본 생성자, 모든 매개변수가 있는 생성자 */
/* getter & setter */
/* toString */
}
public class MenuAndCategoryDTO {
private int code;
private String name;
private int price;
private CategoryDTO category; // CategoryDTO
private String orderableStatus;
/* 기본 생성자, 모든 매개변수가 있는 생성자 */
/* getter & setter */
/* toString */
}
public class ElementTestService {
private ElementTestMapper mapper;
public void selectCacheTest() {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
/* 처음 요청 시에는 시간이 걸리지만 그 이후에는 캐싱된 데이터를 불러오기 때문에 속도가 빠르다 */
for(int i = 0; i < 10; i++) {
Long startTime = System.currentTimeMillis(); // 시작 시간
List<String> nameList = mapper.selectCacheTest();
System.out.println(nameList);
Long endTime = System.currentTimeMillis(); // 끝 시간
Long interval = endTime - startTime;
System.out.println("수행 시간 : " + interval + "(ms)");
}
sqlSession.close();
}
public void selectResultMapTest() {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
List<MenuDTO> menuList = mapper.selectResultMapTest();
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
sqlSession.close();
}
public void selectResultMapConstructorTest() {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
List<MenuDTO> menuList = mapper.selectResultMapConstructorTest();
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
sqlSession.close();
}
public void selectResultMapAssociationTest() {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
List<MenuAndCategoryDTO> menuList = mapper.selectResultMapAssociationTest();
for(MenuAndCategoryDTO menu : menuList) {
System.out.println(menu);
}
sqlSession.close();
}
public void selectResultMapCollectionTest() {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
List<CategoryAndMenuDTO> categoryList = mapper.selectResultMapCollectionTest();
for(CategoryAndMenuDTO category : categoryList) {
System.out.println(category);
}
sqlSession.close();
}
public void selectSqlTest() {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
List<MenuDTO> menuList = mapper.selectSqlTest();
for(MenuDTO menu : menuList) {
System.out.println(menu);
}
sqlSession.close();
}
public void insertMenuTest(MenuDTO menu) {
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
int result = mapper.insertMenuTest(menu);
if(result > 0) {
System.out.println("메뉴 등록에 성공하였습니다.");
sqlSession.commit();
} else {
System.out.println("메뉴 등록에 실패하였습니다.");
sqlSession.rollback();
}
sqlSession.close();
}
public void insertCategoryAndMenuTest(MenuAndCategoryDTO menuAndCategory) {
// CategoryDTO도 한꺼번에 전달됨
SqlSession sqlSession = getSqlSession();
mapper = sqlSession.getMapper(ElementTestMapper.class);
int result1 = mapper.insertNewCategory(menuAndCategory); // 카테고리를 등록하고
int result2 = mapper.insertNewMenu(menuAndCategory); // 메뉴도 등록
if(result1 > 0 && result2 > 0) {
System.out.println("신규 카테고리와 메뉴 등록에 성공하였습니다.");
sqlSession.commit();
} else {
System.out.println("신규 카테고리와 메뉴 등록에 실패하였습니다.");
sqlSession.rollback();
}
sqlSession.close();
}
}
public interface ElementTestMapper {
List<String> selectCacheTest();
List<MenuDTO> selectResultMapTest();
List<MenuDTO> selectResultMapConstructorTest();
List<MenuAndCategoryDTO> selectResultMapAssociationTest();
List<CategoryAndMenuDTO> selectResultMapCollectionTest();
List<MenuDTO> selectSqlTest();
int insertMenuTest(MenuDTO menu);
int insertNewCategory(MenuAndCategoryDTO menuAndCategory);
int insertNewMenu(MenuAndCategoryDTO menuAndCategory);
}
위의 'Mapper xml에서 사용 가능한 엘리먼트' 확인
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
ElementTestService elementTestService = new ElementTestService();
do {
System.out.println("========== 매퍼 element 테스트 메뉴 ===========");
System.out.println("1. <cache> 테스트");
System.out.println("2. <resultMap> 서브 메뉴");
System.out.println("3. <sql> 테스트");
System.out.println("4. <insert> 서브 메뉴");
System.out.print("메뉴 번호를 입력하세요 : ");
int no = sc.nextInt();
switch(no) {
case 1 : elementTestService.selectCacheTest(); break;
case 2 : resultMapSubMenu(); break;
case 3 : elementTestService.selectSqlTest(); break;
case 4 : insertSubMenu(); break;
}
} while(true);
}
private static void resultMapSubMenu() {
Scanner sc = new Scanner(System.in);
ElementTestService elementTestService = new ElementTestService();
do {
System.out.println("========== <resultMap> 서브 메뉴 ===========");
System.out.println("1. <resultMap> 테스트");
System.out.println("2. <constructor> 테스트");
System.out.println("3. <association> 테스트");
System.out.println("4. <collection> 테스트");
System.out.print("메뉴 번호를 입력하세요 : ");
int no = sc.nextInt();
switch(no) {
case 1 : elementTestService.selectResultMapTest(); break;
case 2 : elementTestService.selectResultMapConstructorTest(); break;
case 3 : elementTestService.selectResultMapAssociationTest(); break;
case 4 : elementTestService.selectResultMapCollectionTest(); break;
}
} while(true);
}
private static void insertSubMenu() {
Scanner sc = new Scanner(System.in);
ElementTestService elementTestService = new ElementTestService();
do {
System.out.println("========== <insert> 서브 메뉴 ===========");
System.out.println("1. <insert> 테스트 (메뉴 등록)");
System.out.println("2. <insert> 테스트 (신규 카테고리의 메뉴 등록)");
System.out.print("메뉴 번호를 입력하세요 : ");
int no = sc.nextInt();
switch(no) {
case 1 : elementTestService.insertMenuTest(inputMenu()); break;
case 2 : elementTestService.insertCategoryAndMenuTest(inputMenuAndCategory()); break;
}
} while(true);
}
private static MenuDTO inputMenu() {
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.setName(name);
menu.setPrice(price);
menu.setCategoryCode(categoryCode);
menu.setOrderableStatus(orderableStatus);
return menu;
}
private static MenuAndCategoryDTO inputMenuAndCategory() {
Scanner sc = new Scanner(System.in);
System.out.print("신규 카테고리명을 입력하세요 : ");
String categoryName = sc.nextLine();
System.out.print("등록할 메뉴 이름을 입력하세요 : ");
String menuName = sc.nextLine();
System.out.print("메뉴의 가격을 입력하세요 : ");
int menuPrice = sc.nextInt();
System.out.print("바로 판매 등록을 할까요?(Y/N) : ");
sc.nextLine();
String orderableStatus = sc.nextLine();
MenuAndCategoryDTO menuAndCategory = new MenuAndCategoryDTO();
CategoryDTO category = new CategoryDTO();
menuAndCategory.setName(menuName);
menuAndCategory.setPrice(menuPrice);
menuAndCategory.setOrderableStatus(orderableStatus);
category.setName(categoryName);
menuAndCategory.setCategory(category);
return menuAndCategory;
}