출처 : 오마이뉴스
김영우 의원 "조작된 것 같다"... 동영상 유포자 수사 의뢰하기로
요즘 국개는 완전 개그 조작된거 같다?
동영상 유포자 수사 의뢰하기로 ㅋㅋㅋ
아~~~ 졸라 무서워
개가 웃겠다~~
출처 : 오마이뉴스
김영우 의원 "조작된 것 같다"... 동영상 유포자 수사 의뢰하기로
요즘 국개는 완전 개그 조작된거 같다?
동영상 유포자 수사 의뢰하기로 ㅋㅋㅋ
아~~~ 졸라 무서워
개가 웃겠다~~
램 512MB에서도 빠릿한 속도의 보장 ㅎㅎㅎ
무지 기대됨 이미 손에 있지만 귀차니즘으로 인해 -_-
VMWare로 설치후 가동중~
무료 백신중에 하나인 어베스트 홈에디션 Ver 4.8이다
세계 10위권에 드는 유명한 백신 프로그램이다..
원래 프로페셔널 버전은 상용이지만 홈에디션은 무료이다..
물론 기업에서 쓴다면 구매해야함...
프로페셔널 버전과 별 차이 없이 업데이트등을 제공하니 나름 쓸만하다
다운로드는 아래 링크를 클릭하면 됩니다.
avast! 홈 에디션 다운로드
avast! 4 Home - 한국어 버전 | |
avast! 4 Home - 영어 버전 | |
avast! 4 Home - Chinese (간체) | |
avast! 4 Home - Chinese (번체) | |
avast! 4 Home - Japanese |
무료 라이센스 등록 을 통해 1년 단위로 무료로 라이센스를
발급받아서 사용하면 됩니다. ㅎㅎ
이클립스가 이번에 또 업그레이드 됐다~~~
버전은 3.5 기존 버전보다 약간 더 성능 향상이 된듯
빠릿빠릿한게 맘에 든다 ㅋㅋ
3.3 - 유로파 (목성의 위성)
3.4 - 가니메데 (목성의 위성)
3.5 - 갈릴레오
다음 버전의 코드 네임이 멀까 사뭇 기대된다 ㅋㅋ
멋지구리한 홈페이지~~~
펼쳐두기..
Highlights include Mac Cocoa support, Domain Specific Language modeling and updates to Equinox; multi-language support immediately available
OTTAWA, CANADA – June 24, 2009 – For the sixth year in a row, the Eclipse community has delivered its annual release train on its scheduled date. Galileo, the 2009 release train, is the largest ever release from the Eclipse community, comprising 33 projects and over 24 million lines of code. Over 380 committers from 44 different organizations participated to make this release possible.
Each year the Eclipse community coordinates an annual release of projects during the last week of June. The coordinated release makes it easier for Eclipse users and adopters to take advantage of the innovations and new features created by the different Eclipse projects. Millions of Eclipse users and thousands of organizations that build software on top of Eclipse can now upgrade to this release.
The new features in the Galileo release reflect three important trends in the Eclipse community: 1) Expanding adoption of Eclipse in the enterprise, 2) innovation of Eclipse modeling technology and 3) advancement of EclipseRT runtime technology. Each project has published “new and noteworthy” documentation for their specific release. Overall, highlights from the Galileo release include:
Expanded Enterprise Adoption
Adoption of Eclipse in the enterprise continues to grow. New features in Galileo help expand the use of Eclipse by enterprise developers, including:
Advancement of EclipseRT Runtime Technology
EclipseRT is the set of Eclipse technologies that provide OSGi-based frameworks and runtimes useful in building software systems. The Galileo release includes a dedicated category of EclipseRT components including elements from Equinox, RAP, RCP, Riena, BIRT, Swordfish, EclipseLink, ECF and EMF. Notable feature updates that advance the EclipseRT technology stack include:
Innovation in Eclipse Modeling Technology
The Eclipse Modeling community continues to create new innovative technology for model-based development frameworks, tools and standards. Key new innovations in Galileo include:
For the first time ever, language translations of the Eclipse Platform project v3.5 will be available on the release date. Language packs for Simplified Chinese, Traditional Chinese, French, German, Japanese and Korean will be available immediately, and other languages and projects will be available when completed. In previous years, Eclipse language translations have taken two to three months to become available. This simultaneous release has been made possible by the Eclipse Babel project and the community of individuals and organizations that have provided translations.
“The release train continues to be a great achievement of the Eclipse community,” explained Mike Milinkovich, Executive Director of the Eclipse Foundation. “Galileo demonstrates that large distributed software development can be done on a predictable schedule. This predictability makes it possible for our user and adopter community to quickly adopt new releases from Eclipse.”
The projects in the Galileo release train are now available for download at www.eclipse.org/downloads. More information about Galileo is available at www.eclipse.org/galileo.
About the Eclipse Foundation
Eclipse is an open source community, whose projects are focused on building an open development platform comprised of extensible frameworks, tools and runtimes for building, deploying and managing software across the lifecycle. A large, vibrant ecosystem of major technology vendors, innovative start-ups, universities and research institutions and individuals extend, complement and support the Eclipse Platform.
The Eclipse Foundation is a not-for-profit, member supported corporation that hosts the Eclipse projects. Full details of Eclipse and the Eclipse Foundation are available at www.eclipse.org.
All company/product names and service marks may be trademarks or registered trademarks of their respective companies.
Press Contact:
Chantal Yang
Page One PR for Eclipse Foundation
415-875-7494
eclipse@pageonepr.com
다운로드는 아래 링크를 클릭해 주세요.
특정 파일을 읽어서 처리해야 할 경우 개발 환경과 운영환경이 다른 경우에는
파일이 위치한 정보를 가진 설정 파일을 각각 유지하여 관리한다던가 하는 등의
불편함이 있다...
하지만 ServletConfig객체 (JSP에서는 application)를 사용하면 쉽게 Context에
위치한 파일등을 다룰수가 있다.
URL url = conf.getServletContext().getResource("/img/test.jpg");
URLConnection conn = url.openConnection();
conn.setUseCaches(false);
InputStream is = conn.getInputStream();
위의 내용은 Context의 /img/test.jpg파일을 읽어서 InputStream으로 반환하는 샘플 코드이다.
만약 경로만 준다면 해당 폴더에 있는 파일의 리스트를 가지고 올수도 있다.
응용하기에 따라 웹어플리케이션에서 서버의 환경이 변화 되더라도 쉽게 마이그레이션이
가능한 코드를 작성 할 수있다는게 장점~
정말 간만에 너무 재미있게 본영화 ㅋㅋㅋ
스포는 생략~~~
엔딩 크레딧 올라가기전까지 마음껏 웃게해준 영화!!!
대박 나길 너무 재미있습니다. ㅋㅋㅋㅋ
특히 트랜스포머보다 재미있을거란 말 정말이더군요 ㅋㅋㅋ
강추!!! 올해 아직까지 본 영화중에 최고로 재미있었음~~
iBATIS의 로깅기능으로 log4j를 주로 사용하는데...
일반적으로 로그 파일을 FIle로 저장하게 되는데 가끔... 로깅 정보를 DB로 저장을 원하는곳이
있다..
이때 사용하는것이 JDBCAppender이다... 첨부 파일 다운로드 하면된다.
JDBCAppender는 아래와 같으 Log4j에서 설정해주면 된다.
log4j.appender.dblog=org.apache.log4j.jdbcplus.JDBCAppender
log4j.appender.dblog.url=jdbc:oracle:thin:@접속정보:1521:SID
log4j.appender.dblog.dbclass=oracle.jdbc.driver.OracleDriver
log4j.appender.dblog.username=계정log4j.appender.dblog.password=암호
log4j.appender.dblog.sql=INSERT INTO APP_LOG( SYSTEMNAME, LOGDATE, LOGLEVEL, MDC1,MDC2, MESSAGE, TROWABLE ) values ('MCSMGR',TIMESTAMP '@TIMESTAMP@','@PRIO@','@MDC:APP@','@NDC@','@MSG@','@THROWABLE@')
log4j.appender.dblog.layout=org.apache.log4j.PatternLayout
log4j.appender.dblog.layout.ConversionPattern=%m
log4j.appender.dblog.buffer=1
log4j.appender.dblog.commit=true
log4j.appender.dblog.quoteReplace=true
log4j.appender.dblog.throwableMaxChars=3000
위는 오라클 기준임... 그리고 appender를 loging되게하는 설정이 필요함 각자 알아서..
그리고 아래와 같이 테이블을 작성해 주면 된다.
CREATE TABLE "APP_LOG" (
"SYSTEMNAME" VARCHAR2(100)
,"LOGDATE" DATE DEFAULT SYSDATE
,"LOGLEVEL" VARCHAR2(100)
,"MDC1" VARCHAR2(100)
,"MDC2" VARCHAR2(100)
,"MESSAGE" VARCHAR2(4000)
,"TROWABLE" VARCHAR2(4000)
)
그러면 DB의 테이블로 로그가 저장이 된다.
그리고 로깅시에 외부 설정 정보를 넣을려면... 얘를 들어 로그인한 사용자등등...
MDC.put("APP", "테스트");
db.queryForList("SAMPLE.SELECT");
MDC.remove("APP");
MDC 또는 NDC를 설정하여 사용하면 된다. 자세한것은 아래 URL을 참고...
http://www.onjava.com/pub/a/onjava/2002/08/07/log4j.html?page=3
http://www.mail-archive.com/log4j-user@logging.apache.org/msg06998.html
2004년인가 2003년인가 들어 놨던 비과세 저축
그때 그냥 아는 형님 따라 갔다가 만들어놓은 통장인데
나름 개념 통장임 ㅋㅋㅋ
30년만기인가? 아마 그럴거고...
이게 장기주택마련상품이라... 년 300만원까지 소득공제~~~
그리고 중요한건 이넘이 연복리가 적용된다는거~~~
가입할때 금리는 안습이었는데 요즘 금리는 4.5%였나
뭐 나름 괜춘한 통장 ㅎㅎㅎ
사실 연복리인지 몰랐는데 ㅡ.ㅡ;; 돈 안넣은지 2년가까이 되는데 원금이 계속 늘어나길래 ㅋㅋ
빨간색 테두리가 연복리 적용된거 ㅋㅋㅋ
요즘도 이런 상품 있을려나? 하나더 해두고 싶은데 ㅡ.ㅡ;;
대략 네이버에서 이사 완료~~~
대부분의 자료는 다 옴겨 왔는데 누락된건 나중에 찾아서 올려야 할듯 ㅡ.ㅡ
심플하니 좋구나~~~~~
구글로 옴겨왔는데두 명박이의 손길이 닿을지 두고 볼일 ㅋㅋㅋ
정말 간단히 만들어본 ANT Build.xml파일
대략 적인 기능은 java컴파일하여 ftp 업로드
HTML/JSP파일 ftp업로드
주석으로 막아 놓은 부분은 jar아카이브로 묶어서 처리 ㅎㅎ
<?xml version="1.0" encoding="euc-kr" ?>
<!-- 프로젝트 이름 : 이 이름으로 jar파일이 생성된다. -->
<project name="대한통운" basedir=".">
<property name="project.name" value="${ant.project.name}"/>
<property name="project.version" value="1.0"/>
<property name="user.name" value="user"/>
<property name="apps.name" value="${ant.project.name}"/>
<!--jar 압축 파일 이름 -->
<property name="jars.name" value="${apps.name}.jar"/>
<!--소스가 있는 기준 폴더, Eclipse Project Root Folder-->
<property name="src.dir" value="./src"/>
<!--컴파일하여 class를 저장할 폴더-->
<property name="build.dir" value="./build"/>
<!--jar압축 파일이 저장될 폴더, 프로젝트 루트 디렉토리-->
<property name="jar.dir" value="${basedir}"/><!--jar압축 파일 백업본이 저장될 폴더-->
<property name="backup.dir" value="backup"/>
<!-- Deploy폴더 -->
<property name="deploy.lib.dir" value="./WEB-INF/lib"/>
<!-- 외부 라이브러리 -->
<path id="ext.lib">
<fileset dir="${deploy.lib.dir}" includes="*.jar" />
</path>
<!-- 자바소스 컴파일 -->
<target name="compile">
<!-- BUILD 생성 -->
<mkdir dir="${build.dir}" />
<!-- XML/Properties복사 -->
<copy todir="${build.dir}">
<fileset dir="${src.dir}"
includes="**/*.xml, *.properties" />
</copy>
<javac srcdir= "${src.dir}"
destdir="${build.dir}"
excludes="**/*.class"
classpathref="ext.lib"
debug="on"/>
</target>
<!-- 133서버에 클래스 반영 -->
<target name="클래스반영[1]" depends="compile">
<ftp server="FTP서버IP" port="포트"
remotedir="FTP로 파일 전송할 경로"
userid="계정"
password="암호" verbose="yes" depends="no" timediffauto="yes">
<fileset dir="${build.dir}">
<include name="**"/>
</fileset>
</ftp>
<delete dir="${build.dir}" />
</target>
<!-- 134서버에 클래스 반영 -->
<target name="클래스반영[2]" depends="compile">
<ftp server="FTP서버IP" port="포트"
remotedir="FTP로 파일 전송할 경로"
userid="계정"
password="암호" verbose="yes" depends="no" timediffauto="yes">
<fileset dir="${build.dir}">
<include name="**"/>
</fileset>
</ftp>
<delete dir="${build.dir}" />
</target>
<!--jar파일 압축 타겟-->
<!-- target name="jars" depends="compile">
<jar destfile="${jars.name}"
basedir="${build.dir}"
includes="**"
excludes="doc/**, **/SqlMapConfig.xml"
update="true"
compress="false"
index="true"
>
</jar>
</target --><!--jar파일 deploy-->
<!-- target name="start.copy" depends="jars">
<mkdir dir="${backup.dir}"/>
<echo message="Application Name:${jars.name}"/>
<echo message="Application Name:${jar.dir}/${jars.name}"/>
<copy file="${jar.dir}/${jars.name}" todir="${deploy.lib.dir}" overwrite="true"/>
<delete file="${jar.dir}/${jars.name}"/>
</target -->
<!-- HTML/GFM등을 반영 -->
<target name="HTML/JSP반영[1]" >
<ftp server="FTP서버IP" port="21"
remotedir="FTP로 파일 전송할 경로"
userid="아이디"
password="암호" verbose="yes" depends="no" timediffauto="yes">
<fileset dir="./">
<include name="**/a.gfm"/>
</fileset>
</ftp>
</target><!-- HTML/GFM등을 반영 -->
<target name="HTML/JSP반영[2]" >
<ftp server="FTP서버IP" port="21"
remotedir="FTP로 파일 전송할 경로"
userid="아이디"
password="암호" verbose="yes" depends="no" timediffauto="yes">
<fileset dir="./">
<include name="**/a.gfm"/>
</fileset>
</ftp>
</target></project>
#### [ANT] ftp task의 문제점
http://okjsp.pe.kr/bbs?act=VIEW&bbs=bbs4&seq=38814&pg=0&keyfield=subject&keyword=&pact=&password=
#### 참고 사이트
http://wiki.javajigi.net/pages/viewpage.action?pageId=179
#### Ant에서 SFTP task 활용하기
http://shinnara.tistory.com/127
Jeus라는넘은 당췌 알수가 없다...
왜? 다른거냐 여러 WAS를 다 사용해 보았지만 JEUS만큼 짜증을 유발시키는 WAS는 WebSphere빼고는
없을듯;;;
아래와 같은 코드가 JEUS에서는 비정상적인 동작을 한다...
<%@page contentType="text/html;charset=euc-kr" %>
<%
request.setCharacterEncoding("utf-8");
System.out.println(request.getParameter("TEST"));
request.setCharacterEncoding("euc-kr");
System.out.println(request.getParameter("TEST"));
%>
위와 같이 코딩한 jsp에 TEST라는 파라미터로 한글로 홍길동을 넘기면 결과가...
??浿
??浿
나오고 다른 WAS(웹로직, 톰캣등등)은....
??浿
홍길동
으로 정상적으로 나온다..
즉, JEUS 6에서는 setCharacterEncoding이 최초 설정된 이후에는 두번다시 적용이 안되는 문제점이!!!
상황에 따라 변경을 해야되는에 어찌하란 말인가 ??
IE8이 얼마전에 패치되었는데 다른건 다 좋은데...
소스 보기를 했더니 아래와 같이 전용 뷰어가 뜬다 아쉬운 점은 편집이 안된다!!!
예전처럼 노트 패드에서 열리게 할려면 아래와 같이 레지스트리를 수정해 주면 됩니다.
HKEY_LOCAL_MACHINE > SOFTWARE > Microsoft > Internet Explorer 에 가보면 아래와 같이
View Source Editor라는 부분이 있습니다.
하위에 보면 Editor Name부분이 있는데 기본값을 notepad.exe로 변경하면 끝
이클립스를 사용하다가 자바스크립트 파일을 열어보면 한글이 깨져 보일때가 종종있다..
이 경우는 이클립스의 설정이 안되어 그런 부분인데
지금 부터 따라하면 깨지지 않은 한글을 볼수가 있다 ㅎㅎ
먼저 이클립스 실행후에
상단 메뉴에서 Window > Preferences를 눌러 주면 아래와 같은 화면이 나타난다.
좌측 트리에서 General > Content Types를 선택해 주고
오른쪽의 Content types의 Text부분을 확장하면 위와 같은 리스트가 나타나는데 그중에서
JavaScript를 선택후에 Default encoding을 euc-kr로 변경해 준다음 update를 누르면
끝~~~ 쉽죠?
iBATIS SQLMaps(이하 아이바티스)는 이미 국내외 많은 개발자들이 사용하고 있는 퍼시스턴스 계층의 프레임워크이다. 실제로 국내에서 가장 크다는 포털 사이트인 네이버와 다음도 아이바티스를 사용하고 있으며, 다른 업체들도 아이바티스를 그대로 사용하거나 약간 변형시킨 형태로 사용하고 있는 것으로 알고 있다. 특집 3부에서는 아이바티스의 특징과 활용법에 대해 알아본다.
과거 ORM의 대표인 하이버네이트와 데이터 매퍼인 아이바티스 간에 어느 프레임워크가 더 좋은가에 대한 논쟁도 있었을 만큼 많은 자바 개발자들은 데이터베이스에 관련된 ORM 프레임워크에 대한 관심이 크다.
이런 논쟁 가운데 아이바티스는 ORM이 아니기 때문에 논쟁의 대상이 될 수 없다는 의견도 있었다. 사용 목적이라는 관점에서 본다면 하이버네이트와 아이바티스는 논쟁의 대상이 될 수 있으나 ORM이냐 아니냐를 가름하는 관점에서 본다면, 역시 아이바티스는 ORM이 아니라고 할 수 있다.
ORM과 데이터 매핑 |
기능에 따른 사용형태 |
<그림 1> ER 다이어그램 |
일반적인 CRUD 작업
데이터 매퍼인 아이바티스는 SQL ‘구문’을 자바 객체로 매핑하기 때문에 기본적으로 정적인 CRUD 작업에 가장 최적화되어 있다고 할 수 있다. 물론 데이터베이스 함수 및 프로시저, 동적 SQL 처리에도 사용할 수는 있지만 약간의 제약이 있게 마련이다. 여기서는 SQL 구문의 재사용을 위해
쿼리문을 일종의 특정 id값을 가지는 xml에 저장하고 그 id를 기준으로 호출해서 사용하는 게 아이바티스의 기본 사용법이다. 이 파일을 이해하는데 별무리가 없으리라 생각된다. 그러면 이 SQL 구문들을 어떻게 호출해서 사용하면 좋을까? 자바 코드를 보자.
자바 코드에서는 각각의 SQL 구문을 호출하는 형태를 <리스트 2>와 같이 취하고 있다. 사전에 아이바티스 설정 파일을 통해 관련 정보를 얻는 부분이 static 구문 안에 처리가 되어 있다. 이 소스는 아이바티스를 처리하는 결과를 보기 위해 log4j 설정과 아이바티스 설정 정보를 읽어오는 두 가지로 구성이 되어 있다.
log4j.xml 파일과 아이바티스 설정 파일의 위치를 클래스 패스 기준으로 가져오도록 했다. SqlMapClient가 제공하는 각각의 메소드에 SQLMap 파일에 정의된 SQL 구문의 id와 인자로 넣어줄 객체를 정의하는 형태를 취한다. 짐작하겠지만 SQL 구문의 id와 일치하는 SQL 구문에 해당 객체의 값이 인자로 전달되어서 내부적으로 java.sql의 PreparedStatement 객체 생성 후 일반 JDBC처럼 처리된다.
<리스트 2> CRUD 작업을 위한 자바 코드
private static SqlMapClient sqlMapper;
static {
try {
// iBATIS SQLMaps setting
Reader reader = Resources.getResourceAsReader("kr/or/openframework/dao/ibatis/SqlMapConfig.xml");
sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader);
reader.close()
} catch (IOException e) {
throw new RuntimeException("Something bad happened while building the SqlMapClient instance." + e, e);
}
}
public static Account selectAccountById(int id) throws SQLException {
return (Account) sqlMapper.queryForObject("selectAccountById", id);
}
public static String insertAccount(Account account) throws SQLException {
sqlMapper.insert("insertAccount", account);
return "SUCCESS";
}
프로시저
예제를 위해 필자는 다음처럼 간단한 프로시저를 생성했다. 여기서는 IN 타입의 파라미터만을 사용했지만 IN과 OUT, INOUT 타입 모두 지원한다. 여기서 사용된 프로시저는 인자로 들어온 값을 id로 해서 샘플용 데이터를 생성한다.
<리스트 3> 프로시저 생성 구문
drop procedure if exists procedurein;
delimiter // ;
create procedure procedurein (in p_param int)
begin
insert into account (
acc_id,
acc_first_name,
acc_last_name,
acc_email
) values (
p_param, "procedure_test", "procedure_test", "test@test.com"
);
end;
//
delimiter ; //
프로시저 호출은 일반적으로 콘솔 창에서 실행하는 것처럼 하고 { }로 감싸주면 된다. 처리 자체는 오히려 CRUD보다 간단하다고 할 수 있으나 직접 해보면 쉽지 않다는 것을 알 수 있다.
<리스트 4> 프로시저 호출을 위한 SQLMap 파일
<procedure id="inProcedure" parameterClass= "java.lang.Integer">
{ call procedurein(#val#) }
</procedure>
<리스트 5> 프로시저 호출을 위한 자바 코드
public static String callInTypeProcedure(int id) throws SQLException {
sqlMapper.update("inProcedure", new Integer(id));
return "SUCCESS";
}
프로시저의 처리는 아이바티스 홈페이지나 메일링 리스트를 통해 끊임없이 제기되는 문제 중에 하나이다. 개발자와 데이터베이스마다 구현 방식이 조금씩 다른 탓도 있겠지만 여러 가지 눈에 띄는 어려움이 존재하기 때문에 아이바티스 문서를 보면 꼭 표준 형태의 프로시저를 사용하도록 권장하고 있다.
필자의 경우에도 오라클(Oracle)에서는 정상적으로 프로시저 형태의 샘플 코드를 테스트 했으나 MySQL로 작성하면서 이런저런 문제에 봉착했었다. 실제 프로젝트에서 아이바티스를 사용하여 프로시저를 처리할 때는 프로시저 사용 패턴을 정의하고 사전에 정상적으로 처리가 되는지 테스트 해보길 권하고 싶다.
<표 1> 프로시저 처리시 사용할 메소드에 대한 권고사항 |
앞의 예제는 IN 파리미터에 값을 전달하고 내부적으로 처리한 후에 어떠한 데이터도 반환하지 않기 때문에 update() 메소드가 사용되었다(프로시저 처리 시 결과 세트를 반환하고 데이터를 업데이트 할 경우,
N+1 문제
<리스트 6> N+1문제를 가지는 형태의 SQLMap 파일
<resultMap id="get_account_family_nplus1" class="Account">
<result property="id" column="id" />
<result property="firstName" column="firstName" />
<result property="lastName" column="lastName" />
<result property="emailAddress" column= "emailAddress" />
</resultMap>
<resultMap id="get_family_nplus1" class="Family">
<result property="acc_id" column="id" />
<result property="fullName" column="fullName" />
<result property="account" column="id" select= "getFamiliesUsingNplus1" />
</resultMap>
<sql id="selectAccount_frag">
select
acc_id as id,
acc_first_name as firstName,
acc_last_name as lastName,
acc_email as emailAddress
from account
</sql>
<select id="selectAccountWithFamilyUsingNplus1" parameterClass="int" resultMap="get_family_nplus1">
select
acc_id as id,
family_fullname as fullName
from Account_family
where acc_id = #id#
</select>
<select id="getFamiliesUsingNplus1" parameterClass="int" resultMap="get_account_family_nplus1">
<include refid="selectAccount_frag"/>
where acc_id = #id#
</select>
log4j를 이용해 실제로 수행되는 SQL문을 찍어보면 다음과 같이 표시된다.
Executing Statement: select acc_id as id, family_fullname as fullName from Account_family where acc_id = ?
Executing Statement: select acc_id, acc_first_name, acc_last_name, acc_email from account where acc_id = ?
Executing Statement: select acc_id, acc_first_name, acc_last_name, acc_email from account where acc_id = ?
2(N)개의 데이터를 가져오기 위해 실제로는 SQL 문을 한 번 더(+1) 수행하여 총 3번(Account_family에 한번, Account에 두 번)을 실행하게 되는 셈이다. 이것을 N+1 문제라고 하는데 이를 개선하기 위해 groupBy를 사용할 수 있다.
<리스트 7> N+1문제를 가지는 형태의 자바 코드
@SuppressWarnings("unchecked")
public static List<Account> selectAccountWithFamilyUsingNPlus1(int id) throws SQLException {
List<Account> familys = (ArrayList)sqlMapper.queryForList("selectAccountWithFamilyUsingNplus1", new Integer(id));
return familys;
}
<리스트 8> N+1문제를 가지지 않는 형태의 SQLMap 파일
<resultMap id="get_account_family_avoidnplus1" class= "Account" groupBy="id">
<result property="id" column="id" />
<result property="firstName" column="firstName" />
<result property="lastName" column="lastName" />
<result property="emailAddress" column= "emailAddress" />
<result property="families" resultMap= "Account.get_family_avoidnplus1"/>
</resultMap>
<resultMap id="get_family_avoidnplus1" class="Family">
<result property="acc_id" column="id" />
<result property="fullName" column="fullName" />
</resultMap>
<select id="selectAccountWithFamilyAvoidNplus1" parameterClass="int" resultMap= "get_account_family_avoidnplus1">
select
a.acc_id as id,
a.acc_first_name as firstName,
a.acc_last_name as lastName,
a.acc_email as emailAddress,
f.family_fullname fullName
from account a left join account_family f
on a.acc_id=f.acc_id
where a.acc_id=#id#
</select>
<리스트 9> N+1문제를 가지지 않는 형태의 자바 코드
@SuppressWarnings("unchecked")
public static List<Account> selectAccountWithFamilyAvoidNPlus1(int id) throws SQLException {
List<Account> familys = (ArrayList<Account>)sqlMapper.queryForList("selectAccountWithFamilyAvoidNplus1", new Integer(id));
return familys;
}
조인 구문과 groupBy를 사용하면 다음처럼 일반 join 문을 사용하여 처리를 하기 때문에 N+1과 같은 문제가 발생하지 않는다. 현재는 이 방법이 가장 추천된다고 할 수 있다. 하지만 방법적으로 하위 결과 맵을 사용하는 것이 추천되는 방법이라고 보기는 어려울 듯하다. 아이바티스 3.0에서는 좀 더 개선된 방법이 제공될 것으로 짐작된다.
Executing Statement: select a.acc_id, a.acc_first_name, a.acc_last_name, a.acc_email, f.family_fullname from account a left join account_family f on a.acc_id=f.acc_id where a.acc_id=?
동적 SQL
<리스트 10> 동적 SQL을 사용하는 SQLMap파일 내용
<select id="selectAccountDynamic" parameterClass="Account" resultClass="Account">
<include refid="selectAccount_frag" />
<dynamic prepend="WHERE">
<isGreaterEqual prepend="AND" property= "id" compareValue="2">
acc_id = 1
</isGreaterEqual>
</dynamic>
</select>
<리스트 11> 동적 SQL을 호출하는 자바코드
@SuppressWarnings("unchecked")
public static List<Account> selectAccountDynamic(Account account) throws SQLException {
List<Account> accounts = (List<Account>)sqlMapper.queryForList("selectAccountDynamic", account);
return accounts;
}
필자의 경우 인자로 넣어준 id의 값이 3이었기 때문에 다음처럼 WHERE acc_id =1이 추가되어 처리되었다. 동적 SQL의 경우 자바 코드가 아닌 XML의 태그로 동적 SQL을 생성하기 때문에 손에 익지 않아 불편할 수도 있다. 이미 아이바티스 개발팀은 이 동적 SQL 부분에 대한 개선작업을 진행하고 있다. 그 결과물로 3.0에서 도입될 방식에 대해 잠시 뒤에 살펴보도록 하겠다.
<리스트 12> 동적 SQL을 호출하는 결과
Executing Statement: select acc_id as id, acc_first_name as firstName, acc_last_name as lastName, acc_email as emailAddress from account WHERE acc_id = 1
아이바티스 3.0 소식
아이바티스 3.0 개발은 다음과 같은 방향으로 진행이 된다.
- 테스트 주도 개발
- 성능보다는 코드의 간결성
- 복잡한 디자인 보다는 간결한 디자인
- 하나의 JAR파일
- 다른 라이브러리의 의존성을 없앰
- 더 나은 플러그인 지원
- 추가적인 플러그인을 위한 프로젝트(http://sourceforge.net/ projects/ibatiscontrib/)
역시나 기본적으로 계속 간결한 프레임워크를 유지하면서 많은 사용자들이 원하는 기능은 플러그인 형태로 제공하는 것이 기본적인 개발 방향임을 짐작할 수 있다. 역시 아이바티스는 간단함 내지 간결함으로 표현할 수 있는 프레임워크이다.
그럼 저 기본 방향을 염두에 두고 현재 다소 형상화되어 보이는 3.0 기능에 대해 간단히 살펴보자.
인터페이스 바인딩
일단 다음과 같은 기존 코딩 방식에 대해 살펴보자. 기존 방식의 문제점은 매핑 구문명이 문자열 기반이라 매핑 구문명의 철자가 잘못되어 있을 경우 에러가 발생하게 되고 컴파일시가 아닌 런타임 시 에러가 발견할 수 있게 된다. 더군다나 문자열 기반이라 SQLMap 파일의 증가는 곧 사용할 구문에 대한 체크에 일부 시간을 소요할 수밖에 없도록 만드는 계기가 된다.
그리고 반환 타입은 형 변환을 통해 명시화되지만 실제로 반환 타입의 모호로 인해 형 변환 시 ClassCastException은 아이바티스 사용자라면 한번쯤 겪게 되는 에러가 된다. 즉 애매한 형 변환과 문자열 기반의 매핑 구문은 결과적으로 대부분의 에러가 런타임 시 발생하여 개발을 힘들게 만드는 요인이 되고 있다.
이런 점을 해결하기 위해 나온 것인 이 인터페이스 바인딩이다.
기존에 XML에 타입을 선언하고 문자열 기반으로 구문을 호출하는 것 대신에 좀 더 서술적이고 타입에 안전한 인터페이스의 사용으로 앞서 언급된 문제점을 많이 보완해 나가고 있다. 다음의 코드는 기존 방식의 호출을 새로운 인터페이스 바인딩 방법으로 호출한 것이다.
애매한 형 변환 작업이 없고 문자열 기반의 매핑 구문을 사용하지 않아 기본적으로 컴파일 시 많은 에러를 찾아낼 수 있다. 요즘처럼 IDE를 기본적으로 사용하는 환경이라면 얼마나 많은 도움이 될지는 의심할 여지가 없다.
결과적으로 인터페이스 바인딩을 통해 매핑 구문명과 파라미터 타입, 결과 타입을 자동적으로 알 수 있게 되는 셈이다.
다중 레벨 설정
아이바티스의 오랜 설정 방식인 XML은 3.0에서도 역시 좋은 방법이 되겠지만 3.0에서 디폴트 설정 방법이 되지는 않는다. 3.0에서는 다음과 같은 다중 레벨 설정이 가능하게 된다.
- 관례(Convention)
- 어노테이션(말 그대로 하면 주석) : 앞의 관례보다 우선시 된다.
- XML : 앞의 관계, 어노테이션보다 우선시 된다.
- 자바 API : 앞의 관례, 어노테이션, XML보다 우선시 된다.
관례는 메소드 시그니처로 정해진 규칙이라고 보면 된다.
Employee getEmployee (int id); 라는 메소드 시그니처는 다음과 같은 SQL과 동일 시 된다고 볼 수 있다.
SELECT id, firstName, lastName FROM Employee WHERE id = ?
여기서 메소드 반환 타입의 이름이 테이블 명이 되고 메소드의 인자로 주어진 값이 자동으로 WHERE 조건문을 구성한다고 볼 수 있다. 간단한 조건문이나 들어가는 형식의 전체 데이터를 뽑는 기능이라면 이 메소드를 선언하여 추가 작업 없이 해당 기능이 구현할 수 있다.
어노테이션으로 설정하기
XML 설정을 넘어서 어노테이션으로 메타데이터 정보를 설정하는 기능이 많이 도입되었다. 이에 아이바티스도 이러한 기능을 추가적으로 지원한다. 현재 아이바티스의 XML 파일은 다음과 같은 정보를 가진다.
- 설정
- 메타 정보
- 코드
설정은 환경적인 정보이다.
메타정보는 결과 맵과 파라미터 맵, 그리고 캐시 모델들을 나타내고 코드는 SQL과 동적 SQL 요소를 포함한다. 설정 정보는 properties 파일이나 XML에 담겨야하고, 코드는 자바 코드나 XML에 담겨야 한다. 단 메타정보만이 어노테이션에서 처리가 가능하도록 될 것이다.
어노테이션의 예제는 다음과 같다. 앞의 관례에 의한 방식에서 칼럼 별 타입을 정의하고 조건문을 구체화하기 위해 어노테이션이 사용된 것을 볼 수 있다.
<리스트 13> CRUD 작업을 위한 어노테이션 사용 예제
@Select({"SELECT #id(EMP_ID:NUMERIC), #firstName(FIRST_NAME:VARCHAR), #lastName(LAST_NAME:VARCHAR) ",
"FROM EMPLOYEE",
"WHERE EMP_ID = @id"})
Employee selectEmployee(int id);
@Insert({"INSERT INTO EMPLOYEE (EMP_ID, FIRST_NAME, LAST_NAME)",
"VALUES (@id, @firstName, @lastName)"})
void insertEmployee(Employee emp);
@Delete("DELETE EMPLOYEE WHERE EMP_ID = @id")
void deleteEmployee(int id);
<리스트 14>와 같은 형식의 어노테이션도 사용 가능하게 된다. 여기서는 ResultClass와 PropertyResult를 추가적으로 어노테이션을 통해 정의한 것이다.
<리스트 14> ResultClass, PropertyResults를 사용하는 어노테이션 예제
@ResultClass (Department.class)
@PropertyResults({
@Result(property="id", column="DEPT_ID"),
@Result(property="name", column="NAME"),
@Result(property="employees",
nestedQuery=@QueryMethod(type=CompanyMapper.class, methodName="getEmployeesForDeparment", parameters="id"))
})
@Select("SELECT #id, #name FROM DEPARTMENT WHERE COMP_ID = @id ")
List<Department> getDepartmentsForCompany(int id);
<리스트 15>와 같이 캐싱을 설정할 수도 있다.
<리스트 15> 캐싱 설정의 어노테이션 사용 예제
@CacheContext("Employee")
public class EmployeeMapper {
void insertEmployee(Employee emp);
Employee getEmployee(int id);
List<Employee> findEmployeesLike(Employee emp);
}
기존 설정이라면 다음과 같은 의미를 가진다.
<리스트 16> 캐싱 설정의 기존방식 예제
<Mapper cacheContext="Employee">
<Insert id="insertEmployee" ...> ... </Insert>
<Select id="getEmployee" ...> ... </Select>
<Select id="findEmployeesLike" ...> ... </Select>
</Mapper>
@flushCache
List<Employee> updateAndGetSpecialEmployees();
는 다음과 같은 의미를 가진다.
<Select id=“findEmployeesLike” flushCache==“true”...> ... </Select>
예제를 통해 보면 현재의 설정을 일일이 XML 파일에 적어야 할 사항을 어노테이션을 통해 설정을 함으로써 일단 XML 파일 내용의 갱신 문제에 좀 더 자유로워 질 수 있을 듯하다. 한편, 개발자가 초기 XML 설정 외에 대부분의 작업을 자바 소스에서 제어함으로써 작업도 한결 수월하게 할 수 있을 것이다.
XML 설정 향상
다음은 XML 설정 향상과 관련된 주요 내용들이다.
1. 필드와 생성자 파라미터 그리고 자바빈즈 프로퍼티에 결과 및 파라미터를 맵핑된다.
2. N+1 문제를 해결하는 조인 매핑과 groupBy는 사용이 더 쉬워진다. <리스트 17>의
3. 결과 맵과 파라미터 맵은 기본적으로 ‘자동 매핑’ 되고 이름이 일치하지 않는 프로퍼티만을 명시한다.
4. 기능적으로 좀 더 뛰어나면서 간단한 타입 핸들러 구현체와 데이터 타입 변환 필터를 제공할 것이다.
5. 가장 큰 변화는 XML 파일이 Mapper.class의 복사본과 함께 처리한다는 점이다. 이를테면 EmployeeMapper.xml은 클래스패스에 존재하는 EmployeeMapper.class를 위해 로드 된다. 해당 매퍼 클래스의 객체를 생성하면 자동으로 같은 이름의 매퍼 XML 파일이 로드 되는 형식이라고 짐작된다. 객체 생성에 따라 매퍼 XML 파일이 로드 되는 형식이라면 SQL 구문의 갱신이 3.0에서는 어느 정도 해결될 듯하다.
<리스트 17> Mapper용 XML설정
<Mapper>
<ResultMap id="selectACompanyWithJoin" resultClass= "Company">
<Constructor column="C.COMP_ID"/>
<Constructor column="C.NAME"/>
<Property name="departments.id" column="D.DEPT_ID"/>
<Property name="departments.name" column="D.NAME"/>
<Collection type="Department.class" property= "departments" groupBy="id"/>
<Collection type="Employee.class" property= "departments.employees" groupBy="departments.id"/>
</ResultMap>
<Select id="selectACompanyWithJoin" parameters= "id:int,order:string">
SELECT
#{id},
#{name},
#{departments.id},
#{departments.name},
FROM COMPANY C
INNER JOIN DEPARTMENT D ON C.COMP_ID = D.COMP_ID
INNER JOIN EMPLOYEE E ON D.DEPT_ID = E.DEPT_ID
WHERE
C.COMP_ID = @{id}
ORDER BY ${order}
</Select>
</Mapper>
동적 SQL
현재의
<리스트 18> SQLSource를 확장한 동적 SQL시용 예제
public class GetAccountListSQL extends SQLSource {
public String getSQL(Object param) {
Account acct = (Account) param;
append("select * from ACCOUNT");
prepend(“WHERE”); // 다음의 append 앞에 기본적으로 추가한다.
if (exists(acct.getEmailAddress())) {
append("AND", "ACC_EMAIL like #EmailAddress#"); // 필요하다면 첫 번째 인자를 붙이겠지만 그렇지 않다면 첫 번째 인자는 무시된다.
}
if (greaterThan(0,acct.getID())) {
append("AND", "ACC_ID = #ID#");
}
prepend(); // 앞의 prepend 뒤에 아무 코드가 없다면 앞의 prepend를 지운다.
append ("order by ACCT_LAST_NAME");
}
}
<리스트 18>은 다음과 같이 사용할 수 있다.
@SQLSource(GetAccountListSQL.class)
List<Accout> getAccountList(Account acct);
또는 다음처럼도 가능하다.
<Select id=“getAccountList” source=“org.apache.GetAccount ListSQL” ...> ...
</Select>
그 외에 테이블 관계를 나타내기 위한 간단한 기능이 추가되고 SQL 생성을 지원하는 몇 가지 옵션도 추가될 것으로 보인다.
지금까지 개인적으로 원했던 기능 중에 3.0에서 추가되도록 활발히 진행 중인 것 위주로 살펴보았다. 하지만 곰곰이 생각해보면 아이바티스가 추구했던 간결함은 오히려 반감되고 있는 게 아닌가 하는 우려도 든다. 아이바티스의 장점은 간결함이다. JDBC에 익숙한 개발자라면 몇 시간의 교육만으로도 실제 프로젝트에 적용할 수 있을 정도다.
단점이라면 매핑 작업이 번거롭다는 점을 들 수 있을 것이다. 파라미터 맵과 결과 맵에 매핑해주는 작업이 JDBC를 그대로 사용하는 것보다 그렇게 쉬운 작업이 아니다. 그럼에도 불구하고 아이바티스를 많이 사용하게 되는 데에는 충분히 사용할만한 장점이 있기 때문일 것이다. 앞으로도 발전하는 아이바티스의 모습을 기대해 본다. @
참고자료
1. 아이바티스 홈페이지
2. AppFuse 홈페이지
3. 아이바티스 공식 문서 한글화 프로젝트
4. ORM의 또 다른 핵 iBATIS SQLMaps
5. 아이바티스에서 프로시저사용하기
6. http://www.mail-archive.com/user-java@ibatis.apache.org/msg02045.html
7. 아이바티스 3.0 화이트보드
* 이 기사는 ZDNet Korea의 제휴매체인 마이크로소프트웨어에 게재된 내용입니다.