'jdbc'에 해당되는 글 15

  1. 2010.07.09 JDBC 드라이버에서 날리는 쿼리 확인 - SQL Server
  2. 2010.05.14 JDBC URL for Oracle RAC 1
  3. 2009.12.05 JDBC로 프로시저 호출하기
  4. 2009.08.14 시스템 Hangup 현상 분석
  5. 2009.07.25 SQL Server 2005에서 SchemaSpy 실행하기
  6. 2009.07.24 Apache Derby
  7. 2009.06.18 Oracle JDBC Driver, 버전 및 크기
  8. 2009.05.11 Microsoft SQL Server JDBC Driver
  9. 2009.05.11 MySQL JDBC Driver
  10. 2009.05.05 JDBC Code Template - 프로시저
  11. 2009.04.29 JDBC Code Template
  12. 2009.04.23 해서는 안되는 예외 처리 - 아무것도 안하는 경우
  13. 2009.04.18 좋은 코딩 습관 - 리턴값 확인
  14. 2009.04.15 데이터 확인용 JSP/미완/spring/jndi추가
  15. 2008.12.26 시스템 정보 조회용 JSP

JDBC 드라이버에서 날리는 쿼리 확인 - SQL Server

JDBC 프로그램에서 쿼리를 날렸을 때 DBCC INPUTBUFFER spid 의 결과

- Profiler를 이용하면 쿼리와 매개변수까지 확인 가능함.

- sqljdbc.jar - 569KB (583,286 바이트) 사용, SQL Server 2005

DriverManager.getConnection()
Language Event    0     set transaction isolation level  read committed  set implicit_transactions off

Connection#setAutoCommit(false)
Language Event    0    set implicit_transactions on

Statement#executeQuery()
Language Event    0    SELECT top 2 * FROM TB_SAMPLE

PreparedStatement#executeQuery()
Language Event    0     set transaction isolation level  read committed  set implicit_transactions off //실행시킨 쿼리문을 확인할 수 없음, getConnection() 호출시 남아 있던 문장

CallableStatement#executeQuery()
//상동

SYSPROCESSES에서 program_name을 확인할 수 없음.

- jtds-1.2.2 사용, SQL Server 2005
DriverManager.getConnection()
Language Event    0    SELECT @@MAX_PRECISION  SET TRANSACTION ISOLATION LEVEL READ COMMITTED  SET IMPLICIT_TRANSACTIONS OFF  SET QUOTED_IDENTIFIER ON  SET TEXTSIZE 2147483647

Connection#setAutoCommit(false)
Language Event    0    SET IMPLICIT_TRANSACTIONS ON

Statement#executeQuery()
Language Event    0    SELECT top 2 * FROM TB_SAMPLE

PreparedStatement#executeQuery()
Language Event    0    (@P0 nvarchar(4000))SELECT top 2 * FROM TB_SAMPLE WHERE key =  @P0

CallableStatement#executeQuery()
RPC Event    0    DB123.dbo.pr_test;1

SYSPROCESSES에서 program_name이 jTDS로 확인됨.

- 연결된 서버를 통한 4파트 쿼리나 Openquery도 위와 동일함.
모두 Language Event 이다.

- auto commit이 false면 opentran 1, 그러나 락은 안보임

- sqljdbc.jar - 569KB (583,286 바이트) 사용, SQL Server 2000
커넥션 붙는 부분 위와 동일

CallableStatement, PreparedStatement#executeQuery()
RPC Event    0    sp_prepexec;1

- jtds-1.2.2 사용, SQL Server 2000
CallableStatement#executeQuery()
RPC Event    0    PR_test;1

PreparedStatement#executeQuery()
RPC Event    0    sp_execute;1

- 2005에서 실행된 쿼리를 볼 때 이 쿼리를 이용한다.

- 연결된 서버
처음 접속한 서버(2005)는 일반적인 커넥션과 동일하고, 연결된 원격의 서버(2000)에서 확인해보면 다음과 같은 특징을 가진다.

sp_who
spid ecid status      loginame    hostname        blk  dbname
58    0     dormant    test_user    DBS2005          0     NULL

