3 minute read

dbcp

DB접속 시 마다 커넥션 객체를 받아와야 하는 문제가 있는데 이것을 해결하기 위한 방법이 dbcp이다. pool에 미리 Connection 객체를 저장해 둔 후 필요할 때 바로 가져다 쓰고 사용 후 반납하면 매번 DB연결을 수립하고 해제하는 과정을 줄일 수 있다. DBCP 는 JDBC 1단계 & 2단계에 관여하므로 3단계부터는 작업이 동일하다.

dbcp를 활용한 DB 연동

0단계

META-INF/context.xml 파일 생성 및 커넥션풀 정보를 설정한다.

<?xml version="1.0" encoding="UTF-8"?>

<Context>
	<Resource 
		name="jdbc/MySQL"
		auth="Container"
		type="javax.sql.DataSource"
		driverClassName="com.mysql.cj.jdbc.Driver"
		url="jdbc:mysql://localhost:3306/jsp3"
		username="root"
		password="1234"
		maxTotal="100"
	/>
</Context>

* 표시가 붙은 항목은 값이 변경될 수 있는 항목이다.

  1. name 속성(*) : 공유할 리소스 이름
    DB 작업 수행 코드(JdbcUtil 클래스 등)에서 DBCP API 를 통해 불러올 때 지정할 이름이다.
  2. auth 속성 : 자원 관리를 수행할 대상(인증 대상) 지정
    톰캣이 컨테이너 역할을 통해 수행하므로 “Container” 지정(고정)한다.
  3. type 속성 : 서버상에서 리소스(Connection 객체) 사용 시 해당 리소스를 관리할 객체(클래스)를 지정한다.
    javax.sql.DataSource 인터페이스 타입 지정
    name 속성에 지정된 이름을 통해 DBCP 접근 시 DataSource 타입 객체가 리턴된다.
  4. driverClassName 속성(*) : JDBC 드라이버 클래스 지정(드라이버 API 파일에 해당하는 jar 파일 등록 필수!)
    ex) MySQL 의 경우 “com.mysql.cj.jdbc.Driver”
    ex) Oracle 의 경우 “com.jdbc.OracleDriver”
  5. url 속성(*) : JDBC 접속에 필요한 URL 정보 지정
    ex) MySQL 의 경우 “jdbc:mysql://접속주소:포트번호/DB명”
    ex) Oracle 의 경우 “jdbc:oracle:thin:@접속주소:포트번호:DB명”
  6. username 속성(*) : DB 접속 계정명
  7. password 속성(*) : DB 접속 패스워드
  8. maxTotal 속성 : 동시에 생성(활성화)해 놓을 Connection 객체 갯수(생략 가능)

1단계

톰캣으로부터 관리되는 커넥션 풀(context.xml 파일의 태그)을 객체 형태로 가져온다. javax.naming.InitialContext 객체 생성하여 javax.naming.Context 타입으로 업캐스팅(형변환)하면 된다.

Context initCtx = new InitialContext();

2단계

생성된 Context 객체(initCtx)로부터 context.xml 파일 내의 <Context> 태그 내의 <Resource> 태그 부분을 객체 형태로 가져온다.
Context(initCtx) 객체의 lookup() 메서드를 호출하여 찾아올 리소스 지정하면 된다.

  • 파라미터 : “java:comp/env” 문자열
  • 리턴타입 : Object -> Context 타입 다운캐스팅
Context envCtx = (Context)initCtx.lookup("java:comp/env");

3단계

<Resource> 태그가 복수개 있을 경우(= 관리하는 커넥션풀이 여러개) 각 리소스 구분을 위해 지정한 이름(JNDI)을 사용하여 원하는 리소스 선택하여 가져온다.
Context(envCtx) 객체의 lookup() 메서드를 호출하여 찾아올 리소스를 지정하면 된다.

  • 파라미터 : 리소스 이름
    Resource 태그에 설정된 name 속성값
    주의! context.xml 파일 내의 지정된 name 속성값이 다를 수 있으므로 확인 필수!
  • 리턴타입 : Object -> javax.sql.DataSource 타입 다운캐스팅
DataSource ds = (DataSource)envCtx.lookup("jdbc/MySQL");

2,3단계 통합

1번 과정의 Context 객체의 lookup() 메서드를 호출하여 2번 과정과 3번 과정의 문자열을 결합하여 전달할 수 있다. DataSource 타입으로 리턴받는다.

DataSource ds = (DataSource)initCtx.lookup("java:comp/env/jdbc/MySQL");

4단계

커넥션풀을 관리하는 DataSource 객체로부터 미리 생성된 Connection 객체를 가져온다. DataSource 객체의 getConnection() 메서드를 호출하여 Connection 객체를 리턴받으면 된다.

  • 파라미터 : 없음
  • 리턴타입 : java.sql.Connection
Connection con = ds.getConnection();

클래스로 분리하기

Context initCtx = new InitialContext();
DataSource ds = (DataSource)initCtx.lookup("java:comp/env/jdbc/MySQL");
Connection con = ds.getConnection();

위의 코드를 매번 DB 작업시마다 작성할 경우 코드 중복으로 인해 유지 보수가 어려워지고 코드 작성량도 증가하게 된다. 따라서, 별도의 클래스를 통해 메서드 형태로 정의한 후 메서드 호출을 통해 Connection 객체만 리턴받으면 코드 중복 제거가 가능하게 된다.

Java Resources/src/main/java/ 위치에 패키지를 만들고 클래스를 정의한다. 예시로 jsp12_dbcp 패키지에 JdbcUtil 클래스를 정의 후 getConnection() 메서드 정의 후 호출해보자.

  • 파라미터 : 없음
  • 리턴타입 : java.sql.Connection
package jsp12_dbcp;

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

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class JdbcUtil_backup {
	public Connection getConnection() {
		Connection con = null;
		
		try {
			Context initCtx = new InitialContext(); // NamingException
			Context envCtx = (Context)initCtx.lookup("java:comp/env"); // NamingException
			DataSource ds = (DataSource)envCtx.lookup("jdbc/MySQL"); // NamingException
			con = ds.getConnection(); // SQLException
		} catch (NamingException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return con;
	}
}
JdbcUtil jdbcUtil = new JdbcUtil();
Connection con = jdbcUtil.getConnection();

이제 커넥션 객체를 가져오는 쪽에선 이렇게 코드를 작성하기만 하면 된다. 하지만 여기서 하나 아쉬운 점은 JdbcUtil 클래스는 멤버변수가 존재하지 않으므로 메서드를 호출할 때마다 매번 인스턴스를 생성할 필요가 없다. 즉, 메모리의 낭비이므로 클래스를 프로그램 시작(서버 시작) 시 메모리에 로딩시켜 놓고 클래스에 정의된 메서드를 호출하여 사용하면 이 문제를 해결할 수 있다. 정적메서드로 만들자는 말이다. 위의 getConnection메소드를 static으로 변경하기만 하면 된다.

Connection con = JdbcUtil.getConnection();

이제 인스턴스 생성없이 바로 객체를 가져오면 된다.

Leave a comment