read

Persistence Framework

퍼시스턴스 프레임워크 이야기를 해볼까요?
먼저 퍼시스턴스의 의미를 알아야겠군요.
Persistence는 개발분야에서는 데이터의 지속성을 말한답니다.


라이브러리는 개발에 필요한 도구라면
프레임워크는 구조를 완성해 놓은 머신(기계)라고 볼 수 있습니다.

퍼시스턴스 프레임워크는 JDBC 프로그래밍의 복잡함과 번거로움없이 간단한 작업으로 데이터베이스와 연동되는 시스템을 개발할 수 있게 하지요.
데이터의 저장, 조회, 변경, 삭제를 다루는 클래스와 설정 파일들의 집합입니다.

퍼시스턴스 프레임워크에는
SQL Mapper로 mybatis(구 iBATIS)가 있고요.
객체 관계 맵퍼(Object-Relational Mapper, ORM)으로 하이버네이트(Hibernate), 탑링크(TopLink), JPA 가 있지요.

Mybatis 소개

Mybatis 는 JDBC 프로그래밍을 단순화하기 위해 만든 라이브러리로 클린턴 비긴(Clinton Begin)이 개발했답니다.

2004년 iBATIS 아파치 소프트웨어 재단(Apache Software Foundation)에 기부 했다가
2010년 6월 Google Code로 이동하면서 프로젝트 이름을 mybatis 로 변경했다고 하네요.
2014년 깃허브(github.com/mybatis) 로 이동했지요.


Mybatis 는 자체 커넥션 풀을 구축할 수있고
환경(개발, 테스트, 운영)에 따라 사용할 DB 지정도 가능하지요.
select 결과 캐싱 할수 있게 설정할 수 있고요.
sql mapper 에서 사용할 값객체(Value Object)에 별명 지정해서 긴 이름대신 짧고 명료하게 사용할 수 도 있지요.

핵심 컴포넌트

builder pattern

마이바티스를 사용하기 위한 기본적인 자바 인터페이스는 SqlSession이다.
이 인터페이스를 통해 명령어를 실행하고 매퍼를 얻으며 트랜잭션을 관리 할 수 있다.
SqlSession은 SqlSessionFactory인스턴스를 사용해서 만든다.
SqlSessionFactory는 몇가지 방법으로 SqlSession인스턴스를 생성하기 위한 메소드를 포함하고 있다.
SqlSessionFactory자체는 XML, 애노테이션 또는 자바 설정에서 SqlSessonFactory를 생성할 수 있는 SqlSessionFactoryBuilder를 통해 만들어진다.
https://mybatis.org/mybatis-3/ko/java-api.html#sqlSessions

component Desc.
SqlSessionFactory SqlSession 객체를 생성
SqlSession 맵퍼파일에서 SQL를 찾아 JDBC 드라이버를 통해 SQL을 질의 후 data를 반환하는 객체
SqlSessionFactoryBuilder 설정파일에 따라 SqlSessionFactory 생성
mybatis 설정파일 Database connection 등 설정 내용
SQL mapper 파일 SQL 파일, SqlSession 객체가 참조

SqlSession 메소드

selectOne과 selectList의 차이점은 selectOne메소드는 오직 하나의 객체만을 리턴해야 한다는 것이다.
한개 이상을 리턴하거나 null 이 리턴된다면 예외가 발생할 것이다.
얼마나 많은 객체가 리턴될지 모른다면 selectList를 사용하라.
https://mybatis.org/mybatis-3/ko/java-api.html#sqlSessions

method Desc.
T selectOne(String statement, Object parameter) 객체 하나만 반환
List selectList(String statement, Object parameter) Value Object 반환
Cursor selectCursor(String statement, Object parameter)  
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)  
int insert(String statement, Object parameter) 입력 rows 반환
int update(String statement, Object parameter) 변경 rows 반환
int delete(String statement, Object parameter) 삭제 rows 반환

Builder Pattern

빌더 패턴(Builder Patter)은 집짓기와 비슷하지요.
설계도면을 바탕으로 전문가가 집을 짓는것처럼
설정에 따라 객체를 생성하는 빌더객체가 필요한 다른 객체를 생성하지요.
Mybatis 에서는 SqlSessionFactoryBuilder 객체가 mybatis-cofing.xml 설정파일을 기초로 SqlSessionFactory 객체를 생성한답니다.

매개변수

#{프로퍼피명} 자리에는 객체의 프로퍼티 값을 사용합니다.
객체의 프로퍼티는 인스턴스 변수가 아닌 getter, setter 메서드의 이름에서 추출하지요.

아래 insert 문을 볼까요?