sysprocesses에서 hostname이 DBS2005 이고 program_name이 Microsoft SQL Server 이다.

dbcc inputbuffer (58)
RPC Event    0    sp_reset_connection;1

JDBC URL for Oracle RAC

Connection refused(DESCRIPTION=(TMP=)(VSNNUM=186646784)(ERR=12505)(ERROR_STACK=(ERROR=(CODE=12505)(EMFI=4))))

jdbc:oracle:thin:@1.2.3.4:1521:SID //일반적인 방법, RAC으로 구성된 경우 위와 같은 에러 발생함.

jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=
    (ADDRESS=(PROTOCOL=TCP)(HOST=1.2.3.4)(PORT=1521))
    (ADDRESS=(PROTOCOL=TCP)(HOST=1.2.3.5)(PORT=1521))
    (FAILOVER=on)(LOAD_BALANCE=on)
)(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ZCGS))) //WebLogic 설정인데 오류가 발생함. failover가 description밖으로 나와야 할거 같다.

jdbc:oracle:thin:@(DESCRIPTION=(FAIL_OVER=ON)(LOAD_BALANCE=ON)(ADDRESS_LIST=
    (ADDRESS = (PROTOCOL = TCP) (HOST = 1.2.3.4) (PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP) (HOST = 1.2.3.5) (PORT = 1521))
)(CONNECT_DATA =(SERVICE_NAME = ZCGS)
    (FAILOVER_MODE=(TYPE=SELECT)(METHOD=BASIC)(RETRY=180)(DELAY=5)))
) //검색해서 가져온 설정, 된다.

jdbc:oracle:thin:@(DESCRIPTION=(LOAD_BALANCE=on)
    (ADDRESS=(PROTOCOL=TCP)(HOST=1.2.3.4) (PORT=1521))
    (ADDRESS=(PROTOCOL=TCP)(HOST=1.2.3.5) (PORT=1521))
(CONNECT_DATA=(SERVICE_NAME=service))) //이것도 된다고 함. 이게 더 나을거 같다.

jdbc:oracle:thin:@1.2.3.4^1.2.3.5:1521:SID //이건 안됨.

- http://www.jugpadova.it/articles/2007/04/11/jdbc-url-for-oracle-rac

JDBC로 프로시저 호출하기

- SQL Server에서 SELECT 결과를 리턴하는 경우
CallableStatement cs = null;
ResultSet rs = null;
try{
	cs = con.prepareCall("{ ? = call PR_AUTH('TEAM', ?, 'emp_no', ?) }");
	cs.registerOutParameter(1, Types.OTHER); //리턴값이 결과집합임.
	cs.setString(2, "A");
	cs.registerOutParameter(3, Types.INTEGER);
	if(cs.execute()){
		rs = (ResultSet)cs.getResultSet();
		while(rs.next()){
			out.println(rs.getString(1) + "\t" + rs.getString(2));
		}
		out.println("code:" + cs.getInt(3));
	}
}finally{
	close rs, cs, con;
}
getDatabaseProductName : Microsoft SQL Server
getDatabaseProductVersion : 9.00.1399
getDriverName : Microsoft SQL Server 2005 JDBC Driver
getDriverVersion : 1.2.2828.100
getDriverVersion(int) : 1.2
getJDBCVersion : 3.0

- 위와 동일한 환경에서 PreparedStatement로 프로시저 실행
PreparedStatement ps = null;
...
ps = con.prepareStatement("EXEC PR_AUTH 'TEAM', ?, 'emp_no', '' ");
ps.setString(1, "A"); //input인자는 가능하지만 마지막에 위치한 output인자는 지정할 수 없다(PreparedStatement에는 registerOutParameter()가 없다.)
ResultSet rs = ps.executeQuery();
while(rs.next()){
	out.println(rs.getString(1) + "===>" + rs.getString(2) + "<br />");
}


[todo] openquery로도 가능하지 않나?

시스템 Hangup 현상 분석

Z건설사 시스템이 페이지가 열리지 않는 경우가 발생함.

[todo] 스레드덤프를 남겼지만 분석을 하지 못함.

