2013. 7. 30. 10:33


boardpool.jocl

참고 : http://tomcat.apache.org/tomcat-7.0-doc/jdbc-pool.html

       : http://blog.naver.com/chocolleto?Redirect=Log&logNo=30084743153



1. 커넥션 풀 (Connection Pool)
 
○ 정의

- 데이터베이스와 연결된 커넥션을 미리 만들어 풀(pool)이란 저장소에 저장해 두고 있다가 필요할 때에 커넥션 풀에서 가져다 쓰고
   다시 풀에 반환하는 기법.
- 미리 접속을 하여 메모리에 접속 객체를 저장해 두는 기술.

○ 특징

- pool 속에 미리 커넥션이 생성되어 있기 때문에 커넥션을 생성하는데 드는 연결 시간이 소비되지 않음.
   (어플리케이션의 실행속도가 빨라짐.)
- 커넥션을 계속해서 재사용하기 때문에 생성되는 커넥션 수가 많지 않음.
   (동시 접속자수가 몰려도 웹 어플리케이션이 쉽게 다운되지 않음.)

2. DBCP (Database Connection Pool)

○ 정의

- Database Connection Pool을 구현한 것. (미리 접속을 하여 메모리에 접속 객체를 저장해 두는 기술.)
- Commons DBCP : apache에서 개발한 커넥션 풀 기능을 제공하는 API.

○ 특징
- JDBC의 Connection을 생성하고 사용하지 않는 Connection을 관리하여 Connection의 재사용을 높일 수 있음.

○ 처리 과정

1) DBCP 관련 jar 파일 및 JDBC 드라이버 jar 파일 설치.
2) 커넥션 풀 관련 설정 파일 초기화.
3) 커넥션 풀 관련 드라이버 로딩.
4) 커넥션 풀로부터 커넥션 사용.

○ Web Application에서 DBCP 설정
    
■ 필요한 jar 파일 복사

○ jar 파일

