티스토리 뷰

지난 포스팅에서 DBA를 설명하면서, 하나의 Connection 객체가 여러 작업을 수행할 때, 각 작업마다 Connection 객체를 생성하는 대신에 Singleton 패턴을 이용하여 하나의 Connection 객체를 가지고 작업하는 방법을 실습해 봤다면, 

이번엔 생성된 Connection 객체와 해당 객체의 사용 여부를 따로 저장하는 서브 클래스를 생성하여 여러 명의 사용자가 각각 다른 작업을 수행할 때 사용할 Connection들을 모아 다루는, 좀 더 심화된 DBA 코드를 알아보려고 한다.


코드가 살짝 길긴한데, 전체적인 맥락을 이해한다면 그렇게 복잡하게만 느껴지는 코드는 아니다. 

 

Connection 객체를 여러개 만들어 동시에 사용하기 위한 코드이기 때문에, 역시 가장 중요한 것은 각각의 Connection이 얽히거나 충돌하지 않고, 사용이 끝난 후에도 자원해제가 안되어서 서버에 부담을 주는 지를 잘 체크하며 Connection pool을 관리하는 것이다. 

즉, 사용이 끝난 Connection은 바로 바로 자원 할당을 해제하여야 한다는 것이다.

그렇게 하기 위해서 Connection과 Connection의 사용 여부를 boolean으로 가지고 있는 서브 클래스를 만들어 준 뒤, 이 서브 클래스들을 저장할 자료 구조를 하나 선언하여 따로 사용해야 한다.

 

코드는 다음과 같다. (좀 길긴 하다...ㅎ)


본 코드 맨 밑에 선언 해놓아서... 읽기 힘들까봐 우선 확인부터 하면, DBA 내에 들어갈 서브 클래스는 다음과 같다. 

/** Connection 객체와 그 객체의 사용 여부를 저장할 수 있는 서브 클래스 선언 * */
class ConnectionObject {
	public Connection connection = null;
	public boolean  inuse = false;
	
	public ConnectionObject(Connection c, boolean useFlag) {
		connection = c;
		inuse = useFlag;
	}
}

◎DBA(ConnectionMgr.java)

package com.choonham.mall;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import java.util.Vector;

public class DBConnectionMgr {
	
	private Vector<ConnectionObject> connections = new Vector<>(10);
	private String _driver = "oracle.jdbc.driver.OracleDriver";
	private String _url = "jdbc:oracle:thin:@127.0.0.1:1521:XE";
	private String _user = "shop_nyj";
	private String _pwd = "6725";
	
	private boolean _traceOn = false; //사용 가능 여부
	private boolean initialize = false; // 초기화 여부
	private int _openConnections = 50; //최대 connection
	
	private static DBConnectionMgr instance = null; // 싱글톤 패턴을 사용하기 위한 인스턴스
	
	private DBConnectionMgr() {
	}
	
	public static DBConnectionMgr getInstance() {
		if(instance == null) {
			instance = new DBConnectionMgr();
		}
		return instance;
	}
	
	/** 열려있는 Connection 객체의 수 설정 **/
	public void setOpenConnectionCount(int count) {
		_openConnections = count;
		
	}
	
	/** 사용 가능 여부 설정 **/
	public void setEnableTrace(boolean enable) {
		_traceOn = enable;
	}
	
	
	/** connections Vector 를 반환 **/
	public Vector<ConnectionObject> getConnectionList(){
		return connections;
	}
	
	/** 현재 열려있는 Connection 객체의 수를 반환 **/
	public int getConnectionCount() {
		return connections.size();
	}
	
	/** 새로운 Connection 객체 생성 **/
	private Connection createConnection() {
		Connection conn = null;
		
		try {
			if(_user == null) {
				_user = "";
			}
			if(_pwd == null) {
				_pwd = "";
			}
			
			Properties props = new Properties();
			props.put("user", _user);
			props.put("password",  _pwd);
			
			conn = DriverManager.getConnection(_url, props);
			
		} catch(Exception e) {
			e.printStackTrace();
		}
		
		return conn;
		
	}
	
