데이터베이스 연결 탱크 의 JAVA 구현 (2 편) - JAVA 의 동적 에이 전 트 를 사용 하여 데이터베이스 연결 탱크 구현
데이터베이스 연결 탱크 는 응용 서 비 스 를 작성 하 는 데 자주 사용 되 는 모듈 입 니 다. 데이터 베 이 스 를 너무 자주 연결 하 는 것 은 서비스 성능 에 있어 병목 입 니 다. 버퍼 기술 을 사용 하면 이 병목 을 없 앨 수 있 습 니 다.우 리 는 인터넷 에서 데이터베이스 연결 탱크 에 관 한 소스 프로그램 을 많이 찾 을 수 있 지만 이러한 공 통 된 문 제 를 발견 했다. 이런 연결 탱크 의 실현 방법 은 사용자 와 의 결합 도 를 어느 정도 증가 시 켰 다.많은 연결 탱크 는 사용자 가 규정된 방법 을 통 해 데이터 베 이 스 를 연결 하 라 고 요구 한 다 는 점 을 이해 할 수 있다. 왜냐하면 현재 모든 응용 서버 가 데이터 베 이 스 를 연결 하 는 방식 은 이런 방식 으로 이 루어 진 것 이기 때문이다.그러나 또 다른 공 통 된 문 제 는 사용자 가 Connection. close () 방법 을 명시 적 으로 호출 하 는 것 을 허용 하지 않 고 정 해진 방법 으로 연결 을 닫 아야 한 다 는 것 이다.이런 방법 은 두 가지 단점 이 있다.
첫째, 사용자 의 사용 습관 을 바 꾸 고 사용자 의 사용 난이 도 를 증가 시 켰 다.
우선 정상 적 인 데이터베이스 조작 과정 을 살 펴 보 자.
int executeSQL(String sql) throws SQLException
{
Connection conn = getConnection(); //
PreparedStatement ps = null;
int res = 0;
try{
ps = conn.prepareStatement(sql);
res = ps.executeUpdate();
}finally{
try{
ps.close();
}catch(Exception e){}
try{
conn.close();//
}catch(Exception e){}
}
return res;
}
사용 자 는 데이터 베 이 스 를 연결 한 후에 연결 방법 close 를 직접 호출 하여 데이터 베이스 자원 을 방출 합 니 다. 만약 에 우리 가 앞에서 언급 한 연결 탱크 의 실현 방법 을 사용 하면 그 문 구 는 conn. close () 가 특정한 문구 로 대 체 됩 니 다.
둘째: 연결 탱크 가 그 중의 모든 연결 을 독점 적 으로 제어 할 수 없 게 한다.연결 탱크 는 사용자 가 직접 연결 하 는 close 방법 을 호출 할 수 없 기 때문에 사용자 가 사용 하 는 과정 에서 습관 적 인 문제 로 데이터베이스 연결 을 직접 닫 으 면 연결 탱크 는 모든 연결 상 태 를 정상적으로 유지 하지 못 하고 연결 탱크 와 응용 이 서로 다른 개발 자 에 의 해 이 루어 질 때 이런 문제 가 발생 하기 쉽다.
위 에서 언급 한 두 가지 문 제 를 종합 하여 우 리 는 이 두 가지 죽 을 문 제 를 어떻게 해결 할 것 인 가 를 토론 합 시다.
우선 우 리 는 먼저 사용자 가 이 데이터 베 이 스 를 어떻게 사용 하려 고 하 는 지 를 고려 해 보 자.사용 자 는 특정한 방법 을 통 해 데이터 베 이 스 를 연결 할 수 있 으 며, 이 연결 의 유형 은 표준 자바. sql. connection 이 어야 합 니 다.사용 자 는 이 데이터베이스 연결 을 가 져 온 후에 이 연결 을 닫 는 등 임의로 조작 할 수 있 습 니 다.
사용자 가 사용 하 는 설명 을 통 해 Connection. close 방법 을 어떻게 연결 할 수 있 는 지 하 는 것 이 우리 글 의 주제 가 되 었 습 니 다.
데이터 베 이 스 를 연결 하 는 close 방법 을 연결 하기 위해 서 는 갈고리 와 유사 한 메커니즘 이 있어 야 한다.예 를 들 어 윈도 프로 그래 밍 에서 우 리 는 훅 API 를 이용 하여 특정한 윈도 API 에 대한 연결 을 실현 할 수 있다.JAVA 에 도 이런 메커니즘 이 있다.JAVA 는 프 록 시 클래스 와 Invocation Handler 를 제공 합 니 다. 이 두 종 류 는 모두 자바. lang. reflect 패키지 에 있 습 니 다.우선 썬 이 제공 한 문서 가 이 두 가 지 를 어떻게 묘사 하 는 지 살 펴 보 자.
public interface InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler.
When a method is invoked on a proxy instance,
the method invocation is encoded and dispatched to the invoke method of its invocation handler.
썬 의 API 문서 에는 프 록 시 에 대한 설명 이 많아 나열 되 지 않 습 니 다.인터페이스 InvocationHandler 에 대한 문 서 를 통 해 Proxy 인 스 턴 스 를 호출 하 는 방법 을 볼 수 있 습 니 다. Invocationhanlder 의 invoke 방법 을 실행 합 니 다.JAVA 의 문서 에서 우 리 는 이러한 동적 에이전트 가 인터페이스 만 연결 할 수 있 는 방법 을 동시에 알 게 되 었 고 일반적인 클래스 에 대해 서 는 유효 하지 않다 는 것 을 알 게 되 었 다. 자바. sql. connection 자체 도 하나의 인터페이스 임 을 감안 하여 close 방법 을 어떻게 연결 하 는 지 해결 하 는 길 을 찾 았 다.
먼저, 우 리 는 데이터베이스 연결 탱크 파라미터 의 클래스 를 정의 하고 데이터베이스 의 JDBC 드라이버 클래스, 연 결 된 URL 과 사용자 이름 암호 등 일부 정 보 를 정의 합 니 다. 이 클래스 는 연결 탱크 를 초기 화 하 는 데 사용 되 는 매개 변수 입 니 다. 구체 적 인 정 의 는 다음 과 같 습 니 다.
public class ConnectionParam implements Serializable
{
private String driver; //
private String url; // URL
private String user; //
private String password; //
private int minConnection = 0; //
private int maxConnection = 50; //
private long timeoutValue = 600000;//
private long waitTime = 30000; //
그 다음은 연결 탱크 의 공장 류 Connection Factory 입 니 다. 이 종 류 를 통 해 연결 탱크 의 대상 을 하나의 이름 과 대응 시 킵 니 다. 사용 자 는 이 이름 을 통 해 지정 한 연결 탱크 의 대상 을 얻 을 수 있 습 니 다. 구체 적 인 코드 는 다음 과 같 습 니 다.
/**
* ,
* @author liusoft
*/
public class ConnectionFactory
{
//
static Hashtable connectionPools = null;
static{
connectionPools = new Hashtable(2,0.75F);
}
/**
*
* @param dataSource
* @return DataSource
* @throws NameNotFoundException
*/
public static DataSource lookup(String dataSource)
throws NameNotFoundException
{
Object ds = null;
ds = connectionPools.get(dataSource);
if(ds == null || !(ds instanceof DataSource))
throw new NameNotFoundException(dataSource);
return (DataSource)ds;
}
/**
*
* @param name
* @param param , ConnectionParam
* @return DataSource
* @throws NameAlreadyBoundException name
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws SQLException
*/
public static DataSource bind(String name, ConnectionParam param)
throws NameAlreadyBoundException,ClassNotFoundException,
IllegalAccessException,InstantiationException,SQLException
{
DataSourceImpl source = null;
try{
lookup(name);
throw new NameAlreadyBoundException(name);
}catch(NameNotFoundException e){
source = new DataSourceImpl(param);
source.initConnection();
connectionPools.put(name, source);
}
return source;
}
/**
*
* @param name
* @param param , ConnectionParam
* @return DataSource
* @throws NameAlreadyBoundException name
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws SQLException
*/
public static DataSource rebind(String name, ConnectionParam param)
throws NameAlreadyBoundException,ClassNotFoundException,
IllegalAccessException,InstantiationException,SQLException
{
try{
unbind(name);
}catch(Exception e){}
return bind(name, param);
}
/**
*
* @param name
* @throws NameNotFoundException
*/
public static void unbind(String name) throws NameNotFoundException
{
DataSource dataSource = lookup(name);
if(dataSource instanceof DataSourceImpl){
DataSourceImpl dsi = (DataSourceImpl)dataSource;
try{
dsi.stop();
dsi.close();
}catch(Exception e){
}finally{
dsi = null;
}
}
connectionPools.remove(name);
}
}
Connection Factory 는 사용자 가 연결 탱크 를 구체 적 인 이름 에 연결 하고 연결 을 취소 하 는 동작 을 제공 합 니 다.사용 자 는 이 두 가지 유형 에 만 관심 을 가지 면 데이터베이스 연결 탱크 의 기능 을 사용 할 수 있다.연결 탱크 를 어떻게 사용 하 는 지 알려 드 리 겠 습 니 다.
String name = "pool";
String driver = " sun.jdbc.odbc.JdbcOdbcDriver ";
String url = "jdbc:odbc:datasource";
ConnectionParam param = new ConnectionParam(driver,url,null,null);
param.setMinConnection(1);
param.setMaxConnection(5);
param.setTimeoutValue(20000);
ConnectionFactory.bind(name, param);
System.out.println("bind datasource ok.");
// ,
//
DataSource ds = ConnectionFactory.lookup(name);
try{
for(int i=0;i<10;i++){
Connection conn = ds.getConnection();
try{
testSQL(conn, sql);
}finally{
try{
conn.close();
}catch(Exception e){}
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
ConnectionFactory.unbind(name);
System.out.println("unbind datasource ok.");
System.exit(0);
}
사용자 의 예제 코드 를 통 해 알 수 있 듯 이 우 리 는 일반적인 연결 탱크 에서 발생 하 는 두 가지 문 제 를 해결 했다.하지만 우리 가 가장 관심 을 가 지 는 것 은 close 방법 을 어떻게 해결 하 느 냐 하 는 것 이다.연결 작업 은 주로 Connection Factory 의 두 마디 코드 입 니 다.
source = new DataSourceImpl(param);
source.initConnection();
DataSourceImpl 은 인터페이스 javax. sql. dataSource 를 실현 하 는 클래스 로 연결 탱크 를 유지 하 는 대상 입 니 다.이 종 류 는 보 호 받 는 클래스 이기 때문에 사용자 에 게 노출 되 는 방법 은 인터페이스 DataSource 에서 정의 하 는 방법 만 있 고 다른 모든 방법 은 사용자 에 게 볼 수 없다.사용자 가 접근 할 수 있 는 방법 에 관심 을 가 져 보 겠 습 니 다. getConnection
/**
* @see javax.sql.DataSource#getConnection(String,String)
*/
public Connection getConnection(String user, String password) throws SQLException
{
//
Connection conn = getFreeConnection(0);
if(conn == null){
// ,
// ,
if(getConnectionCount() >= connParam.getMaxConnection())
conn = getFreeConnection(connParam.getWaitTime());
else{// ,
connParam.setUser(user);
connParam.setPassword(password);
Connection conn2 = DriverManager.getConnection(connParam.getUrl(),
user, password);
//
_Connection _conn = new _Connection(conn2,true);
synchronized(conns){
conns.add(_conn);
}
conn = _conn.getConnection();
}
}
return conn;
}
/**
*
* @param nTimeout 0 null
* nTimeout ,
* @return Connection
* @throws SQLException
*/
protected synchronized Connection getFreeConnection(long nTimeout)
throws SQLException
{
Connection conn = null;
Iterator iter = conns.iterator();
while(iter.hasNext()){
_Connection _conn = (_Connection)iter.next();
if(!_conn.isInUse()){
conn = _conn.getConnection();
_conn.setInUse(true);
break;
}
}
if(conn == null && nTimeout > 0){
// nTimeout
try{
Thread.sleep(nTimeout);
}catch(Exception e){}
conn = getFreeConnection(0);
if(conn == null)
throw new SQLException(" ");
}
return conn;
}
DataSourceImpl 클래스 에서 getConnection 방법 을 실현 하 는 것 은 정상 적 인 데이터 베이스 연결 탱크 의 논리 와 일치 합 니 다. 먼저 남 은 연결 여 부 를 판단 하고 없 으 면 연결 수가 최대 연결 수 를 초과 하 는 지 등 일부 논 리 를 판단 합 니 다.그러나 한 가지 다른 것 은 DriverManager 를 통 해 얻 은 데이터베이스 연결 이 제때에 돌아 오 는 것 이 아니 라 라 는 것 이다.Connection 의 클래스 를 중개 하고 호출Connection. getConnection 이 돌 아 왔 습 니 다.만약 우리 가 돌아 올 인터페이스 대상 을 중개, 즉 JAVA 의 Proxy 를 통 해 인수 하지 않 았 다 면, 우 리 는 Connection. close 방법 을 막 을 방법 이 없 었 을 것 이다.
드디어 핵심 이 왔 군. 우리 먼저 보 자Connection 이 어떻게 실현 되 었 는 지, 그리고 클 라 이언 트 가 Connection. close 방법 을 호출 할 때 어떤 절 차 를 밟 았 는 지, 왜 진정 으로 연결 을 닫 지 않 았 는 지 소개 합 니 다.
/**
* , close
* @author Liudong
*/
class _Connection implements InvocationHandler
{
private final static String CLOSE_METHOD_NAME = "close";
private Connection conn = null;
//
private boolean inUse = false;
//
private long lastAccessTime = System.currentTimeMillis();
_Connection(Connection conn, boolean inUse){
this.conn = conn;
this.inUse = inUse;
}
/**
* Returns the conn.
* @return Connection
*/
public Connection getConnection() {
// conn , close
Connection conn2 = (Connection)Proxy.newProxyInstance(
conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(),this);
return conn2;
}
/**
*
* @throws SQLException
*/
void close() throws SQLException{
// conn , close
conn.close();
}
/**
* Returns the inUse.
* @return boolean
*/
public boolean isInUse() {
return inUse;
}
/**
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
*/
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object obj = null;
// close , close
if(CLOSE_METHOD_NAME.equals(m.getName()))
setInUse(false);
else
obj = m.invoke(conn, args);
// ,
lastAccessTime = System.currentTimeMillis();
return obj;
}
/**
* Returns the lastAccessTime.
* @return long
*/
public long getLastAccessTime() {
return lastAccessTime;
}
/**
* Sets the inUse.
* @param inUse The inUse to set
*/
public void setInUse(boolean inUse) {
this.inUse = inUse;
}
}
사용자 가 연 결 된 close 방법 을 호출 하면 사용자 의 연결 대상 이 연결 을 거 친 대상 이기 때문에 JAVA 가상 기 회 는 먼저 호출Connection. invoke 방법 은 이 방법 에서 close 방법 인지 여 부 를 먼저 판단 하고 그렇지 않 으 면 코드 를 연결 되 지 않 은 연결 대상 conn 에 전달 합 니 다.그렇지 않 으 면 이 연결 상 태 를 간단하게 사용 할 수 있 도록 설정 할 수 있 습 니 다.이로써 당신 은 전체 인수 과정 을 알 게 될 것 입 니 다. 그러나 동시에 의문 도 있 습 니 다. 그러면 이미 만들어 진 연결 들 이 진정 으로 닫 을 수 없 는 것 이 아 닙 니까?답 은 돼.Connection Factory. unbind 방법 을 살 펴 보 겠 습 니 다. 이 방법 은 먼저 이름 에 해당 하 는 연결 풀 대상 을 찾 은 다음 이 연결 풀 의 모든 연결 을 닫 고 연결 풀 을 삭제 합 니 다.DataSourceImpl 클래스 에서 모든 연결 을 닫 는 close 방법 을 정 의 했 습 니 다. 자세 한 코드 는 다음 과 같 습 니 다.
/**
*
* @return int
* @throws SQLException
*/
public int close() throws SQLException
{
int cc = 0;
SQLException excp = null;
Iterator iter = conns.iterator();
while(iter.hasNext()){
try{
((_Connection)iter.next()).close();
cc ++;
}catch(Exception e){
if(e instanceof SQLException)
excp = (SQLException)e;
}
}
if(excp != null)
throw excp;
return cc;
}
이 방법 은 연결 탱크 의 모든 대상 을 연결 하 는 close 방법 을 일일이 호출 합 니 다. 이 close 방법 은 에 대응 합 니 다.Connection 에서 close 에 대한 실현 은Connection 정의 에서 데이터베이스 연결 을 닫 을 때 연결 되 지 않 은 대상 을 직접 호출 하 는 닫 는 방법 이기 때문에 이 close 방법 은 데이터베이스 자원 을 진정 으로 방출 합 니 다.
상기 문 자 는 인터페이스 방법의 연결 만 묘 사 했 을 뿐 구체 적 으로 실 용적 인 연결 탱크 모듈 은 남 은 연결 에 대한 모니터링 과 연결 을 제때에 풀 어야 합 니 다. 상세 한 코드 는 첨부 파일 을 참조 하 십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.