- jakarta commons DBCP
- jakarta commons Collections (Pool이 사용)
- jakarta commons Pool (DBCP가 사용)
(jakarta : http://jakarta-k.sourceforge.net/)      

○ 다운로드

○ 파일 위치

- /WEB-INF/lib 폴더에 위치.

■ 커넥션 풀 설정 파일 작성.
      
○ 특징

- 커넥션 풀에 대한 설정 파일. 
- 파일명 : pool.jocl (파일 이름은 사용자가 지정.)
      
○ 설정 파일 위치

- DBCP API는 클래스패스로부터 설정 파일을 읽어오므로 클래스패스에 위치해 있어야 함.
  (/WEB-INF/classes 폴더 혹은 /build/classes 폴더)

○ boardpool.jocl

 

<object class = "org.apache.commons.dbcp.PoolableConnectionFactory"
    xmlns = "http://apache.org/xml/xmlns/jakarta/commons/jocl">
   
    <!-- mysql 접속. -->
    <object class = "org.apache.commons.dbcp.DriverManagerConnectionFactory">
        <string value = "jdbc:mysql://localhost:3306/board" />
        <string value = "root" />
        <string value = "1111" />
  </object>

  <object class = "org.apache.commons.pool.impl.GenericObjectPool">
    <object class ="org.apache.commons.pool.PoolableObjectFactory" null="true" />
        <int value  =  "10"/>  <!-- maxActive -->
        <byte value = "1"/>  <!-- whenExhaustedAction -->
        <long value = "10000"/>  <!-- maxWait -->
        <int value  = "10"/>  <!-- maxIdle -->     
        <int value  = "3"/>  <!-- minIdle -->     
        <boolean value = "true"/>  <!-- testOnBorrow -->
        <boolean value = "true"/>  <!-- testOnReturn -->
        <long value    = "600000"/>  <!-- timeBetweenEvictionRunsMillis -->
        <int value = "5"/>  <!-- numTestsPerEvictionRun -->
        <long value = "3600000"/>  <!-- minEvictableIdleTimeMillis -->
        <boolean value ="true"/>  <!-- testWhileIdle -->
    </object>

    <object class = "org.apache.commons.pool.impl.GenericKeyedObjectPoolFactory" null="true" />
    <string null = "true" />
    <boolean value = "false" />
     <boolean value = "true" />
</object>


- 주석을 지우고 실행할 것.
- 대소문자 확실히 구분.
        
● GenericObjectPool

- 커넥션 풀의 속성과 관련된 부분.
- API : http://commons.apache.org/pool/api-1.6/index.html
          
□ 속성

속성설명
maxActive커넥션 풀이 제공할 최대 커넥션 개수
whenExhaustedAction커넥션 풀에서 가져올 수 있는 커넥션이 없을 때 어떻게 동작할지를 지정한다. 1일 경우 maxWait 속성에서 지정한 시간만큼 커넥션을 구할 때 까지 기다리며, 0일 경우 에러를 발생시킨다. 2일 경우에는 일시적으로 커넥션을 생성해서 사용한다.
maxWaitwhenExhaustedAction 속성의 값이 1일 때 사용되는 대기 시간. 단위는 1/1000초이며, 0 보다 작을 경우 무한히 대기한다.
maxIdle사용되지 않고 풀에 저장될 수 있는 최대 커넥션 개수. 음수일 경우 제한이 없다.
minIdle사용되지 않고 풀에 저장될 수 있는 최소 커넥션 개수.
testOnBorrowtrue일 경우 커넥션 풀에서 커넥션을 가져올 때 커넥션이 유효한지의 여부를 검사한다.
testOnReturntrue일 경우 커넥션 풀에 커넥션을 반환할 때 커넥션이 유효한지의 여부를 검사한다.
timeBetweenEvctionRunsMillis사용되지 않은 커넥션을 추출하는 쓰레드의 실행 주기를 지정한다. 양수가 아닐 경우 실행되지 않는다. 단위는 1/1000 초이다.
numTestsPerEvictionRun사용되지 않는 커넥션을 몇 개 검사할지 지정한다.
minEvictableIdleTimeMillis사용되지 않는 커넥션을 추출할 때 이 속성에서 지정한 시간 이상 비활성화 상태인 커넥션만 추출한다. 양수가 아닌 경우 비활성화된 시간으로는 풀에서 제거되지 않는다. 시간 단위는 1/1000초이다.
testWhileIdletrue일 경우 비활성화 커넥션을 추출할 때 커넥션이 유효한지의 여부를 검사해서 유효하지 않은 커넥션은 풀에서 제거한다.

 

□ 속성 지정시 고려사항


■ maxActive

- 사이트의 최대 커넥션 사용량을 기준으로 지정.
- 동시 접속자 수에 따라서 지정.
            

■ minIdle
- 사용되지 않는 커넥션의 최소 개수를 0으로 지정하면 풀에 저장된 커넥션의 개수가 0이 될 수 있으며, 이 경우 커넥션이 필요

   할때 다시 커넥션을 생성하게 됨. (최고 개수는 5개 정도로 지정.)
            

■ timeBetweenEvctionRunsMillis
- 이 값을 알맞게 지정해서 사용되지 않는 커넥션을 풀에서 제거하는 것이 좋음.
- 커넥션의 동시 사용량은 보통 새벽에 최저이며, 낮 시간대에 최대.
  (최대 상태에 접어 들었다가 최소 상태로 가게 되면 풀에서 사용되지 않는 커넥션의 개수가 점차 증가.)

- 사용되지 않는 커넥션은 일정 시간 후에 삭제 되도록 하는 것이 좋음. 
  (보통 10 ~20분 단위로 사용되지 않는 커넥션을 검사하도록 지정하는 것이 좋음.)
      

■ testWhileIdle
- 사용되지 않는 커넥션을 검사할 때 유효하지 않은 커넥션은 검사하는 것이 좋음.

■ 드라이버 로드 프로그램 작성
      

○ HttpServlet을 상속받아 초기화.

- 서블릿이 초기에 실행될 때 실행되는 init() 메소드에 작성.
- jsp 페이지에서 DB를 사용하는 부분을 작성할 때 유용. (서버가 실행될 때 한 번만 실행되기 때문에.)
- 자바 파일을 작성한 다음 서블릿 설정파일인 web.xml에 반드시 추가.
        

● 예제


/* DBConInit.java */


package test.connection;

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

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

public class DBConInit extends HttpServlet {
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Class.forName("org.apache.commons.dbcp.PoolingDriver");
            System.out.println("Driver connection success");
        }catch(Exception e) {
            throw new ServletException(e);
        }
        
    }

} 


