본문 바로가기

Java dev/JSPServlet

Commons-dbcp


Commons-dbcp

1. DBCP (DataBase Connection Pool)

이제는 데이터베이스 풀을 이용한 커넥션 풀을 사용하지 않는다는것은 상상조차 할 수 없게 되었다. 각 WAS의 벤더들은 오래전부터 자사제품에 대해 최적화된 커넥션 풀을 기본적으로 제공을 하기까지 이르렀다. 또한 한스버그의 커넥션 풀링이나 풀맨의 풀등 여러 오픈된 커넥션 풀 소스들이 돌아다기기도 하였다.

여기서는 Jakarta Commons에서 진행하고있는 Commons-dbcp project에 대해 알아보고 간단한 예제를 소개하기로 하겠다


II. 다운로드 및 설치

dbcp는 commons의 pool과 collection을 사용하기때문에 다음3가지를 모두 설치해야 정상적으로 dbcp를 사용할 수 있다.

Commons-dbcp http://jakarta.apache.org/site/downloads/downloads_commons-dbcp.cgi

Commons-pool http://jakarta.apache.org/site/downloads/downloads_commons-pool.cgi

Commons-collections http://jakarta.apache.org/site/downloads/downloads_commons-collections.cgi

DBCP API http://jakarta.apache.org/commons/dbcp/apidocs/index.html

 

/WEB-INF/lib/ 에 다운받은 3가지 *.jar를 복하하도록 한다


III. DBCP 웹에서 사용하기!

이번 dbcp 강의는 웹에서 사용하는것을 전제로 설명하겠습니다 ^^

톰캣을 사용한다면 server.xml에 dbcp를 설정해도 되지만 web.xml에 설정하는것이 좀더 유연성을 지닙니다


1) JDBC 정보 정의

web.xml에 dbcp를 사용하기위한 jdbc 정보를 정의합니다.

   <servlet>
      <servlet-name>connectioninitialize</servlet-name>
      <servlet-class>db.ConnectionInitialize</servlet-class>
      <init-param>
         <param-name>driverClassName</param-name>
         <param-value>org.gjt.mm.mysql.Driver</param-value>
      </init-param>
      <init-param>
      <param-name>url</param-name>
      <param-value>jdbc:mysql://localhost/unicorn</param-value>
      </init-param>
      <init-param>
         <param-name>username</param-name>
         <param-value>unicorn</param-value>
      </init-param>
      <init-param>
         <param-name>password</param-name>
         <param-value>iloveyou</param-value>
      </init-param>
      <init-param>
         <param-name>defaultAutoCommit</param-name>
         <param-value>true</param-value>
      </init-param>
      <init-param>
         <param-name>defaultReadOnly</param-name>
         <param-value>false</param-value>
      </init-param>
      <init-param>
         <param-name>maxActive</param-name>
         <param-value>10</param-value>
      </init-param>
      <init-param>
         <param-name>maxIdle</param-name>
         <param-value>30</param-value>
      </init-param>
      <init-param>
         <param-name>maxWait</param-name>
         <param-value>10000</param-value>
      </init-param>
      <load-on-startup>1</load-on-startup>   
   </servlet>


driverClassName : 사용할 jdbc driver의 class name

url : jdbc driver의해 연결할 connection에 대한 url

username : connection을 연결할 사용자 이름

password : connection을 연결할 사용자 비밀번호

defaultAutoCommit : 풀에의해 생성된 커넥션의 auto-commit 상태

defaultReadOnly : 풀에의해 생성된 커넥션의 read-only 상태

                             jdbc에 따라 지원하지 않을수도 있다 (예 : informix)

maxActive : 커넥션풀의 Active한 커넥션의 최대 갯수

maxIdle : 커넥션풀의 idle한 커넥션의 최대 갯수

maxWait : 커넥션이 풀에 반납되기까지 기다리는 최대 시간 (millisecond)


2) JDBC 정보 로딩 및 초기화

이 클래스는 web.xml에 의해 서블릿 컨테이너가 구동될 때 딱 한번 실행됩니다.

db.ConnectionInitialize.java

package db;


import java.sql.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.commons.dbcp.ConnectionFactory;
import org.apache.commons.dbcp.PoolingDriver;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.DriverManagerConnectionFactory;


public class ConnectionInitialize extends HttpServlet {
 
