커넥션풀을 사용한다면 첫 요청시에만 직접적인 연결을 하고
그 다음부터는 커넥션풀에 저장되어 있는 커넥션을 사용한다.
그렇다면 직접적인 연결을 할 때 어떤 코드가 실행되길래 시간이 오래걸리고
커넥션풀을 사용하면 금방되는지 알아보자.
Connection connection= DriverManager.getConnection("myurl");
Connection connection= DriverManager.getConnection("myurl","myid","mypass");
@CallerSensitive
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, Reflection.getCallerClass()));
}
DriverManger.getConnection("url) , DriverManager.getConnection("url","id","pw") 전부
id나 pw 정보를 가지고
DriverManager.getConnection(url,info,Reflection.getCallerClass())) 메소드를 실행한다.
DriverManager.getConnection(url,info,Reflection.getCallerClass()))
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
/*
* When callerCl is null, we should check the application's
* (which is invoking this class indirectly)
* classloader, so that the JDBC driver class outside rt.jar
* can be loaded from here.
*/
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
// Walk through the loaded registeredDrivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
오라클 드라이버로 직접 연결
DriverManager.getConnection(url,info,Reflection.getCallerClass()))에서
주목해야할 부분은 for문이다.
여러분의 코드에서 Class.forName("오라클드라이버") 가 있었다면
registeredDrivers에는 오라클드라이버가 있을것입니다.
for문을 반복하다가 오라클드라이버가 있는 경우
Connection con = aDriver.driver.connect(url, info)입니다.
여기선 오라클드라이버로 진행하기 때문에 오라클드라이버에서 정의한
connect(url,info) 메소드가 실행되겠네요.
※ Class.forName() 실행시 드라이버 등록이 됩니다. 자세한내용은 링크 참고
https://brilliantdevelop.tistory.com/54
OracleDriver.class의 connect메소드입니다.
connect메소드가 여러개 지만 내부적으로는 아래의 connect메소드를 호출합니다.
public Connection connect(String var1, Properties var2, GSSCredential var3, SSLContext var4, Supplier<? extends AccessToken> var5) throws SQLException {
if (var1 == null) {
throw (SQLException)DatabaseError.createSqlException(296).fillInStackTrace();
} else {
if (var1.regionMatches(0, "jdbc:default:connection", 0, 23)) {
String var6 = "jdbc:oracle:kprb";
int var7 = var1.length();
if (var7 > 23) {
var1 = var6.concat(var1.substring(23, var1.length()));
} else {
var1 = var6.concat(":");
}
var6 = null;
}
int var14 = oracleDriverExtensionTypeFromURL(var1);
if (var14 == -2) {
return null;
} else if (var14 == -3) {
throw (SQLException)DatabaseError.createSqlException(67).fillInStackTrace();
} else if (var14 != 0 && var3 != null) {
throw (SQLException)DatabaseError.createUnsupportedFeatureSqlException().fillInStackTrace();
} else {
OracleDriverExtension var15 = null;
var15 = this.driverExtensions[var14];
if (var15 == null) {
try {
synchronized(this) {
if (var15 == null) {
var15 = (OracleDriverExtension)Class.forName(driverExtensionClassNames[var14]).newInstance();
this.driverExtensions[var14] = var15;
} else {
var15 = this.driverExtensions[var14];
}
}
} catch (Exception var13) {
}
}
if (var2 == null) {
var2 = new Properties();
}
if (!var1.matches("jdbc:oracle:(thin|oci|oci8|kprb):\\w*/?\\w*@(//)?[A-z0-9-._]+(:\\d+)[:/][A-z0-9-._:]+")) {
EZConnectResolver var8 = EZConnectResolver.newInstance(var1);
var2.putAll(var8.getProperties());
if (var8.getResolvedUrl() != null) {
var1 = var8.getResolvedUrl();
}
}
Enumeration var16 = DriverManager.getDrivers();
final Driver var9;
while(var16.hasMoreElements()) {
var9 = (Driver)var16.nextElement();
if (var9 instanceof OracleDriver) {
break;
}
}
while(var16.hasMoreElements()) {
var9 = (Driver)var16.nextElement();
if (var9 instanceof OracleDriver) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws SQLException {
DriverManager.deregisterDriver(var9);
return null;
}
});
} catch (PrivilegedActionException var11) {
throw (SQLException)var11.getException();
}
}
}
var9 = null;
Object var17;
if (var14 == 2 && var2.containsKey("connection_pool") && var2.getProperty("connection_pool").equals("connection_pool")) {
var17 = (PhysicalConnection)var15.getConnection(var1, var2);
((PhysicalConnection)var17).protocolId = var14;
} else {
String var10 = null;
if (var2 != null) {
var10 = var2.getProperty("oracle.jdbc.sqlTranslationProfile");
}
if (var10 == null) {
var10 = PhysicalConnection.getSystemPropertySqlTranslationProfile();
}
if (var10 != null) {
var17 = this.babelfishConnect(var2, var10, var1, var15, var14);
} else {
if (var14 == 0) {
var17 = (PhysicalConnection)var15.getConnection(var1, var2, var3, var4, var5);
} else {
var17 = (PhysicalConnection)var15.getConnection(var1, var2);
}
((PhysicalConnection)var17).protocolId = var14;
}
}
return (Connection)var17;
}
}
}
여러과정이 있지만 실제로 return 하는것은 Connection타입의 객체 var17입니다.
이 var17에 값이 할당되는건 var15의 getConnection()메소드입니다.
var15는 T2CDriverExtension이고 이 클래스의 getConnection() 메소드를 봅시다.
T2CDriverExtension.getConnection()
@DefaultLevel(Logging.FINEST)
Connection getConnection(String var1, Properties var2) throws SQLException {
Object var3 = null;
if (var2.getProperty("is_connection_pooling") == "true") {
var3 = new OracleOCIConnection(var1, var2, this);
} else {
var3 = new T2CConnection(var1, var2, this);
}
((T2CConnection)var3).connect((GSSCredential)null, (SSLContext)null, (Supplier)null);
return (Connection)var3;
}
여기서 또 T2CConection의 connect 메소드가 실행되는걸 알 수 있는데요
T2CConection은 abstract 클래스이므로 구현체인 PhysicalConnection의
connect() 메소드를 봅시다.
PhysicialConnection.connect()
참고로 여기서부터는 메소드가 getConnection이 아니라 connect()입니다.
실제로 연결을 하겠다는 것이지요.
void connect(GSSCredential var1, SSLContext var2, Supplier<? extends AccessToken> var3) throws SQLException {
try {
this.lifecycle = 1;
this.needLine();
if (this.isDRCPConnection(this.url)) {
this.drcpEnabled = true;
if (this.drcpConnectionClass != null) {
this.drcpConnectionClass = this.drcpConnectionClass.trim();
}
}
this.logon(var1, var2, var3);
this.setAutoCommit(this.autocommit);
this.setDriverSpecificAutoCommit(this.autocommit);
this.versionDependentInit(this.getVersionNumber());
if (this.implicitStatementCacheSize > 0) {
this.setStatementCacheSize(this.implicitStatementCacheSize);
this.setImplicitCachingEnabled(true);
}
if (this.fanEnabled) {
HAManager.enableHAIfNecessary(this.url, this);
}
} catch (SQLException var7) {
this.lifecycle = 2;
try {
this.logoff();
} catch (SQLException var6) {
}
this.lifecycle = 4;
throw var7;
}
this.txnMode = 0;
}
여기서 대부분의 설정을 하고
this.logon()의 코드를 보면 (abstract메소드입니다. 구현체는 T2Connection의 logon().)
byte를 사용해 통신하는 걸 알 수있습니다.
(약200줄가량의 코드 + 여러 메소드 호출 ==> 대략 1000줄)
이렇게 byte를 사용해 통신하는 과정에서 많은 시간이 걸리게 되는데
connection pool을 사용하지 않는다면
DB에 접속할 때 마다 이와같은 일을 반복하게 됩니다.
커넥션 풀을 사용하는 경우
위의 DrvierManger의 코드를 다시보면
DriverManager.getConnection(url,info,Reflection.getCallerClass()))에서
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
........
for(DriverInfo aDriver : registeredDrivers) {
// If the caller does not have permission to load the driver then
// skip it.
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
.......
}
}
for문을 반복하다가 Connection con = aDriver.driver.connect(url, info) 부분이 실행된다.
오라클드라이버를 사용하는 경우 OracleDriver의 connect메소드가 실행되지만,
Connection pool을 사용할 경우 PoolingDriver의 connect메소드가 실행된다.
PoolingDriver의 connect()
public Connection connect(String url, Properties info) throws SQLException {
if (this.acceptsURL(url)) {
ObjectPool<? extends Connection> pool = this.getConnectionPool(url.substring(URL_PREFIX_LEN));
try {
Connection conn = (Connection)pool.borrowObject();
return conn == null ? null : new PoolGuardConnectionWrapper(pool, conn);
} catch (NoSuchElementException var5) {
throw new SQLException("Cannot get a connection, pool error: " + var5.getMessage(), var5);
} catch (RuntimeException | SQLException var6) {
throw var6;
} catch (Exception var7) {
throw new SQLException("Cannot get a connection, general error: " + var7.getMessage(), var7);
}
} else {
return null;
}
}
ObjectPool<? extends Connection> pool
= this.getConnectionPool(url.substring(URL_PREFIX_LEN))가 실행된다.
this.getConnectionPool()은 아래와 같다.
public synchronized ObjectPool<? extends Connection> getConnectionPool(String name) throws SQLException {
ObjectPool<? extends Connection> pool = (ObjectPool)pools.get(name);
if (null == pool) {
throw new SQLException("Pool not registered: " + name);
} else {
return pool;
}
}
여기서 pools는 단순한 HashMap이다.
즉, 커넥션풀을 사용하면 단순한 HashMap에 Pool 을 저장하고
요청이 올 때 마다 HashMap에서 Pool을 꺼내고 get ()
꺼낸 Pool에서 Connection을 얻는다. borrowObject()
이렇게 커넥션풀은 단순히 HashMap에 는 Pool( Connection을 갖고있는)을 저장해놓기 때문에
빠르게 Connection 객체를 얻을 수 있다.