LOGBack Configurator with JMX

Posted at 2014. 7. 20. 23:11 | Posted in Java+/Example
반응형

Introduce


요즘 logback 많이 쓸꺼다... "Reasons to prefer logback over log4j"에 보면 이제는 log4j 를 버리고 logback 을 써야하는지 이유를 영어로 설명 해놓았다. ㅅㅂ -_-;;


딴건 잘 모르것고 맘에 드는걸 보자면... 

Automatic reloading of configuration files (설정 파일 자동으로 다시 로드)

- Automatic removal of old log archives (이전 로그 아카이브 자동 삭제)

- Automatic compression of archived log files (아카이브 로그 파일 자동 압축)


서버를 재시작 하지 않고 logback.xml 파일을 수정하면 바로 설정이 적용 된다고 한다. ㅎㅎ


그리고 내가 관심 있게 본 하나는 JMX 가 지원된다. JConsole 로 테스트 해보니까 로그 레벨 변경이 바로 적용 되었다.


JMX 를 통해서 할 수 있는 짓거리는 아래와 같다. 몇개 안되고 메소드명 만으로도 이해가 될꺼다.


public interface JMXConfiguratorMBean {

  

  void reloadDefaultConfiguration() throws JoranException;

  

  void reloadByFileName(String fileName) throws JoranException, FileNotFoundException;

  

  void reloadByURL(URL url) throws JoranException;

  

  void setLoggerLevel(String loggerName, String levelStr);

  

  String getLoggerLevel(String loggerName);

  

  String getLoggerEffectiveLevel(String loggerName);


  List<String> getLoggerList();

  

  List<String> getStatuses();


}


오랜만에 웹 프로젝트나 하나 만들어 볼까.. 하고 이상한거 만들어 봤다 -_-




Example


Spring Framework 4.0.5

jQuery 1.11.1

jQuery UI 1.11.0

pqGrid 1.1.3


Logback ConfiguratorLogback Configurator 화면! 매우 간단한 화면이다 ㅎㅎ.


그리드에서 로그 레벨을 클릭하면 변경 할 수 있는 콤보 박스가 나와서 수정하면 바로 반영이 된다.


그리고 아래 Test 버튼을 클릭하면 서버 에 DEBUG/INFO/WARN/ERROR 4줄의 로그를 출력하게 된다.


2014-07-20 22:50:06,375 [http-bio-8080-exec-11] DEBUG c.a.c.LogController - this is debug level log.

2014-07-20 22:50:06,375 [http-bio-8080-exec-11] INFO  c.a.c.LogController - this is info level log.

2014-07-20 22:50:06,375 [http-bio-8080-exec-11] WARN  c.a.c.LogController - this is warn level log.

2014-07-20 22:50:06,375 [http-bio-8080-exec-11] ERROR c.a.c.LogController - this is error level log.


위의 "com.antop.controller.LogController" 의 레벨을 WARN으로 바꾸고 Test 하면? 당연 WARN/ERROR 두줄만 나오게 된다.


Spring MVC 의 URL 매핑은 아래와 같아. 이걸 그림으로 표현주는 툴이 있으면 좋겠네 ㅎㅎ


스프링 MVC URL 매핑 구성도


클래스 다이어그램은 아래와 같다. 어때요? 참 쉽죠~?


Class Diagram클래스 다이어그램. 이게 전부다. -_-/


끝이다.




Download


더이상의 자세한 설명은 생략한다 -_-;; 소스 보거라.


logback.jmx.war


↑ 소스 포함 WAR 파일


logback.jmx.zip


↑ 이클립스 프로젝트 파일 (메이븐)


반응형

'Java+ > Example' 카테고리의 다른 글

현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//

현지어로 언어명 보여주기

Posted at 2014. 2. 9. 15:34 | Posted in Java+/Example
반응형

Introduction


다국어를 지원하는 홈페이지에서 언어 선택하는 부분을 보면 대표하는 언어로 다국어명이 나오는 것을 볼 수 있다.



이거 따라해 보자 -_-;;




Locale 에서 getDisplayCountry() 뽑아낼 때 인자로 현재 Locale 을 넣어주면 자신의 Locale 언어 그대로 나온다.