/* web.xml */

[출처] | commons | DBCP|작성자 외계인셩




    Board
    
        index.html
    

    
        DBConInit
        test.connection.DBConInit
        1 
    

 


/* dbcpTest.jsp */

[출처] | commons | DBCP|작성자 외계인셩


<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>


<%@ page import="java.sql.*"%>

<%
    Connection con = null;

    con = DriverManager.getConnection("jdbc:apache:commons:dbcp:boardpool");
    
    if(con == null) {
        out.print("connection is fail.");
    } else {
        out.print("connection is success");
        con.close();
    }
    
%>


[출처] | commons | DBCP|작성자 외계인셩





 DAO 사용 프로그램 작성



package com.chocolleto.board.db;

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


/**
 * DB Connection 하는 Singleton class.
 * 
 * @author Administrator
 * 
 */
public class DBUtils {

    private static final String DBCP_POOLING_DRIVER = "org.apache.commons.dbcp.PoolingDriver";
    private static final String MYSQL_JDBC_DRIVER = "com.mysql.jdbc.Driver";
    private static final String JDBC_URI = "jdbc:apache:commons:dbcp:/boardpool";
    
    // 자기 스스로에 대한 인스턴스 생성. (생성자가 private이기 때문.)
    private static volatile DBUtils instance = null;

    private DBUtils() throws Exception {
        try {
            Class.forName(MYSQL_JDBC_DRIVER);
            Class.forName(DBCP_POOLING_DRIVER);
        } catch (Exception e) {
            throw new Exception("Failed to create JDBC drivers.", e);
        }
    }

    public static DBUtils getInstance() throws Exception {
        if (instance == null) {
            // 클래스를 생성하려면 클래스 자체에 대해 접근해야하므로 동기화를 걸어줌.
            synchronized (DBUtils.class) {
                if (instance == null) {
                    instance = new DBUtils();
                }
            }
        }
        return instance;
    }
    
    public Connection getConnection() throws SQLException {
        return DriverManager.getConnection(JDBC_URI);
    }
} 

/* UserDAO.java */

[출처] | commons | DBCP|작성자 외계인셩


package com.chocolleto.board.user;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;

import com.chocolleto.board.db.DBUtils;
import com.chocolleto.board.user.model.User;

/**
 * @author Administrator
 * 
 */
public class UserDAO {
    private DBUtils db;
    // private Logger log = Logger.getLogger(UserDAO.class);
    
    public UserDAO() throws Exception {
        // DB를 관리하는 객체의 인스턴스를 얻어온다.
        db = DBUtils.getInstance();
    }

    /**
     * 사용자 등록
     * 
     * @param user
     * @throws SQLException
     */
    public void insertUser(User user) throws SQLException {

        String sql = "insert into users(userid, password, nickname, birthday, email)"
                + "values(?,?,?,?,?)";

        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
        String birth;
        if (user.getBirthday() == null) {
            birth = null;
        } else {
            birth = dateformat.format(user.getBirthday());
        }

        Connection con = null;
        try {
            con = db.getConnection();
            PreparedStatement st = con.prepareStatement(sql);

            st.setString(1, user.getUserid());
            st.setString(2, user.getPassword());
            st.setString(3, user.getNickname());
            st.setString(4, birth);
            st.setString(5, user.getEmail());

            st.executeUpdate();
            st.close();
        } catch (SQLException e) {
            throw e;
        } finally {
            if (con != null) {
                try {
                    // 컨넥션은 익셉션이 발생하더라도 항상 close() 될수 있도록 finally 에서 처리한다
                    con.close(); // 실제 컨넥션을 close하는게 아니라 가지고 있던 MySQL의 컨넥션을 풀에 반환.
                } catch (SQLException e) {
                    // 컨넥션을 닫을때 생기는 문제는 무시한다
                }
            }
        }

    }