 public void init() throws ServletException {
 
    String driverClassName = null;
    String url = null;
    String username = null;
    String password = null;
    boolean defaultAutoCommit = true;
    boolean defaultReadOnly = false;
    int maxActive = 0;
    int maxIdle = 0;
    long maxWait = 0;


    // web.xml에서 정의한 파라미터 이름을 enumeration에 담습니다.

    Enumeration names = getServletConfig().getInitParameterNames();
       do {
          if(!names.hasMoreElements())
             break;


          // web.xml에 정의한 파라미터값을 가져옵니다.

          String name = (String)names.nextElement();
          String value = getServletConfig().getInitParameter(name).trim();

          System.out.println(name+" : "+value);


          if (name.startsWith("driverClassName"))
             driverClassName = value;
          else if (name.startsWith("url"))
             url = value;
          else if (name.startsWith("username"))
             username = value;
          else if (name.startsWith("password"))
             password = value;
          else if (name.startsWith("defaultAutoCommit"))
             defaultAutoCommit = "true".equals(value);
          else if (name.startsWith("defaultReadOnly"))
             defaultReadOnly = "true".equals(value);
          else if (name.startsWith("maxActive"))
             maxActive = Integer.parseInt(value);
          else if (name.startsWith("maxIdle"))
             maxIdle = Integer.parseInt(value);
          else if (name.startsWith("maxWait"))
             maxWait = Long.parseLong(value);

        } while(true);
       
        try {

           // jdbc driver 및 기타 정의를 설정합니다.
           setupDriver(driverClassName,

                           url,

                           username,

                           password,

                           defaultAutoCommit,

                           defaultReadOnly,

                           maxActive,

                           maxIdle,

                           maxWait);


           System.out.println("Connection initialize success");
        } catch (Exception exception) {
           System.out.println("Connection initialize fail!");
           exception.printStackTrace();
      }
 }
 
 public void setupDriver(String driverClassName,
       String url,
       String username,
       String password,
       boolean defaultAutoCommit,
       boolean defaultReadOnly,
       int maxActive,
       int maxIdle,
       long maxWait) throws Exception {


       try {

            // jdbc class를 로딩합니다.
            Class.forName(driverClassName);
        } catch (ClassNotFoundException classnotfoundexception) {
            System.out.println(driverClassName+" is not found");
            classnotfoundexception.printStackTrace();
            throw classnotfoundexception;
        }
       

        // 커넥션 풀로 사용할 commons-collections의 genericOjbectPool을 생성합니다.
        GenericObjectPool connectionPool = new GenericObjectPool(null);
        connectionPool.setMaxActive(maxActive);
        connectionPool.setMaxIdle(maxIdle);
        connectionPool.setMaxWait(maxWait);
       

        // 풀이 커넥션을 생성하는데 사용하는 DriverManagerConnectionFactory를 생성합니다.
        ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(url, username, password);
       

        // ConnectionFactory의 래퍼 클래스인 PoolableConnectionFactory를 생성한다
        PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, connectionPool, null, null, defaultReadOnly, defaultAutoCommit);
       

        // 마지막으로 PoolingDriver 자신을 로딩한다
        Class.forName("org.apache.commons.dbcp.PoolingDriver");

        PoolingDriver driver = (PoolingDriver) DriverManager.getDriver("jdbc:apache:commons:dbcp:");

        // 그리고 풀에 등록한다. 풀이름을 "unicorn"이라고 지정하였다        
        driver.registerPool("unicorn",connectionPool);       
    }
}



3) 커넥션 래퍼 클래스

데이터베이스 커넥션을 관리하는 Interface로 다음 4가지 함수를 정의하였습니다.

db.ConnectionContext.java

package db;


public interface ConnectionContext {
   public java.sql.Connection getConnection();
   public void rollback();
   public void commit();
   public void release();
}


ConectionContext를 구현한 커넥션 클래스

db.ConnectionResource.java

package db;

import java.sql.Connection;
import java.sql.DriverManager;

public class ConnectionResource implements ConnectionContext {
   private Connection connection = null;
   private boolean transaction = false;


   public ConnectionResource() throws Exception {
      init(false);
   }
   
    public ConnectionResource(boolean transaction) throws Exception {
       init(transaction);
    }
   
    public void init(boolean transaction) throws Exception {
       this.transaction = transaction;
       connection = DriverManager.getConnection("jdbc:apache:commons:dbcp:unicorn");
       if (connection == null) throw new Exception("fail to get connection");
    }
     
    public Connection getConnection() {  
       return connection;
    }


    public void rollback() {
       if (transaction) {
          if (connection != null) try { connection.rollback(); } catch (Exception e) {}
      }
   }


   public void commit() {
      if (transaction) {
         if (connection != null) try { connection.commit(); } catch (Exception e) {}
      }
   }


   public void release() {
      if (connection != null) {
         if (transaction) {
            try { connection.setAutoCommit(true); } catch (Exception e) {}
         }
         try { connection.close(); } catch (Exception e) {}
      }
   }
}


4) 테스트 JSP

jsp에서 다음과 같이 사용하면 됩니다.

<%@ page contentType="text/html;charset=EUC_KR" %>
<%@ page import="java.sql.*,db.*" %>