어디에서 발생하는 에러인지 원인이 WAS인지 DB인지 파악도 안됨.

DBCP 설정에서 removeAbandoned, logAbandoned를 true로 두고 모니터링을 함.

	...
	rs.close(); rs = null;
	pstmt.close(); pstmt = null;
	conn.commit(); conn = null;

}catch(Exception e){
	ROLLBACK
	THROW EXCEPTION
}finally{
	CLOSE ResultSet, PreparedStatement, Connection
}
RETURN
예외처리도 정확하게 되었지만 Connection이 닫히지 않아 누수가 되고 있다.

[todo] 스레드덤프받아서 분석해보자.

SQL Server 2005에서 SchemaSpy 실행하기

- 데이터베이스 타입을 mssql05로 두고, 드라이버를 sqljdbc.jar를 사용한 경우 아래와 같은 에러가 발생한다.

Failed to retrieve column comments: com.microsoft.sqlserver.jdbc.SQLServerException: variant 데이터 형식은 지원되지 않습니다.
SELECT OBJECT_NAME(c.object_id) AS TABLE_NAME, c.name AS COLUMN_NAME, ex.value AS comments FROM sys.columns c LEFT OUTER JOIN sys.extended_properties ex ON ex.major_id = c.object_id AND ex.minor_id = c.column_id AND ex.name = 'MS_Description' WHERE OBJECTPROPERTY(c.object_id, 'IsMsShipped')=0 ORDER BY OBJECT_NAME(c.object_id), c.column_id
com.microsoft.sqlserver.jdbc.SQLServerException: 연결이 닫혔습니다.
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(Unknown Source)
    at com.microsoft.sqlserver.jdbc.SQLServerDatabaseMetaData.checkClosed(Unknown Source)
    ...
    at net.sourceforge.schemaspy.SchemaAnalyzer.analyze(SchemaAnalyzer.java:164)
    at net.sourceforge.schemaspy.Main.main(Main.java:21)

- 데이터베이스 타입을 mssql05-jtds로 바꾼다.

Apache Derby

- http://db.apache.org/derby/

- 더비는 임베디드되거나 서버 형태로 구동될 수 있다.

- Embedded Derby
  • jdbc:derby:testDerbyDB;create=true
  • create=true 옵션은 데이터베이스가 없는 경우 생성하도록 한다.
  • 데이터베이스명만 있는 경우 사용자 디렉토리에 데이터베이스가 생긴다.(이 경우 C:\Documents and Settings\USER\testDerbyDB)
  • /testDerbyDB 이면 C드라이브에 생성됨, 절대경로로 지정할 수도 있음.
  • org.apache.derby.jdbc.EmbeddedDriver
  • derby.jar: contains the Derby engine and the Derby Embedded JDBC driver

- Derby Network Server
  • jdbc:derby://localhost:1527/testDerbyDB;create=true
  • DERBY/bin/startNetworkServer 로 서버를 시작할 수 있다.
  • 데이터베이스는 서버를 실행시킨 디렉토리에 생성된다. Eclipse에서 Ant로 실행시키면 프로젝트 루트에 생긴다.
  • org.apache.derby.jdbc.ClientDriver
  • 클라이언트 : derbyclient.jar
  • 서버 : derbyrun.jar, derbynet.jar, derby.jar

- 참고
Apache Derby로 개발하기 -- Trifecta: Apache Derby 소개 (한글)
Derby 소개
Cloudscape FAQ (한글)
derby의 유용한 기능

Oracle JDBC Driver, 버전 및 크기

ssss

