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)에 별명 지정해서 긴 이름대신 짧고 명료하게 사용할 수 도 있지요.
핵심 컴포넌트
마이바티스를 사용하기 위한 기본적인 자바 인터페이스는 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. |
---|---|
객체 하나만 반환 | |
Value Object 반환 | |
<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() |
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 |