<%

   ConnectionContext connectionContext = new ConnectionResource();
   Connection connection = null;

   try {
      connection = connectionContext.getConnection();

   } catch (Exception exception) {
       exception.printStackTrace();
   } finally {
       if (connection != null) try { connectionContext.release(); } catch (Exception exception) {}
   }

%>

관련 파일들을 첨부합니다 ^^&


IV. Struts와 DBCP

만약 Struts와 함께 사용한다면 다음과 같이 사용하세요. 현재 Unicorn이 다음과 같은 형식으로 되어있습니다.

1) struts-config.xml 에 DBCP 정보를 정의합니다.

 <data-sources>
  <data-source key="mysql" type="org.apache.commons.dbcp.BasicDataSource">
   <set-property property="defaultAutoCommit" value="true"/>
   <set-property property="defaultReadOnly" value="false"/>
   <set-property property="description" value="Tomcat Data Source"/>
   <set-property property="driverClassName" value="org.gjt.mm.mysql.Driver"/>
   <set-property property="maxActive" value="10"/>
   <set-property property="maxIdle" value="30"/>
   <set-property property="maxWait" value="10000"/>
   <set-property property="username" value="unicorn"/>
   <set-property property="password" value="iloveyou"/>
   <set-property property="url" value="jdbc:mysql://localhost/unicorn"/>
  </data-source>
 </data-sources>


2) Action classs에서 다음과 같이 커넥션을 얻어올 수 있습니다.

Connection connection = getDataSource(request, "mysql").getConnection();

이 getDataSource() 함수는 org.apache.struts.action.Action 클래스에 다음과 같이 정의되어 있습니다.

protected DataSource getDataSource(HttpServletRequest request) {
    return getDataSource(request, "org.apache.struts.action.DATA_SOURCE");
}

protected DataSource getDataSource(HttpServletRequest request, String key) {
    ServletContext context = getServlet().getServletContext();
    ModuleConfig moduleConfig = ModuleUtils.getInstance().getModuleConfig(request, context);
    return (DataSource)context.getAttribute(key + moduleConfig.getPrefix());
}


3) Struts에서 트랜잭션 관리를 위한 코딩으로는 unicorn의 소스에 충실히 구현되어 있습니다.

마찬가지로 Interface인 ConnectionContext와 이를 구현한 ConnectionResource를 사용하여 트랜잭션 관리를 하였습니다. 이 예제는 unicorn 소스를 참조하세요 ^^


V. 톰캣에서의 DBCP 사용

1) DBCP에 대한 정보를 server.xml에 정의할 수 있습니다.

<Context path="/unicorn" docBase="unicorn" debug="0"
                 reloadable="true" crossContext="true">
          <Logger className="org.apache.catalina.logger.FileLogger"
                     prefix="localhost_ktng_log." suffix=".txt"
              timestamp="true"/>


    <Resource name="jdbc/unicorn" auth="Container"

                    type="javax.sql.DataSource"/>
    <ResourceParams name="jdbc/unicorn">
       <parameter>
         <name>factory</name>
         <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
       </parameter>
       <parameter>
         <name>username</name>
         <value>unicorn</value>
       </parameter>
       <parameter>
         <name>password</name>
         <value>iloveyou</value>
       </parameter>
       <parameter>
         <name>driverClassName</name>
         <value>org.gjt.mm.mysql.Driver</value>
       </parameter>
       <parameter>
         <name>url</name>
         <value>jdbc:mysql://localhost/unicorn</value>
       </parameter>
       <parameter>
         <name>maxActive</name>
         <value>10</value>
       </parameter>
       <parameter>
         <name>maxIdle</name>
         <value>30</value>
       </parameter>
       <parameter>
         <name>maxWait</name>
         <value>10000</value>
       </parameter>
     </ResourceParams>


2) web.xml에 JNDI 리소스를 사용할 수 있도록 다음을 추가합니다.

<resource-ref>
   <description>unicorn db</description>
   <res-ref-name>jdbc/unicorn</res-ref-name>
   <res-type>javax.sql.DataSource</res-type>
   <res-auth>Container</res-auth>
</resource-ref>

3) 실제 소스에서는 다음과 같이 DataSource를 사용합니다.

InitialContext context = new InitialContext();

DataSource dataSource = (DataSource) initContext.lookup("java:/comp/env/jdbc/unicorn");

Connection connection = dataSource.getConnetion();

간단하죵?


=============================================

본문서는 자유롭게 배포/복사 할수 있지만

이문서의 저자에 대한 언급을 삭제하시면 안됩니다

저자 : GoodBug (unicorn@jakartaproject.com)

최초 : http://www.jakartaproject.com 

=============================================

ConnectionContext.java 175 Bytes
ConnectionInitialize.java 3 KB
ConnectionResource.java 1 KB
web.xml 1 KB
test.jsp 433 Bytes



출처 : http://www.jakartaproject.com/article/jakarta/1111890409958