- http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html
  • Oracle Database 11g Release 1 (11.1.0.7.0) JDBC Drivers
    ojdbc5.jar (1,890,499 bytes) - Classes for use with JDK 1.5.
    ojdbc6.jar (1,988,051 bytes) - Classes for use with JDK 1.6.
  • Oracle Database 11g Release 1 (11.1.0.6.0) JDBC Drivers
    ojdbc5.jar (1,879,860 bytes)
    ojdbc6.jar (1,977,267 bytes)
  • Oracle Database 10g Release 2 (10.2.0.4) JDBC Drivers
    classes12.jar (1,609,607 bytes) - for use with JDK 1.2 and JDK 1.3
    ojdbc14.jar (1,555,682 bytes) - classes for use with JDK 1.4 and 1.5
  • Oracle Database 10g Release 2 (10.2.0.3) JDBC Drivers
    classes12.jar (1,600,090 bytes) - for use with JDK 1.2 and JDK 1.3
    ojdbc14.jar (1,545,954 bytes) - classes for use with JDK 1.4 and 1.5
  • Oracle Database 10g Release 2 (10.2.0.2) JDBC Drivers
    classes12.jar (1,594,191 bytes) - for use with JDK 1.2 and JDK 1.3
    ojdbc14.jar (1,540,457 bytes) - classes for use with JDK 1.4 and 1.5
  • Oracle Database 10g Release 2 (10.2.0.1.0) JDBC Drivers
    classes12.jar (1,590,491 bytes) - for use with JDK 1.2 and JDK 1.3
    ojdbc14.jar (1,536,979 bytes) - classes for use with JDK 1.4 and 1.5
  • Oracle Database 10g 10.1.0.5 JDBC Drivers
    classes12.jar (1,442,469 bytes) - for use with JDK 1.2 and JDK 1.3
    ojdbc14.jar (1,378,346 bytes) - classes for use with JDK 1.4
  • Oracle Database 10g 10.1.0.2 JDBC Drivers
    classes12.jar (1,417,089 bytes) - for use with JDK 1.2 and JDK 1.3
    ojdbc14.jar (1,352,918 bytes) - classes for use with JDK 1.4
  • Oracle9i 9.2.0.8 JDBC Drivers
    ojdbc14.jar - JDBC classes (1,212,964 bytes) - For use with JDK 1.4
    classes12.jar - JDBC classes (1,234,433bytes) - For use with JDK 1.2 and JDK 1.3
    classes111.jar - JDBC classes (1,063,074 bytes) - For use with JDK 1.1
  • Oracle9i 9.2.0.5 JDBC Drivers
    ojdbc14.jar - JDBC classes (1,200,046 bytes) - For use with JDK 1.4
    classes12.zip - JDBC classes (1,232,604 bytes) - For use with JDK 1.2 and JDK 1.3
    classes111.zip - JDBC classes (1,063,479bytes) - For use with JDK 1.1
  • Oracle9i 9.2.0.4 JDBC Drivers
    ojdbc14.jar - JDBC classes (1,187,584 bytes) - For use with JDK 1.4
    classes12.zip - JDBC classes (1,219,950 bytes) - For use with JDK 1.2 and JDK 1.3
    classes111.zip - JDBC classes (1,052,833 bytes) - For use with JDK 1.1
  • Oracle9i 9.2.0.3 JDBC Drivers
    ojdbc14.jar - JDBC classes (1,181,679 bytes) - For use with JDK 1.4
    classes12.zip - JDBC classes (1,213,897 bytes) - For use with JDK 1.2 and JDK 1.3
    classes111.zip - JDBC classes (1,048,261 bytes) - For use with JDK 1.1
  • Oracle9i 9.2.0.1 JDBC Drivers
    ojdbc14.jar - JDBC classes ( 1,174,976 bytes) - For use with JDK 1.4
    classes12.zip - JDBC classes ( 1,207,068 bytes) - For use with JDK 1.2 and JDK 1.3
    classes111.zip - JDBC classes ( 1,043,528 Bytes) - For use with JDK 1.1
  • Oracle9i 9.0.1.4 JDBC Drivers
    classes12.zip - JDBC classes (1,143,559 bytes) - For use with JDK 1.2 and JDK 1.3
    classes111.zip - JDBC classes (988,625 bytes) - For use with JDK 1.1
  • Oracle9i 9.0.1 JDBC Drivers
    classes12.zip - JDBC classes( 1,081 kb) - For use with JDK 1.2 and JDK 1.3
    classes111.zip - JDBC classes ( 936 kB) - For use with JDK 1.1
  • Oracle8i 8.1.7.1JDBC Drivers
    classes12.zip - JDBC classes ( 1,892 kB) - For use with JDK 1.2
    classes111.zip - JDBC classes ( 1,741 kB)