	/** 모든 연결을 닫고, 연결 pool(vector) 내 모든 내용을 삭제 **/
	public void finalize() {
		ConnectionObject conns = null;
		
		for(int i = 0; i<connections.size(); i++) {
			conns = (ConnectionObject)connections.elementAt(i);
			try {
				conns.connection.close();
			} catch(Exception e) {
				e.printStackTrace();
			}
			conns = null;
		}
		
		connections.removeAllElements();
	}
	
	/** 현재 사용되지 않지만, 연결되어 있는 Connection 자원 해제 **/
	public void releaseFreeConnections() {
		ConnectionObject conns = null;
		for(int i = 0; i < connections.size(); i++) {
			conns = (ConnectionObject)connections.elementAt(i);
			if(!conns.inuse) {
				removeConnection(conns.connection);
			}
		}
	}
	
	/** Connection 객체 제거 **/
	public void removeConnection(Connection c) {
		if(c==null) return;
		ConnectionObject conns = null;
		for(int i = 0; i < connections.size(); i++) {
			conns = (ConnectionObject)connections.elementAt(i);
			if(c == conns.connection) {
				try {
					c.close();
					connections.removeElementAt(i);
				} catch(Exception e) {
					e.printStackTrace();
				}
				break;
			}
		}
	}
	
	/** PreparedStatement, Statement, ResultSet 등 다양한 자원 해제 메서드(오버라이딩) **/
	public void freeConnection(PreparedStatement pstmt, ResultSet rs) {
		try {
			if(rs != null) rs.close();
			if(pstmt != null) pstmt.close();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void freeConnection(Statement stmt, ResultSet rs) {
		try {
			if(rs != null) rs.close();
			if(stmt != null) stmt.close();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void freeConnection(Statement stmt) {
		try {
			if(stmt != null) stmt.close();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public void freeConnection(PreparedStatement pstmt) {
		try {
			if(pstmt != null) pstmt.close();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	/** 새로운 Connection 객체를 반환 **/
	public Connection getConnection() {
		Connection conn = null;
		ConnectionObject conns = null;
		
		if(!initialize) {
			try {
				Class.forName(_driver);
				conn = DriverManager.getConnection(_url, _user, _pwd);
			} catch(Exception e) {
				System.err.print(e);
			}
		}
		
		boolean badConnection = false;
		
		for(int i=0; i<connections.size(); i++) {
			conns = (ConnectionObject)connections.elementAt(i);
			
			// 연결이 유효한 지 테스트
			if(!conns.inuse) {
				try {
					badConnection = conns.connection.isClosed();
					
					if(!badConnection) {
						badConnection = (conns.connection.getWarnings() != null); // getWarnings(): 경고 발생(true), else(false)
					}
				} catch(Exception e) {
					System.err.print(e);
				}
				if(badConnection) {
					connections.removeElementAt(i);
					continue;
				}
				
				conn = conns.connection;
				conns.inuse = true;
				break;				
			} 
			//연결이 유효한 지 테스트
			
			if(conn == null) {
				conn = createConnection();
				conns = new ConnectionObject(conn, true);
				connections.addElement(conns);
			}
		}
		
		return conn;
		
	}

}

/** Connection 객체와 그 객체의 사용 여부를 저장할 수 있는 서브 클래스 선언 * */
class ConnectionObject {
	public Connection connection = null;
	public boolean  inuse = false;
	
	public ConnectionObject(Connection c, boolean useFlag) {
		connection = c;
		inuse = useFlag;
	}
}

좀 길고 복잡하게 느껴질 수 있으나, 이렇게 하지 않으면 서버가 진짜 시간 단위로 터지기 때문에 잘 익혀두고 사용하도록 하자!

Comments