MyBatis - Test Dao(공통코드)

지환·2023년 11월 30일
0

Jsp & Servlet

목록 보기
2/21
post-thumbnail

Test Dao[전체코드]

package com.example.demo.dao;

import java.util.HashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.util.MyBatisCommonFactory;

public class TestDao extends JFrame{
	SqlSessionFactory sqlSessionFactory = null;
	SqlSession sqlSession  = null;
	public TestDao() {
		//메소드 호출을 통해서 객체를 주입받는 코드가 싱글톤 패턴에서 자주 등장하는 방식이다.
		sqlSessionFactory = MyBatisCommonFactory.getSqlSessionFactory();
	}
	public void currentTime() {
		System.out.println("currentTime 호출");
		String time = null;
		try {
			sqlSession = sqlSessionFactory.openSession();
			time = sqlSession.selectOne("currentTime");
			System.out.println(time);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public void procEmpcursor() {
		Map<String,Object> pMap = new HashMap<>();
		try {
			sqlSession = sqlSessionFactory.openSession();
			sqlSession.selectOne("proc_empcursor", pMap);
			System.out.println(pMap);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	//개발팀
	//공통팀 - 고급정보 - 간부회의
	public void procLogin1() {
		Map<String,Object> pMap = new HashMap<>();
		try {
			pMap.put("m_id", "apple");
			pMap.put("m_pw", "123777777");
			sqlSession = sqlSessionFactory.openSession();
			sqlSession.selectOne("proc_login1", pMap);
			System.out.println(pMap);
			//System.out.println(pMap.get("key"));
			Object keys[] = pMap.keySet().toArray();
			for(int i=0;i<keys.length;i++) {
				if("r_msg".equals(keys[i])) {
					JOptionPane.showMessageDialog(this, pMap.get("r_msg"));
				}				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		TestDao td = new TestDao();
		//td.currentTime();
		td.procEampcursor();
		//td.procLogin1();
	}
}

  • 마이바티스랑 연동하는 부분인데 main부터 먼저 살펴보자.

    TestDao td = new TestDao(); 하는 순간 생성자가 호출된다.
    
    그 이전에 전역변수를 초기화 해야된다.
    
    SqlSessionFactory sqlSessionFactory = null;
    SqlSession sqlSession  = null;
    
    해당 전역변수를 초기화한다., 그렇다면 이 부분에 대한 SqlSessionFactory가 뭘까?
    

[ MyBatis 구성요소 역할 – Mapping API ]

MyBatis API

SqlSessionFactoryBuilder

  • 역할 : SqlSessionFactory 생성

SqlSessionFactory

  • MyBatis의 전역 정보를 가지고 실행을 제어
  • SqlSession 생성
  • Application당 하나만 생성하는 것이 권장됨

SqlSession

  • 역할 : 쿼리 실행 처리
  • 작업 단위 별로 SqlSessionFactory로 부터 생성

MyBatisCommonFactory

package com.util;

import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyBatisCommonFactory {
	Logger logger = LoggerFactory.getLogger(MyBatisCommonFactory.class);
	public static SqlSessionFactory sqlSessionFactory = null;
	public static void init() {
		try {
			String resource = "com/mybatis/MapperConfig.xml";
			Reader reader = null;
			reader = Resources.getResourceAsReader(resource);
			if(sqlSessionFactory == null) {
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}/////////////end of init()
	public static SqlSessionFactory getSqlSessionFactory() {
		init();
		return sqlSessionFactory;
	}
}

자바에서 Logger 사용 시 LoggerFactory로 Logger를 사용하는 방법과 lombok를 쓸 경우 @Slf4j 어노테이션을 넣고 Logger를 사용할 수 있는데,

먼저 LoggerFactory를 사용하는 경우에는
Logger log = LoggerFactory.getLogger(class명.class)

이렇게 사용해주면 되고 lombok을 사용할 경우에는 훨씬 간단하게 클래스명 위에 @Slf4j 어노테이션을 입력한 후

log.메소드명(..);

이런 식으로 사용해주면 된다(출처)

Logger logger = LoggerFactory.getLogger(MyBatisCommonFactory.class);
=====================================================================
Logger log = LoggerFactory.getLogger(class.class)

를 통해 Logger를 등록할 수 있다.

이 부분에 해당하는 클래스를 넣었다. 다음엔

public static SqlSessionFactory sqlSessionFactory = null;

SqlSessionFactory 타입의 참조 변수를 sqlSessionFactory null값으로 선언했고, 이 값은 getSqlSessionFactory 메소드를 통해 주입받는다.(외부 클래스에서 / static으로 선언한 이유는 (미리 로딩 되어있어야해서)

init

	public static void init() {
		try {
			String resource = "com/mybatis/MapperConfig.xml";
			Reader reader = null;
			reader = Resources.getResourceAsReader(resource);
			if(sqlSessionFactory == null) {
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}/////////////end of init()


이 경로를 resource 변수에 담는다.

왜 굳이 Reader?

  • Resources.getResourceAsReader(resource); 사용하기 위해서

    • Resources 상대경로 파일읽기(경로정리)
      // src/main/resources 경로의 파일읽기
      Reader reader = Resources.getResourceAsReader("config_properties.properties");
      reader에 해당 경로를 넣는다.

sqlSessionFactory 가 null값이라면 sqlSessionFactory 를 생성해야하는 Builder가 sqlSessionFactory를 생성해야된다.

if(sqlSessionFactory == null) {
				sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, "development");
			}
  • SqlSessionFactory는 Mybatis 설정파일(reader)과 SqlSessionFactoryBuilder를 이용하여 인스턴스를 생성한다.

getSqlSessionFactory

	public static SqlSessionFactory getSqlSessionFactory() {
		init();
		return sqlSessionFactory;
	}

외부 클래스에서 static이기 떄문에 클래스.메소드멍() 으로 호출한다면 해당 sqlSessionFactory 객체를 불러올 수 있다.
근데 왜 init을 저 자리에 뒀을까?

시점의 문제이기 때문에 저기에 주지 않으면 생성된 sqlSessionFactory 인스턴스를 찾아 볼 수 없다. 그렇기 떄문에 저 자리에 init()이 들어가야된다.

자세히 보면 인스턴스 생성은 init() 메소드 안에서 이뤄졌다. 그렇기 때문에 꼭 호출해야된다.

그 생성된 인스턴스를 가지고 TestDao에서 어떻게 활용하는지 살펴보자.


다시 앞으로 돌아와서

public class TestDao extends JFrame{
	SqlSessionFactory sqlSessionFactory = null;
	SqlSession sqlSession  = null;
	public TestDao() {
		//메소드 호출을 통해서 객체를 주입받는 코드가 싱글톤 패턴에서 자주 등장하는 방식이다.
		sqlSessionFactory = MyBatisCommonFactory.getSqlSessionFactory();
	}
  • 전역변수 초기화 및 생성자에서 sqlSessionFactory 객체를 주입받는다.(싱글톤)

시간을 찍어보는 메소드 & Oracle

	public void currentTime() {
		System.out.println("currentTime 호출");
		String time = null;
		try {
			sqlSession = sqlSessionFactory.openSession();
			time = sqlSession.selectOne("currentTime");
			System.out.println(time);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
  • try문을 살펴보자. sqlSession은 전역변수로 선언된 SqlSession타입이다.
  • 현재 시간을 가져와서 time에 찍는다.

sqlSession 이란?

    myBatis - SqlSession: SQL 실행
    openSession(): SqlSession 얻기
    SqlSession sqlSession = sqlSessionFactory.openSession();
  • sqlSessionFactory에서 경로를 읽어왔기 떄문에 밑에 같은 동작이 가능한거임

  • SqlSessionFactory 객체의 openSession()을 호출해서 SqlSession을 얻는다.

  • SqlSession은 MyBatis에서 실제 SQL 실행을 담당하는 컴포넌트이다. 즉 SQL을 실행하려면 SqlSession이 필요하며, 이 객체가 JDBC 드라이버를 사용한다.

SqlSession 클래스에서는 CRUD를 위한 다양한 메서드를 제공한다.

  • selectOne 메서드는 오직 하나의 객체만을 리턴해야 한다
    한개 이상을 리턴하거나 null 이 리턴된다면, exception 이 발생할 것이다.

  • sqlSession.selectOne () 사용법
    호출 API 는 여러 건을 가져오는 것이 아니라 한 건을 가져오는 것을 사용해야 합니다.
    int count = sqlSession.selectOne("쿼리문 아이디", 파라미터);

procEmpcursor

	public void procEmpcursor() {
		Map<String,Object> pMap = new HashMap<>();
		try {
			sqlSession = sqlSessionFactory.openSession();
			sqlSession.selectOne("proc_empcursor", pMap);
			System.out.println(pMap);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
  • 궁금증 > 그러면 pMap에 put해야 정보가 담기는데 put하지 않았는데 어떻게 그 정보가 담겨서 찍혀?

    • selectOne 메서드를 사용할 때, 매개변수로 전달한 쿼리의 실행 결과가 자동으로 Map에 매핑되어 반환될 수 있다.

    • MyBatis의 내부 동작 방식 중 하나로, 쿼리의 실행 결과를 자동으로 매핑하여 Map에 담아주는 메커니즘이다.

    • 쿼리의 실행 결과가 한 행이고, 그 결과가 여러 개의 컬럼으로 구성되어 있다면, MyBatis는 자동으로 해당 결과를 Map에 매핑한다.

    • 그 매핑 코드는 이와같다

      <select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
      	{  call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
      </select>
      	- parameterType="java.util.Map"이 부분이 Map형태로 매핑 될 수 있게 설정한다. + (empVO-매핑)
      
  • proc_empcursor 쿼리가 여러 행을 반환하는 경우는?

    • selectList 메서드를 사용하면 된다. 이 경우에는 각 행이 Map에 매핑되어 List로 반환된다.

selectone 동작원리에 대해 살펴보자.

  1. 컬럼 이름을 Key로 사용하고 and 컬럼 값을 Value로 사용한다. 이게 무슨 말이냐면,

    • "SELECT id, name, age FROM employee"와 같이 세 개의 컬럼을 선택했다면, Map에는 "id", "name", "age"라는 세 개의 Key가 생성된다.

    • id" 컬럼의 값은 Map에서 "id"라는 Key에 대응하는 Value로 설정되고, "name" 컬럼의 값은 "name"이라는 Key에 대응하는 Value로 설정된다.

| id | name   | age |
|----|--------|-----|
| 1  | Alice  | 25  |


sqlSession.selectOne("proc_empcursor", pMap);




pMap = {
    "id": 1,
    "name": "Alice",
    "age": 25
}

{}

  • 형식으로 표현된 것은 자바의 Map 인터페이스나 그 구현체를 사용하여 데이터를 표현한 것

  • JSON(JavaScript Object Notation)은 {} 형식으로 표현되는 경량의 데이터 교환 형식이다.

    • JSON 데이터는 자바의 Map 구조와 비슷해 보일 수 있지만, JSON은 특정 언어에 종속되지 않기 때문에 자바뿐만 아니라 여러 언어에서 사용할 수 있는 표준 데이터 형식이다.

procLogin1

	public void procLogin1() {
		Map<String,Object> pMap = new HashMap<>();
		try {
			pMap.put("m_id", "apple");
			pMap.put("m_pw", "123777777");
			sqlSession = sqlSessionFactory.openSession();
			sqlSession.selectOne("proc_login1", pMap);
			System.out.println(pMap);
			//System.out.println(pMap.get("key"));
			Object keys[] = pMap.keySet().toArray();
			for(int i=0;i<keys.length;i++) {
				if("r_msg".equals(keys[i])) {
					JOptionPane.showMessageDialog(this, pMap.get("r_msg"));
				}				
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
  • pMap 변수를 선언했고 타입은 Map<>타입이다.
    try문을 보면, pMap.put을 통해서 값을 주는 것을 볼 수 있다.
    put을 한 이유는 m_id값과 m_pw값을 미리 지정해서 select 할 떄 넣기위함이다.

  • 이제 sqlSession.selectOne("proc_login1", pMap); 이 코드에 대해 설명하겠다.

    • selectone은 단일행만 가져오는데, 프로시저는 리턴타입이 없기 때문에, OUT 출력변수를 통해 리턴해야된다. 이와 관련된 설정은 XML 파일에 있다. (mode = out...etc) 중요한 부분은 proc_login1가 Ref 커서이기 때문에 한행만 리턴하는게 아니라 해당 테이블의 행들을 전부 리턴한다. 그 때, Map형태로 저장 되는 것이다. 😈그렇게 되면 덮어 씌워지는 것이 아니라, 계속 누적되어 저장되는 것이다.(중요)
  • 그 반환값은 pMap 객체에 저장된다.

  • Object keys[] = pMap.keySet().toArray(); 이 부분은 Map의 Key를 toArray로 변환했다. 그 배열이 keys에 저장된다.

  • for문을 돌면서 if문 조건을 수행하는데, r_msg.equals(keys[i]))는 현재 반복 중인 키가 "r_msg"인지 확인한다.

왜 r_msg를 기준으로 했는가?에 대한 답변

오라클 PL/SQL 프로시저는 기본적으로 함수(Function) 처럼 결과값을 Return 받을 수 없다.

하지만 OUT 변수를 사용하여 결과를 Return 받는 것처럼 보이게 할 수 있다. 근데 지금 r_msg가 출력변수이기 떄문에 해당 조건을 줬다.

sqlSession.selectOne("proc_login1", pMap);

for(int i=0;i<keys.length;i++) {
				if("r_msg".equals(keys[i])) {
					JOptionPane.showMessageDialog(this, pMap.get("r_msg"));
				}				
			}

r_msg는 출력변수이다. 여기서 keys 배열은 무엇일까? sqlSession.selectOne("proc_login1", pMap);결과 정보를 pMap에 리턴하는데, 당연히 디비에 저장된 건수가 14건이니 총 14개의 key = {[vo,vo,vo * 14]}로 이루어져 있을 것이다. 반환된 결과값에 대해 담겨진

r_msg가 있다면 모달처리를 하려고 이렇게 진행 한 것이다. 당연히 리턴 시 이 부분도 포함되어 리턴된다. (why? 출력변수(out))

profile
아는만큼보인다.

0개의 댓글