Microsoft SQL Server JDBC Driver

2008을 지원한다기 보다 jdbc 4.0을 지원하는 거다.
SQL Server JDBC Driver 2.0 Documentation
http://msdn.microsoft.com/ko-kr/library/bb418447%28v=SQL.10%29.aspx

JDBC 드라이버 시스템 요구 사항
http://msdn.microsoft.com/ko-kr/library/ms378422%28v=SQL.100%29.aspx


- MS Driver


  • 이 드라이버는 아마도 SQL Server 2000 드라이버.
  • com.microsoft.jdbc.sqlserver.SQLServerDriver
  • jdbc:microsoft:sqlserver://ADDRESS:1433
  • mssqlserver.jar 와 com/microsoft/jdbc/base/BaseDriver 을 필요로 한다.

- jTDS
  • http://jtds.sourceforge.net
  • net.sourceforge.jtds.jdbc.Driver
  • jdbc:jtds:sqlserver://ADDRESS:1433/DB_NAME
  • jtds-1.2.2.jar
  • TEXT 컬럼 처리 가능

=-=> ms 드라이버는 text 컬럼처리 안되는게 있을거야.

- Windows 통합인증을 사용할 때는 sqljdbc_auth.dll이 필요 ==???


MySQL JDBC Driver

- MySQL Connector/J
  • MySQL Connectors
  • com.mysql.jdbc.Driver
  • jdbc:mysql://ADDRESS/DB_NAME?Unicode=true&characterEncoding=EUC_KR
  • mysql-connector-java-5.1.0-bin.jar(554KB)

JDBC Code Template - 프로시저

CallableStatement cs = null;
try{
    cs = con.prepareCall("{ ? = call sp_test(?,?,?) }");
    int idx = 0;
    cs.registerOutParameter(++idx, Types.INTEGER);
    cs.setString(++idx, COL_1);
    cs.setString(++idx, COL_2);
    cs.setString(++idx, COL_3);
    
    cs.execute();
    int result = cs.getInt(1);
    if(result == -1){ //check error
        throw new SQLException("result is -1"); //=-=> 좀 더 정형화된 다른 예외를 만들어야...
    }
    return result;

}catch(Exception e){  
    throw new RuntimeSQLException(e, primaryKey); 
}finally{
    if(cs != null){ try{ cs.close(); }catch(Exception ignored){  } }
}

JDBC Code Template


