sqljdbc.jar sqljdbc.jar 클래스 라이브러리는 JDBC 3.0을 지원합니다. sqljdbc.jar 클래스 라이브러리에는 JRE(Java Runtime Environment) 버전 5.0이 필요합니다. JRE 6.0에서 sqljdbc.jar을 사용하면 데이터베이스에 연결할 때 예외가 발생합니다.
참고: JDBC 드라이버 버전 2.0은 JRE 1.4를 지원하지 않습니다. JDBC 드라이버 버전 2.0을 사용하려면 JRE 1.4를 JRE 5.0 이상으로 업그레이드해야 합니다. 응용 프로그램이 JDK 5.0 이상과 호환되지 않아 다시 컴파일해야 하는 경우도 있습니다.
sqljdbc4.jar sqljdbc4.jar 클래스 라이브러리는 JDBC 4.0을 지원합니다. 이 라이브러리에는 sqljdbc.jar의 모든 기능과 함께 새로운 JDBC 4.0 메서드가 포함되어 있습니다. sqljdbc4.jar 클래스 라이브러리에는 JRE(Java Runtime Environment) 버전 6.0 이상이 필요합니다. JRE 1.4 또는 5.0에서 sqljdbc4.jar을 사용하면 예외가 발생합니다.
참고: 응용 프로그램을 JRE 6.0에서 실행해야 하는 경우에는 JDBC 4.0 기능을 사용하지 않더라도 sqljdbc4.jar을 사용하십시오.
자. 이제 위의 예제코드의 ResultSet을 XML데이터로 변형 해 봅시다. 이를 위해 com.sun.rowset.WebRowSetImple 클래스를 이용할 수 있습니다. 위 예제 코드에 몇 라인의 코드를 추가함으로써 result set을 XML 파일로 출력 할 수 있습니다.
<properties> 태그는 provider( isolation leve, RowSet type 등)를 의미 합니다. <metadata> 태그는 해당 데이타베이스의 테이블의 갯수, 이름, 컬럼타입 등을 나타내며, 마지막으로 <data> 태그는 해당 테이블의 실제 정보를 기술합니다.
예제의 자바 클래스는 connection 인스턴스를 최기화 하고, 수행할 SQL문자열을 포함하는 statement 를 생성한 후 result set을 조회하는 아주 일반적인 JDBC 코딩 양식을 보여주고 있습니다. 위 코드를 DatasourceConnector.java란 이름으로 저장하고 컴파일, 실행을 하면
RDBMS는 Data 질의와 갱신에을 위해 가장 광범위하게 이용되는 영구 저장 메카니즘(persistance storage mechanism) 입니다. Java Database Connectivity (JDBC) 는 Java 프로그램이 RDBMS 상의 데이터를 SQL을 이용하여 조작할 수 있게끔 해주는 프레임워크의 API 모임입니다.
Java 프로그램에서 데이터베이스의 데이터를 조회하거나 갱신하는데는 일련의 순서가 있습니다. 첫째로, 프로그램은 접속하고자하는 DataBase에 커넥션이 맺어져야 합니다. 이 커넥션을 맺는데는 몇 가지 다른 방법이 있습니다.. 관습적인 방법으로는 벤더에서 제공하는 JDBC드라이버를 로딩한 후 java.sql.DriverManger 클래스의 getConnection() 메소드를 호출하는 방법이 있을수 있습니다. 또 다른 한가지 방법은 접속하고자하는 데이터베이스를 개발한 회사에서 제공해 주는 java.sql.Connection 인터페이스를 구현한 클래스를 이용하는 방법이 있을수 있고, Java EE 컨테이너에서처럼 JNDI (Java Naming Direvtory Interface) lookup을 통해 커넥션을 맺는 방법도 있겠네요. 어느 방법을 이용하든 Java 프로그램은 java.sql.Connection 인터페이스를 이용하여 데이터베이스와 커넥션을 맺게됩니다. 둘째로, 위에서 얻어온 Connection 객체로부터 Query를 DataBase로 전송하기위한 java.sql.Statement 인터페이스를 생성합니다. 마지막으로 Statement의 실행 결과를 java.sql.ResultSet 인스턴스로 돌려 받습니다.
본 포스팅에서는 JDBC version 3.0을 지원하는 JDK version 5.0에서 새로이 등장한 WebRowSet을 이용하는 방법을 알아보겠습니다. JDBC 3.0은 데이터의 생성과 변형을 손쉽게 해 낼 수 있는 몇 가지 기능을 제공합니다.
WebRowSet 계층구조
본격적인 WebRowSet의 이야기에 앞서 WebRowSet 인터페이스의 상속 계층 구조를 먼저 살펴 보도록 하겠습니다.
WebRowSet inheritance Hierarchy(출처:OnJava)
상속관계에서 Root는 java.aql.ResultSet 인터페이스 입니다. 이 인터페이스의 인스턴스는 데이터베이스가 해석할수 있는 query를 포함하는 java.sql.Statement 인스턴스를 실행함으로써 얻어지는 표 형식의 데이터입니다. 기본적으로 result set은 앞으로만 탐색할 수 있고 업데이트를 할 수 없습니다. default result set을 가지고는 임의의 위치에 있는 데이터를 다룰 수 없다는 말이 됩니다.
자. result set에 담아진 임의의 위치에 있는 테이터를 제어하려면 어떤 방법이 있을까요? result set으로 무엇을 하길 원하는지에 따라 달라집니다. 예를들어 JavaBeans component model을 지원하기위해서는 java.sql.ResultSet의 서브인터페이스인 javax.sql.RowSet 인터페이스를 이용할 필요가 있겠죠.
자바프로그램에서 데이타베이스를 액세스하는것은 꽤나 무거운 오퍼레이션이기 때문에 이런 경우 메모리에 올라와있는 데이타 캐시는 성능의 주요한 key factor가 되는데요. 메모리에 적재된 데이타를 이용하기위해서 javax.sql.RowSet의 서브인터페이스인 javax.sql.rowset.CachedRowSet을 이용할 수 있습니다.CachedRowSet인터페이스의 인스턴스는 원하는 데이터를 가져오기위해 항상 데이터 베이스에 접속하지 않고 이미 메모리 상에 올라온 데이터를 통하여 원하는 결과를 가져올 수있능 능력이 있습니다. 이에 더해 CachedRowSet은 스크롤, 데이터 변경이 가능하고, serializable 합니다. 또한 스프레드시트와 같은 표형식의 데이터 소스에도 잘 작동 합니다.
위에 기술한 모든 기능들과, 거기에 더해 result set의 출력을 XML로 한다든지 하는 기능이 필요하다면 javax.sql.rowset.WebRowSet이 적절한 해답일 수 있습니다. WebRowSet인터페이스에 대해 썬에서는 JDK 5.0에 이미 reference implementation클래스로 com.sun.rowset.WebRowSetImpl 클래스를 제공하고 있습니다.
javax.sql.rowset 패키지의 마지막 서브인터페이스인 javax.sql.rowset.JoinRowSet은 result set 내의 object를 대상으로 SQL JOIN문과 같은 조작을 수행 할 수 있는 기능을 제공합니다.
회사의 인턴에 대한 교육을 목적으로 정말 오랜만에 Tomcat , MySQL 조합으로 예제 코드를 작성 하고 있었다.
톰캣이랑 MySQL의 조합을 마지막으로 사용 해 본게 톰캣 3.X 시절의 일이다.
현재의 조합은 톰캣 5.0 + MySQL 4.1.22 에 utf-8 로 캐릭터 셋을 설정 한데다 서블릿 필터와 JDBC 드라이버 버전업으로 예전의 그 악몽같던 JDBC 한글문제는 사라졌을거라 믿었다..
그러나 왠걸.. 코드를 작성하고 돌려보니 java 클래스 단까지는 한글이 잘 전달 되는데 db에 인서트 하는데 아래와 같은 익셉션이 발생...
MysqlDataTruncation 발생..
com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data truncated for column 'username' at row 1 at com.mysql.jdbc.SQLError.convertShowWarningsToSQLWarnings(SQLError.java:709) at com.mysql.jdbc.MysqlIO.scanForAndThrowDataTruncation(MysqlIO.java:3461) at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:1202) at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:681) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1368) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1283) at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1268) at code.logic.DbProcess.insertUserInfo(DbProcess.java:51) at code.servlet.SimpleWebApp.doPost(SimpleWebApp.java:61) at javax.servlet.http.HttpServlet.service(HttpServlet.java:709) at javax.servlet.http.HttpServlet.service(HttpServlet.java:802) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at code.filter.CharacterSetEncodingFilter.doFilter(CharacterSetEncodingFilter.java:105) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:368) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869) at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664) at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527) at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80) at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684) at java.lang.Thread.run(Thread.java:595)
이때의 connection url은 jdbc:mysql://localhost:3306/subby_test?useUnicode=true&characterEncoding=UTF8
이 문제는 꽤 간단하게 해결 할 수 있었다. url에 Truncation 옵션을 false 로 주어서 해결 할 수 있었지만 여전히 DB에는 한글이 안들어가고 있다.
생 ㅈㄹ을 다 해 본다. JDBC버전 별로 봐꿔가며 컴파일과 실행.. 그옜날 썼던 new String() 하며 캐릭터셋 바꿔보기.. 그래도 안된다.. 그만큼 짜증도 밀려오고..
잠시 머리를 식히고 다시 차근차근 생각 해 봤다. 우선 톰캣의 JSP와 MYSQL의 캐릭터 셋은 분명히 utf-8인 상황이다. 그럼 순순히 안 될리가 없지않나? 역시나 JDBC 드라이버 문제인가? JDBC드라이버를 최신의 안정버전인 5.0.6 으로 선택하고 JDBC 메뉴얼을 살피기 시작했다..
그러다 찾게 된 희망의 한 단락 Using the UTF-8 Character Encoding - Prior to MySQL server version 4.1, the UTF-8 character encoding was not supported by the server, however the JDBC driver could use it, allowing storage of multiple character sets in latin1 tables on the server. Starting with MySQL-4.1, this functionality is deprecated. If you have applications that rely on this functionality, and can not upgrade them to use the official Unicode character support in MySQL server version 4.1 or newer, you should add the following property to your connection URL: useOldUTF8Behavior=true
쌍.. 장난하냐? 장난해? 이놈은 뻑하면 옵션을 바꿔... 답은 useOldUTF8Behavior 에 있는 듯했다.. 적용하고 돌려보니 깔끔.. 이런 옵션이야 메뉴얼에서 찾는게 정석이긴 하지만, MySQL은 버전이나 서버 설정에 따른 옵션 변화가 너무 많아 난감한 경우가 한두번이 아니다..
아~ 내 3시간은 어딜가서 하소연 하나...
최종적으로 문제를 해결한 utf8 과 MySQL JDBC Driver 5.0 에서의 DB URL은 jdbc:mysql://localhost:3306/subby_test?useUnicode=true&characterEncoding=UTF8&jdbcCompliantTruncation=false&useOldUTF8Behavior=true