<insert id="insertAuthor">
  insert into Author (id, username, email)
  values (#{id}, #{userName}, #{email})
</insert>
프로퍼티 값 게터, 세터
id getId(), setId()
userName getUserName(), setUserName()
email getEmail(), setEmail()

Auto-boxing

select, insert, update, delete 모두 두번째 매개변수에 객체를 요구하지요.
두번째 매개변수에 기본형을 전달하게 되는 경우 어떻게 될까요?

int no = 1;
int cnt = sqlSession.delete("project.dao.delete", no);

이런경우 mybatis 는 int 형 을 Integer 객체로 Auto-boxing 해준답니다.
int no > Auto-boxing > new Integer(no)
마치 아래 예시처럼요.

int cnt = sqlSession.delete("project.dao.delete", new Integer(no));

위와 같이 랩퍼 클래스를 사용하는 경우에는 입력 변수의 이름(sql)은 어떤 이름을 사용해도 무방합니다.
#{idx}를 사용해도 #{no}를 사용해도 결과는 같지요.

<delete id="delete" parameterType="int">
delete from T1 where no=#{idx}
</delete>

Wrapper Class

기본형 객체형
byte java.lang.byte
short java.lang.Short
int java.lang.Integer
long java.lang.long
float java.lang.Float
boolean java.lang.Boolean
char java.lang.Character

Configuation


configuration 주요 자식 엘리먼트

엘리먼트 설명
properties 프로퍼티 파일 경로 설정
settings 프레임워크의 실행 환경을 설정
typeAliases 자바 클래스 이름(package)에 대한 별명 설정
typeHandlers 컬럼의 값을 자바 객체로,
자바 객체를 컬럼의 값으로 변화해 주는 클래스를 설정
environments Database 정보(transaction, datasource) 설정
mappers SQL 맵퍼 파일 경로 설정

Auto commit

자동 커밋으로 설정하고 싶으면 아래와 같이 설정하세요.
자동 커밋인 경우 트랜잭션은 사용 할 수 없습니다.

 sqlSession sqlSession = sqlSessionFactory.openSession(true);

Settings

매퍼설정에 관한 이야기를 해볼께요.

mapUnderscoreToCamelCase

myBatis 설정할 때 꼭 해주는 셋팅값입니다.
Mapper(SQL)와 VO간에 Mapping이 가능하지요.

대개 데이터베이스 칼럼은 대문자를 사용해서 명명하고 단어 사이사이에는 밑줄을 넣는다.
자바 프로퍼티는 대개 낙타표기법을 사용해서 명명한다.
이런 둘사이의 자동매핑을 가능하게 하기 위해서는 mapUnderscoreToCamelCase 를 true로 설정하자.
https://mybatis.org/mybatis-3/ko/sqlmap-xml.html#Auto-mapping
mapUnderscoreToCamelCase
전통적인 데이터베이스 칼럼명 형태인 A_COLUMN을 CamelCase형태의 자바 프로퍼티명 형태인 aColumn으로 자동으로 매핑하도록 함
default : false

mapUnderscoreToCamelCase 설정이 안된 경우 아래처럼 사용해야 하지요.

/sample/src/main/resources/mybatis/sqlmaps/sample/List.xml

<select id="selectSampleList" resultType="com.sample.domain.Sample">
    <include refid="com.sample.domain.common.pagingPrefix"/>
    SELECT
          default_no AS defaultNo
        , domain_nm AS domainNm
        , reg_date AS regDate
...

mapUnderscoreToCamelCase 설정을 true 로 적용해보아요.

설정파일 위치는 프로젝트 규칙에 따라 다르답니다.
/src/package/dao/mybatis-config.xml
/src/main/java/com/sample/commons/lib/mybatis/config-mapper.xml

<configuration>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true" />
    </settings>
...
</configuration>

어노테이션으로 설정한 경우예요.

@Configuration
public class WebDatabaseConf {
...
    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        config.setCallSettersOnNulls(true);
        config.setMapUnderscoreToCamelCase(true);
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setMapperLocations(ResourcePatternUtils.getResourcePatternResolver(resourceLoader)
                .getResources("classpath:/mapper/**/*.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage(EventDealWebApplication.class.getPackage().getName());
        sqlSessionFactoryBean.setConfiguration(config);

        return sqlSessionFactoryBean.getObject();
    }

깔끔해졌지요?
/sample/src/main/resources/mybatis/sqlmaps/sample/List.xml

<select id="selectSampleList" resultType="com.sample.domain.Sample">
    <include refid="com.sample.domain.common.pagingPrefix"/>
    SELECT
          default_no
          , domain_nm
          , reg_date

...

ResultMap

mapUnderscoreToCamelCase 설정이 안된 경우에 컬럼 별명을 일일히 지정해주었었지요.
다른 방법으로 resultMap 을 이용하는 방법도 있습니다.

<resultMap type="com.sample.domain.Sample" id="SampleResultMap">
  <id column="default_no" property="defaultNo"/>
  <result column="domain_nm"  property="domainNm"/>
  <result column="reg_date" property="regDate" javaType="java.sql.Date"/>
</resultMap>

<select id="selectSampleList" resultMap="SampleResultMap">
  SELECT
        default_no
      , domain_nm
      , reg_date
...

여기서 잠깐~

javaType=”java.sql.Date” 지정한 경우
myBatis 가 DB 에서 가져온 데이터를 넘겨줄때 java.sql.Date 객체로 변환해준답니다.
java.util.Date 타입으로 지정한 경우 외국 날짜 형식으로 가져오지요~

SQL Mapper

SQL 맵퍼 파일 작성하다 예치기 않는 오류가 발생할 때가 있지요.
그중 하나로 < 기호가 있겠습니다.

sql mapper 파일은 XML문서 이기 때문에 SAX Parser 가 <기호를 XML Parsing 해버린답니다.


이럴때 저는 2가지를 쓰는데요.

CDATA

먼저 CDATA 랍니다.

<select id="getBoardNoticeList" parameterType="boardListParam" resultType="boardNotice">
      SELECT
         board_id,
         <![CDATA[ IF(DATE_SUB(NOW(), INTERVAL 1 DAY) <= reg_dt, 'Y', 'N') AS new_icon]]>,
         subject,
         content,
         display,
         reg_dt,
         reg_by
      FROM board_notice
      <include refid="sqlBoardNoticeListWhere"/>
      ORDER BY
        board_id DESC
      LIMIT #{start}, #{fetchSize}
  </select>

대체식

두번째로는 대체식을 사용하기도 해요.

<sql id="sqlBoardNoticeListWhere">
  <where>
      <if test="auth lt 5">
          AND display = 'Y'
      </if>
기호 대체식
< lt
> gt
<= (=<) lte
>= (=>) gte

동적 SQL

동적 SQL 레퍼런스 에 자세한 설명이 있네요~ ^^

LOG

myBatis 에 로그 기능을 추가해볼까요?

로그 출력 구현체로 아래와 같이 있습니다.

설명
SLF4J SLF4J
LOG4J Log4j
LOG4J2 Log4j2
JDK_LOGGING JDK logging
COMMONS_LOGGING Apache Commons Logging
STDOUT_LOGGING 표준 출력 장치로 출력
NO_LOGGING 로그 출력 기능 사용 안함
클래스명(패키지명 포함) org.apache.ibatis.logging.Log 인터페이스의 구현


• SLF4J
• Apache Commons Logging
• Apache Log4j 1.x and 2.x
• JDK Logging API


위 대표적인 4가지 중에 log4j 로 설명할께요.

mybatis-config.xml 에 아래와 같이 추가하세요.

<configuration>
  <settings>
    <setting name="logImpl" value="LOG4J"/>
  </settings>
</configuration>

log4j 설정

설정파일을 살펴보아요.

/src/log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout

# MyBatis logging configuration...
log4j.logger.pacakege.dao=DEBUG

# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

rootLogger는 최상위 로거랍니다.
하위 로거들은 부모의 출력 등급을 상속받지요.

pacakege.dao 로거는 DEBUG 등급을 가지게 되지요.

로그출력등급 설명
FATAL application을 중지해야 할 심각한 오류
ERROR 오류가 발생했지만 application 계속 실행 할 수 있는 상태
WARN 잠재적인 위험을 안고 있는 상태
INFO application 주요 실행 정보
DEBUG application의 내부 실행 상황을 추적해 볼 수 있는 상세 정보
TRACE DEBUG 보다 더 상세한 정보

하위 등급은 상위 등급을 포함한답니다.
DEBUG 는 INFO, WARN, ERROR, FATAL 을 포함하지요.

log4j.rootLogger=ERROR, stdout

위에서 출력담당자(appender) 의 이름은 stdout 입니다.

stdout을 정의해야겠지요.

로그 출력 담당자 유형

log4j.appender.이름=출력담당자(fully qualified name)

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
  • org.apache.log4j.ConsoleAppender
    System.out 또는 System.err 로 로그를 출력한다.
    기본은 System.out , 즉 표준 출력장치인 모니터로 출력한다.

  • org.apache.log4j.FileAppender
    파일로 로그를 출력한다.

  • org.apache.log4j.net.SocketAppender
    원격의 로그 서버에 로그 정보를 담은 LoggingEvent 객체를 보낸다.

로그 출력 형식 정의

log4j.appender.이름.layout=출력형식 클래스(fully qualified name)

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
클래스 설명
org.apache.log4j.SimpleLayout 출력형식 ‘출력 등록 - 메시지’
org.apache.log4j.HTMLLayout HTML 테이블 형식
org.apache.log4j.PatternLayout 변환 패턴 형식
org.apache.log4j.xml.XMLLayout log4j.dtd 규칙에 따라 XML

java mybatis

Blog Logo

Weeping Willow


Published

Image

Willow's Blog

Welcome!

Back to Overview