PreparedStatement ps = null;
try{
    ps = con.prepareStatement(QUERY);
    int idx = 0;
    ps.setString(++idx, COL_1);
    ...
    ps.setString(++idx, COL_N);
    
    return ps.executeUpdate();

}catch(Exception e){
    throw new RuntimeSQLException(e, primaryKey);
}finally{
    if(ps != null){ try{ ps.close(); }catch(Exception ignored){  }
}
Connection con = null;
PrepareStatement ps = null;

try{
	ps = con.prepareStatement(QUERY);  
	int idx = 0;  
	ps.setString(++idx, COL_1);  
	...  
	ps.setString(++idx, COL_N);  

	int affectedRow = ps.executeUpdate();
	if(affectedRow != 1){
		throw new RuntimeSQLException(" expected : 1, result : " + affectedRow
			, primaryKey); //=-=> 정형화...
	}
	con.commit();
}catch(Exception e){
	con.rollback();
	throw new RuntimeSQLException(e, primaryKey);
}finally{
    if(ps != null){ try{ ps.close(); }catch(Exception ignored){  }
    if(con != null){ try{ con.close(); }catch(Exception ignored){  }
}

해서는 안되는 예외 처리 - 아무것도 안하는 경우

예외를 잡아서 아무것도 처리하지 않는 경우 예외가 발생했는지도 모르기 때문에 유지보수가 힘들다.
필요한 경우가 아닌 경우 절대 해서는 안되는 방식이다.
try{
    //throw exception
}catch(Exception e){
    //do nothing
}

로그만 남기고 지나는 경우도 별반 다르지 않다.
사용자는 예외가 발생했는지 인지할수가 없다.
그냥 안된다고만 한다.
try{
    //throw exception
}catch(Exception e){
    logger.error(e);
}

예외처리를 하더라도 로그는 남겨야 한다.(로그는 중앙집중적으로 한곳에서 남겨야 한다.)
에러났다고 하는데 원인을 파악할 수 없다.
try{
    //throw exception
}catch(Exception e){
    isError = true; //예외를 추적할 수 있는 e를 그냥 무시하고 있다.
    또는
    throw new XxxException(e.getMessage()); //이런 경우에도 예외를 추적할 수 없다. 필요한 경우가 아니라면 이렇게 사용하지 말고 아래와 같이 생성자에 이전 예외를 넣어준다. 
    //throw new XxxException(e); //권장
}
...
if(isError){
    message("에러입니다.");
}

  [todo] 적절한 예외 처리 를 정리할 것.

좋은 코딩 습관 - 리턴값 확인

자바는 예외로 처리할 수 있지만 C 언어같은 경우는 실행성공유무를 리턴값으로 돌려 준다.
이런 리턴값을 확인하지 않고 넘어가는 경우 디버깅하기가 힘들다.

SQL 관련 처리를 할때도 INSERT/UPDATE/DELETE 구문을 실행한 후에 삽입/수정/삭제된 개수를 알수 있다.
이 개수를 확인하지 않아서 프로그램에 구멍이 생기는 경우가 많다.
적용된 개수를 반드시 확인하도록 한다.
int affectedRows = INSERT/UPDATE/DELETE result
if(affectedRows != 1){
    THROW "SQL EXECUTE ERROR : " + affectedRows
}

데이터 확인용 JSP/미완/spring/jndi추가

<%@page contentType="text/html; charset=UTF-8" %>
<%@page import="java.sql.*"%>
<%
String cmd = request.getParameter("cmd");
String query = empty(request.getParameter("query"));
%>
<html>
<head><title></title>
</head>
<body>
<form method="post">
<input type="hidden" name="cmd" value="execute">
<textarea name="query" rows="" cols=""><%= query %></textarea>
<input onclick="this.form.submit();" type="button">

<%
if("execute".equals(cmd)){
	
	Connection con = getConnection(); 
	PreparedStatement ps = null;
	ResultSet rs = null;
	
	try{
		ps = con.prepareStatement(query);
		boolean queryType = ps.execute();
		if(queryType){			
			
			ResultSetMetaData md = ps.getMetaData();
			int columnCount = md.getColumnCount();
			out.println("<table border="1"><tr>");
			for(int i = 1 ; i <= columnCount ; i++){
				out.println("<td>" + md.getColumnName(i) + "</td>"); //getColumnLabel()...sql server는 columnName 과 동일함.
			}
			out.println("</tr>");
						
			rs = ps.getResultSet();
			while(rs.next()){
				out.println("<tr>");
				for(int j = 1 ; j <= columnCount ; j++){
					out.println("<td>" + rs.getObject(j) + "</td>");
				}
				out.println("</tr>");
			}
			out.println("</table>");
		}else{
			//ps.getUpdateCount() //select 에도 이게 되나? sql server 는 select 인 경우 -1임.
		}		
	}catch(Exception e){ //이거 출력하는거 하나 만들어둬.
%>
		<%= e %>
<%		
	}finally{
		close(con, ps, rs);
	}
}
%>
<%!
Connection getConnection(){ //이 메서드의 구현이 바뀌겠지
	return project.DBConnectionManager.getInstance();
}
String empty(String s){
	return s == null ? "" : s;
}
void close(Connection con, Statement st, ResultSet rs){
	if(rs != null){ try{ rs.close(); }catch(Exception ignored){ } }
	if(st != null){ try{ st.close(); }catch(Exception ignored){ } }
	if(con != null){ try{ con.close(); }catch(Exception ignored){ } }
}
%>

UPDATE/DELETE/INSERT 구문은 affectedRows 를 넘겨받아서 일치하는 경우만 commit 하도록 하자.
SELECT 시 로우개수를 제한할 것.


시스템 정보 조회용 JSP

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.