for (Locale locale : Locale.getAvailableLocales()) { if (locale.getCountry().isEmpty() == false) { System.out.println("--"); System.out.println(locale.getCountry()); // 이거 System.out.println(locale.getDisplayCountry(locale)); } }


그리고 다국어 처리를 하려면 View 부분이 UTF-8 이어야 한다. 당연한건가? -_-;




Sample


locale.war


↑ 소스 포함 WAR 파일


locale.zip


↑ 메이븐 프로젝트 파일


Spring MVC 의 인터셉터 부분에서 Locale 부분을 처리하여 ModelAndView 에 넣어주는 방식으로 해봤다.



실행해 보면 아래와 같이 나온다.





반응형

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//
반응형

Intorduction


http://www.juso.go.kr


- 도로명 주소는 2014년부터 생활(법정)주소로 전면 사용됩니다.

- 도로명주소를 사용하지 않으면 은행, 카드, 보험 등 거래처의 우편물 수신에 불편함이 있을 수 있습니다.


이렇게 나와있다... Y2K[각주:1] 처럼 버그도 아니고, 그냥 체계가 바뀌는 거지만... 미리 준비하자. -_-/




데이터 입력


http://www.juso.go.kr


정보제공 게시판 쪽에 가보면 현재 11월 기준의 도로명 주소 데이터가 올라와 있다. 매월 말에 데이터가 갱신 되는듯 하다... ㅅㅂ



4개의 게시글 첨부파일을 다 다운로드 받은 후 txt 파일이 나올때까지 전부 압축을 해제하자.


 

도로명주소정보제공내용변경계획.hwp


파일을 보면 알겠지면 4개의 테이블 구조로 되어있다. 이것을 대충 ERD로 짜보면 아래와 같다. 네이밍 ㅅㅂ -_-/


road.txp


01


이제 텍스트 파일에서 테이블로 데이터를 넣어야 한다.


근데 이게 많은 양의 데이터라서 툴을 이용해서 넣기 번거로으므로 Java 로 만들어서 넣었다 -_-;; (템플릿 메소드 패턴 이용!)


 레이아웃

 건수

 설명

 도로명코드 (tbl_road)

356.792 

 시도 시군구 읍면동 도로명

 주소 (tbl_road_addr)

5,976,050 

 도로명코드 x 건물본번-건물부번

 지번 (tbl_addr_jibun)

7,943,977 

 주소에 해당하는 지번(옛날) 주소 (주소에 해당하는 지번 주소는 n 건)

 부가정보 (tbl_addr_info)

5,976,050 

 주소에 해당하는 부가정보 (우편번호가 여기에 있다)


zipcode.reader.zip


↑ 메이븐 프로젝트이다.




데이터 조회


안전행정부에서 도로명 주소를 검색해 가면서 분석한 결과 아래와 같은 공식(?)이 나왔다. 


 구분

 공식

 도로명 주소

 #도로명코드.시도명# [#도로명코드.시군구명#] #도로명코드.도로명# [#주소.지하여부#=="1"?"지하"]

 #주소.건물번호본번#[-#주소.건물번호부번#!="0"?#주소.건물번호부번#]

 [(#도로명코드.읍면동명#[, #부가정보.공동주택여부#=="1"?#부가정보.시군구건물명#])]

 도로명 주소 영문

 [#주소.지하여부#=="1"?"Jiha, "] #주소.건물번호본번#[-#주소.건물번호부번]

 [, #도로명코드.읍면동명영문#] [, #도로명코드.시군구명영문#], #도로명코드.시도명영문#

 지번 주소 (~2013 주소)

 #지번.시도명# #지번.시군구명# [#지번.법정읍면동명#] [#지번.법정동명#] [#지번.법정리명#]

 [#지번.산여부#=="1"?"산"] [#지번.지번본번#[-#지번.지번부번#]] [#부가정보.시군구건물명#]


음.. 어렵다.. -_-... 위 3개의 경우를 쿼리(오라클)로 뽑아보면 아래와 같다. 쿼리로 보는게 더 편할지도? ㅋㅋ


select /* 우편번호 */ t3.zipcode as zipcode /* 도로명 주소 */ , t1.sido_name || case when t1.sigun_name is not null then ' ' || t1.sigun_name end || ' ' || t1.road_name || decode(t2.base_yn, '1', ' ' || '지하') || ' ' || t2.bldg_no1 || case when t2.bldg_no2 != 0 then '-' || t2.bldg_no2 end || ' (' || t1.eup_name || case when t3.apt_yn = '1' and t3.sigun_bldg_name is not null then ', ' || t3.sigun_bldg_name end || ')' as address /* 도로명 주소 영문 */ , decode(t2.base_yn, '1', 'Jiha' || ' ') || t2.bldg_no1 || case when t2.bldg_no2 != 0 then '-' || t2.bldg_no2 end || ', ' || t1.road_name_eng || case when t1.sigun_name_eng is not null then ', ' || t1.sigun_name_eng end || ', ' || t1.sido_name_eng as address_eng /* 지번 주소 (~2013 주소) */ , t4.sido_name || case when nvl(t4.gugun_name, t1.sigun_name) is not null then ' ' || nvl(t4.gugun_name, t1.sigun_name) end || case when nvl(t4.eup_name, t1.eup_name) is not null then ' ' || nvl(t4.eup_name, t1.eup_name) end || case when t4.ri_name is not null then ' ' || t4.ri_name end || decode(t4.san_yn, '1', '산') || ' ' || t4.ji_no1 || case when t4.ji_no2 != 0 then '-' || t4.ji_no2 end || case when t3.sigun_bldg_name is not null then ' ' || t3.sigun_bldg_name end as address_jibun

/* 관리번호(PK) */ , t2.num from tbl_road t1 join tbl_road_addr t2 on t1.road_code = t2.road_code and t1.eup_seq = t2.eup_seq join tbl_road_info t3 on t2.num = t3.num join tbl_road_jibun t4 on t2.num = t4.num where t1.using_yn = '0' and t4.dele_yn = '1' and to_char(sysdate, 'YYYYMMDD') between nvl(t1.noti_date, '11110101') and nvl(t1.cancel_date, '99991231') and to_char(sysdate, 'YYYYMMDD') >= nvl(t2.noti_date, '11110101') ;


뭐 보면 대충 이렇게 나온다.





데이터 가공


데이터의 양이 너무 많다. 4개의 테이블만 덤프를 떠도 1.8GB 정도 한다.


내가 이 도로명주소 데이터를 뽑는 이유는 우편번호를 사용하기 위함이기 때문에 필요없는거 다 날리고 다른 테이블에 다시 넣어보자.


아래 ERD 는 도로명 주소를 표현할 수 있는 최소한의 데이터만 사용했을 때의 테이블이다.


zipcode_road.txp



아래는 insert select 문이다.


insert into zipcode_road (
	zipcode
    , road
    , sido
    , sigun
    , eup
    , under_yn
    , bldg_no1
    , bldg_no2
    , bldg
    , num
    , apply_date
)
select
	t3.zipcode
    , t1.road_name
	, t1.sido_name
    , t1.sigun_name
    , t1.eup_name
	, decode(t2.base_yn, '1', 'Y', 'N') as under_yn
    , t2.bldg_no1
    , decode(t2.bldg_no2, 0, null, t2.bldg_no2) as bldg_no2
    , case when t3.apt_yn = '1' and t3.sigun_bldg_name is not null then t3.sigun_bldg_name end as bldg
    , t2.num
    , to_char(sysdate, 'YYYYMMDD') as apply_date
from
	tbl_road t1
join
	tbl_road_addr t2
on
	t1.road_code = t2.road_code
    and t1.eup_seq = t2.eup_seq
join
	tbl_road_info t3
on
	t2.num = t3.num
where
	t1.using_yn = '0'
	and to_char(sysdate, 'YYYYMMDD') between nvl(t1.noti_date, '11110101') and nvl(t1.cancel_date, '99991231')
	and to_char(sysdate, 'YYYYMMDD') >= nvl(t2.noti_date, '11110101')
;


이렇게 넣으니 데이터는 5,975,824건에 650MB 정도 된다. 이래도 많다... 예전 우편번호는 5만건 정도밖에 안되는데 ㅜㅜ




Web Application


이 데이터를 기반으로 조회하는걸 한번 만들어 봤다.


http://antop.nerv.kr/zipcode


zipcode.war


zipcode.zip


↑ 메이븐 프로젝트


  1. 컴퓨터 프로그램 내에의 연도 표기 중에서 두 자리 숫자인 세기(century)를 생략한 연도 표현으로 인해 2000년에 컴퓨터 운영 체제 및 각종 업무용 응용 프로그램 등에서 2000년을 1900년과 동일하게 「00」년으로 인식함으로써 발생될 수 있는 각종 문제를 말한다. 또한 밀레니엄 버그를 Y2K 문제라고도 하는데, Y는 연도(year)의 첫 글자를 딴 것이고, K는 1000(kilo)에서 딴 것으로 2000년을 의미한다. [본문으로]
반응형

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//

JSTL Custom Tag using Spring Beans

Posted at 2013. 12. 1. 17:37 | Posted in Java+/Example
반응형

Introduction


JSTL[각주:1] 태그를 만들어 사용할 때에 스프링 빈을 가져다 쓰는 방법이다.




Using Spring Beans


너무나도 간단하다. 


일반적으로 TagSupport 클래스를 상속 받아서 구현 하면 되는데, 스프링 빈을 사용하고 싶으면 RequestContextAwareTag 클래스를 상속 받아서 구현하면 된다.



사용법은 아래와 같다.


import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.tags.RequestContextAwareTag;

public class XXXTag extends RequestContextAwareTag {

	@Override
	protected int doStartTagInternal() throws Exception {
		// WebApplicationContext 를 얻는다.
		WebApplicationContext ctx = getRequestContext().getWebApplicationContext();
		// 빈을 가져와서 처리
		return SKIP_BODY;
	}

}




Example Source


WAR 파일 (소스 포함)


webapp.war


프로젝트 파일 (메이븐 구조)


webapp.zip




  1. 자바 서버 페이지 표준 태그 라이브러리(JavaServer Pages Standard Tag Library, 약칭 JSTL)은 Java EE 기반의 웹 애플리케이션 개발 플랫폼을 위한 컴포넌트 모음이다. [본문으로]
반응형

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
Spring Message Source from Database  (1) 2013.03.03
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//

Spring Message Source from Database

Posted at 2013. 3. 3. 05:19 | Posted in Java+/Example
반응형

스프링을 이용한 다국어 처리를 데이터베이스를 이용해 어떻게 할까 이것저것 고민하다가 구현해 봤습니다.


개발 환경

 - Eclipse Indigo + Maven Integration (Sonatype, Inc.)

 - WebLogic 12c (Tomcat 7)


테이블은 아래와 같이 구성하였습니다.




특별한 것은 없고 여느 책에서나 나오는 스프링에서 MessageSource를 사용하여 다국어 메세지를 가져오는데 AbstractMessageSource 클래스를 상속 받아 따로 클래스를 만들어 봤습니다. (com.tistory.antop.framework.support.DatabaseMessageResource)




구동해 볼 수 있는 샘플을 만들어 봤습니다.


실제 메세지 리소스 부분만 구현하는 시간은 금방인데 하나의 완성품을 만들면서 더 많이 배우는군요~


Spring Framework 3.2.1

Mybatis 3.2 + Ehcache 2.6.3

Tiles 2.2.2 + Dynamic Tiles 1.2.1

HSQLDB 2.2.9

 

http://antop.nerv.kr/multi-lang/

 

데이터를 제외한 모든 부분을 다국어 처리 해봤습니다영어(기본), 한국어, 일어 데이터를 만들어 놨습니다. 구글 번역!


언어를 한글로 했을 때의 화면입니다.




아래는 언어를 영문으로 했을 때




자바스크립트 메세지 부분도 다국어 처리 해봤습니다. (/js/locale.js)






이클립스 프로젝트 파일입니다. 모든 파일 인코딩은 UTF-8 입니다.


multi-lang.zip


war 파일입니다. 16메가의 용량이 다 라이브러리군요... -_-/


multi-lang.zip.001

multi-lang.zip.002



반응형

'Java+ > Example' 카테고리의 다른 글

LOGBack Configurator with JMX  (0) 2014.07.20
현지어로 언어명 보여주기  (0) 2014.02.09
2014년 도로명 주소 사용에 따른 우편번호 준비  (2) 2013.12.22
JSTL Custom Tag using Spring Beans  (0) 2013.12.01
Infinite Routing DataSource  (1) 2013.01.13
Mybatis Type Handler  (1) 2012.09.30
Using AUTO_INCREMENT keys  (0) 2011.03.01
//

Infinite Routing DataSource

Posted at 2013. 1. 13. 18:54 | Posted in Java+/Example
반응형

스프링을 이용하여 몇개의 데이터소스를 정해놓고 라우팅을 하는 경우 AbstractRoutingDataSource를 이용할 수 있었습니다.


하지만 경우에 따라 접속해야하는 데이터베이스가 무한대일 경우?



그래서 이것저것 생각해 봤는데 -_-.... 


로그인시에 [고객 코드], [아이디], [비밀번호] 3가지를 입력 받습니다.


[고객코드]로 고객을 찾은 후 고객의 정보로 디비 정보를 만든 후 ThreadLocal에 저장합니다.


그 후 로그인 처리를 합니다.


로그인 시에 데이터소스를 사용할 때 ThreadLocal에 있는 디비 정보(DbInfo 객체)로 데이터소스를 만들거나 캐쉬에서 가져오게 합니다.


로그인 때에 처음 그 데이터베이스에 접속을 하게 되니까 RoutingDataSource 부분에서 데이터소스를 처음 만들게 될 것입니다.



로그인 처리 이후에는 인터셉터를 이용하여 세션에 있는 디비 정보를 ThreadLocal에 넣고 사용하면 되겠습니다.



이렇게 하면 똑같은 쿼리를 사용하면서 [코객코드]에 따라 다른 디비에 붙어서 사용할 수 있겠네요 ㅠ_ㅠ




Spring + Spring Security + Mybatis + SQLite 를 이용해서 샘플을 만들어 봤습니다.


아래와 같이 3개의 고객 스키마가 있습니다.



아래 테이블은 manager 스키마의 고객 관리 테이블 입니다.



비밀번호는 SALT 와의 조합으로 ARIA 알고리즘으로 저장됩니다.


SQLite는 파일 디비라서 직접적으로 아이디와 비밀번호는 사용하지 않습니다.




아래 테이블은 각각 고개 스키마에 있는 사용자 관리 테이블입니다. 암호는 MySQL 의 password() 알고리즘으로 저장됩니다.



계정은 각각 아래와 같이 들어 있습니다. 스키마명은 고객코드와 동일하게 했습니다.


cust001 (비밀번호: 12345 , welcome1!)



cust002 (비밀번호: jsnot)



cust003 (비밀번호: 11111)





핵심 소스 부분 몇 개 살펴보면...


package com.tistory.antop.service.common.impl;


public class LoginSoImpl implements LoginSo {


private AccountMapper accountMapper;


@Override

public Authentication login(String accountId, String username,

String password) {

// 고객 정보 조회

Account account = accountMapper.selectById(accountId);


// 없거나 사용 중지인 경우 예외

if (account == null || account.isUse() == false) {

throw new AccountNotFoundException();

}


// 디비 정보를 만듬

DbInfo dbInfo = new DbInfo();

dbInfo.setUsername(account.getId());

dbInfo.setEncPass(account.getEncPass());

dbInfo.setSalt(account.getSalt());


// ThreadLocal 에 저장

DbInfoContextHolder.set(dbInfo);


// Spring Security 인증

UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(

username, password);

token.setDetails(account);


Authentication auth = authenticationManager.authenticate(token);


return auth;

}

}



아래 소스는 o.s.jdbc.datasource.lookup.AbstraceRoutingDataSource 클래스를 상속받아 구현한 RoutingDataSource 입니다.


캐쉬 역할을 하는 객체는 동기화를 위해 Hashtable을 사용하였습니다.


package com.tistory.antop.framework.datasource;


abstract public class RoutingDataSource extends AbstractRoutingDataSource {


private String url;

private String driverClassName;

private PasswordDecoder passwordDecoder;


private Map<DbInfo, DataSource> dataSources = new Hashtable<DbInfo, DataSource>();


@Override

protected DbInfo determineCurrentLookupKey() {

return DbInfoContextHolder.get();

}


@Override

protected DataSource determineTargetDataSource() {


DbInfo dbInfo = determineCurrentLookupKey();


if (dbInfo == null) {

throw new RoutingDataSourceException("데이터베이스 정보가 없습니다.");

}


try {

// 데이터소스가 없으면 생성

if (!contains(dbInfo)) {

// 비밀번호 복호화

String password = passwordDecoder.decodePassword(

dbInfo.getEncPass(), dbInfo.getSalt());

DataSource ds = createDataSource(dbInfo.getUsername(), password);

add(dbInfo, ds);

}


// 데이터소스 리턴

return get(dbInfo);

} catch (Exception e) {

throw new RoutingDataSourceException(e);

}


}


// 데이터 소스를 만드는 부분은 상속받은 객체가 함

abstract public DataSource createDataSource(String username, String password);

)




WAR 파일


바로 배치 가능한 WAR 파일입니다.


routing_datasource.zip.001


routing_datasource.zip.002



소스 ZIP 파일


이클립스 메이븐 구조의 프로젝트 소스입니다.


routing_datasource.zip



개발 테스트 : 톰켓 7, 웹로직 12c


배포 테스트 : 웹로직 12c




※ 질문!!!


웹로직에서 사용할 때 컨텍스트루트 다음에 "/" 가 붙느냐 안붙느냐에 따라서 스프링 시큐리티 필터를 타고 안타고 다른 결과가 나옵니다...


 URL  정상 처리 여부

 http://localhost:7001/routing_datasource/

 정상 처리 됨
 http://localhost:7001/routing_datasource

 스프링 시큐리티 필터를 타지 못하고 그냥 넘어가서 에러


톰켓에서는 "/" 상관없이 정상적으로 됨.... 이 부분 해결법 아시는분.. ㅠㅠ



반응형
//

Mybatis Type Handler

Posted at 2012. 9. 30. 12:49 | Posted in Java+/Example
반응형

마이바티스를 이용하여 자바 객체와 데이터베이스 테이블을 매핑할 때 서로 완전히 다른 타입의 것들을 사용할 때가 있습니다.

 

그 예로 대표적인 예가 자바의 Boolean 과 데이터베이스의 플래그 문자입니다.

 

 

오라클 같은경우 boolean 타입이 없기 때문에 보통 CHAR(1) 잡고 Y/N 값을 많이 사용합니다.

 

다른 경우는 1 or 0 을 사용하여 참/거짓, 사용/미사용, 차단/허용 같은 on/off 플래그 값을 사용합니다.

 

자바 

데이터베이스 

boolean

CHAR(1)

INTEGER  

참 / 사용

true

Y

 1 

거짓 / 미사용

false

N

 0 

 

org.apache.ibatis.type.TypeHandler 인터페이스를 이용해서 서로 다른 타입을 연결할 수 있습니다.

 

package com.tistory.antop.mybatis.handler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.log4j.Logger;

public class StringYnTypeHandler implements TypeHandler<Boolean> {

    private Logger logger = Logger.getLogger(getClass());

    public Boolean getResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);

        return parseBoolean(s);
    }

    public Boolean getResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);

        return parseBoolean(s);
    }

    public Boolean getResult(CallableStatement cs, int columnIndex)
        throws SQLException {
        String s = cs.getString(columnIndex);

        return parseBoolean(s);
    }

    public void setParameter(PreparedStatement ps, int i, Boolean bool,
        JdbcType jdbcType) throws SQLException {

        ps.setString(i, parseString(bool));
    }

    // "Y" or "N" -> true or false
    private boolean parseBoolean(String s) {
        boolean bool = false;

        if (s == null) {
            return false;
        }

        s = s.trim().toUpperCase();

        if (s.length() == 0) {
            return false;
        }

        // allow "Y" or "N"
        if ("Y".equals(s) == false && "N".equals(s) == false) {
            throw new PersistenceException("value must be \"Y\" or \"N\".");
        }

        bool = "Y".equals(s);

        if (logger.isDebugEnabled()) {
            logger.debug("\"" + s + "\" -> " + bool);
        }

        return bool;
    }

    // true or false -> "Y" or "N"
    private String parseString(Boolean bool) {
        String s = (bool != null && bool == true) ? "Y" : "N";

        if (logger.isDebugEnabled()) {
            logger.debug(bool + " -> " + "\"" + s + "\"");
        }

        return s;
    }
}

 

이 클래스를 이용하면 자바에서는 boolean 타입을 사용하면 데이터베이스에서는 Y or N 값이 들어가게 됩니다.

 

mybatis.typehandler.zip

 

위 샘플은 메이븐 프로젝트 구조입니다.

 

샘플을 만드는 도중에 sqlite3 는 이상한게 TIMESTAMP 타입을 java.util.Date 클래스와 매핑 하면 들어갈 때는 그대로 들어가는데 조회할 때는 java.util.String 이 나오는군요....

 

그래서 TIMESTAMP 에 대한 핸들러 하나 더 만들었습니다.

 

테스트는 com.tistory.antop.mybatis.TypeHandlerTest 클래스 입니다. JUnit 입니다.

 

실행시 Run Configurations 중 VM 옵션에 이래와 같이 "-Dlog4j.configuration=config/properties/log4j.properties" 를 추가해 줘야 로그를 볼 수 있습니다.

 

 

 

반응형
//

Using AUTO_INCREMENT keys

Posted at 2011. 3. 1. 16:02 | Posted in Java+/Example
반응형

http://dev.mysql.com/tech-resources/articles/autoincrement-with-connectorj.html

http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/jdbc.html

MySQL 에는 AUTO_INCREMENT(이하 AI)라는 것이 있습니다. 오라클 디비의 시퀀스와 같이 자동으로 값이 증가하게 됩니다.

AI를 적용하려는 칼럼은 기본키K(Primary Key)여야 합니다.



Before JDBC API 3.0

JDBC API 3.0 이전에서는 INSERT 이후에 "select last_insert_id()" 쿼리를 날려서 추가된 AI 값을 가져와야 합니다.

Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; // auto increment value int generatedKey = -1; try { conn = dataSource.getConnection(); // query String sql = "insert into sample_user (name, age) values (?, ?)"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, user.getName()); pstmt.setInt(2, user.getAge()); // execute query pstmt.executeUpdate(); // query sql = "select last_insert_id()"; pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); if(rs.next()) { // auto increment value generatedKey = rs.getInt(1); } return generatedKey; } catch (Exception e) { throw e; } finally { // release rs, pstmt, conn }



After JDBC API 3.0

3.0 부터서는 "getGeneratedKeys()" 메소드를 이용하면 됩니다.

Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; int generatedKey = -1; try { conn = dataSource.getConnection(); // query String sql = "insert into sample_user (name, age) values (?, ?)"; pstmt = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); pstmt.setString(1, user.getName()); pstmt.setInt(2, user.getAge()); // execute query pstmt.executeUpdate(); rs = pstmt.getGeneratedKeys(); if (rs.next()) { generatedKey = rs.getInt(1); } return generatedKey; } catch (Exception e) { throw e; } finally { // release rs, pstmt, conn; }



Spring + JdbcDaoSupport

스프링과 연동하여 사용할 때에는 KeyHolder 인터페이스를 이용하면 됩니다.

@Override
public int create(User user) throws Exception {
	// query
	String sql = "insert into sample_user (name, age) values (:name, :age)";
	// parameter
	SqlParameterSource paramSource = new BeanPropertySqlParameterSource(user);
	// key holder
	KeyHolder generatedKeyHolder = new GeneratedKeyHolder();
	// execute query
	getNamedParameterJdbcTemplate().update(sql, paramSource, generatedKeyHolder);
	// auto increment
	return generatedKeyHolder.getKey().intValue();
}
※ KeyHolder 기능을 사용하려면 JDBC 드라이버가 JDBC 3.0을 지원해야 합니다.



샘플 소스입니다. (이클립스 Java Project 폴더 통째로 압축했씁니다. Buil Path 설정 해줘야 할지도...)

Spring 3.0 으로 되어있습니다. 간단한 DI만 이용...

kr/nerv/antop/config/spring.xml 파일에서 디비 설정 해줘야합니다.

테스트 클래스는 app.TestUserDao 입니다.

sample_mysql_ai.zip


반응형
//