    /**
     * 사용자 정보 수정
     * 
     * @param user
     * @throws SQLException
     */
    public void updateUser(User user) throws SQLException {

        String sql = "update users set password=?,nickname=?,birthday=?,email=?"
                + " where userid=?";

        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
        String birth;
        if (user.getBirthday() == null) {
            birth = null;
        } else {
            birth = dateformat.format(user.getBirthday());
        }

        Connection con = null;
        try {
            con = db.getConnection();
            PreparedStatement st = con.prepareStatement(sql);

            st.setString(1, user.getPassword());
            st.setString(2, user.getNickname());
            st.setString(3, birth);
            st.setString(4, user.getEmail());
            st.setString(5, user.getUserid());

            st.executeUpdate();
            st.close();
        } catch (SQLException e) {
            throw e;
        } finally {
            if (con != null) {
                try {
                    // 컨넥션은 익셉션이 발생하더라도 항상 close() 될수 있도록 finally 에서 처리한다
                    con.close();
                } catch (SQLException e) {
                    // 컨넥션을 닫을때 생기는 문제는 무시한다
                }
            }
        }
    }

    /**
     * 사용자 삭제
     * 
     * @param userid
     * @throws SQLException
     */
    public void deleteUser(String userid) throws SQLException {

        String sql = "delete from users where userid=?";
        Connection con = null;
        try {
            con = db.getConnection();
            PreparedStatement st = con.prepareStatement(sql);

            st.setString(1, userid);

            st.executeUpdate();
            st.close();
        } catch (SQLException e) {
            throw e;
        } finally {
            if (con != null) {
                try {
                    // 컨넥션은 익셉션이 발생하더라도 항상 close() 될수 있도록 finally 에서 처리한다
                    con.close();
                } catch (SQLException e) {
                    // 컨넥션을 닫을때 생기는 문제는 무시한다
                }
            }
        }
    }

    /**
     * 사용자 검색.
     * 
     * @param userid
     * @param password
     * @throws SQLException
     */
    public User selectUser(String id) throws SQLException {

        String sql = "select * from users where userid=?";
        Connection con = null;
        User user = null;
        try {
            con = db.getConnection();
            PreparedStatement st = con.prepareStatement(sql);

            st.setString(1, id);

            ResultSet rs = st.executeQuery();

            if (!rs.next()) {
                // 검색된 내용이 없으면 회원이 아니므로 null을 반환.
                rs.close();
                st.close();
            } else {
                // 검색된 내용이 있으면 검색된 내용을 user에 저장.
                user = new User();
                user.setUserid(rs.getString("userid"));
                user.setPassword(rs.getString("password"));
                user.setNickname(rs.getString("nickname"));
                user.setBirthday(rs.getDate("birthday"));
                user.setEmail(rs.getString("email"));
            }

            rs.close();
            st.close();
        } catch (SQLException e) {
            throw e;
        } finally {
            if (con != null) {
                try {
                    // 컨넥션은 익셉션이 발생하더라도 항상 close() 될수 있도록 finally 에서 처리한다
                    con.close();
                } catch (SQLException e) {
                    // 컨넥션을 닫을때 생기는 문제는 무시한다
                }
            }
        }

        return user;
    }
}



'DBMS' 카테고리의 다른 글

Virtual Hosting and Tomcat (가상 호스팅과 톰캣) - (직역)  (0) 2013.07.30
JNDI 톰캣 구성의 예(직역)  (0) 2013.07.30
Posted by 1+1은?