서론 (Introduction)
Java에서 JDBC를 이용해 MySQL에 연결하는 방법을 알아보고, MySQL 드라이버의 연결과정에 대해서 알아보자
본문 (Body)
1. MySQL을 실행한다.
아래 Docker Compose를 이용하면 localhost:3306을 통해서 mysql에 접속이 가능하다. 물론 도커가 아니라 직접 설치해도 된다.
services:
db:
image: mysql:8.2.0
command: --default-authentication-plugin=mysql_native_password
restart: always
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: testDB
ports:
- 3306:3306
2. gradle 종속성에 mysql JDBC connector 라이브러리를 추가한다.
이렇게 하면 어플리케이션을 시작할 때, MySQL JDBC 라이브러리의 클래스가 등록된다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
runtimeOnly 'com.mysql:mysql-connector-j:8.2.0'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
2. 테스트 코드 작성
데이터베이스 연결 테스트를 위한 코드를 작성한다.
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@SpringBootTest
public class ConnectionTest {
@Test
void test(){
Connection connection=null;
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/testDB", "root", "example");
Class<? extends Connection> aClass = connection.getClass();
System.out.println("connection = " + connection);
System.out.println("aClass = " + aClass);
} catch (SQLException e) {
throw new RuntimeException(e);
}
finally {
if(connection!=null){
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
}
3. 결과
실행 결과를 보면, 커넥션 인스턴스가 생성되었고 그 클래스는 com.mysql.cj.jdbc.ConnectionImpl 인 것을 볼 수 있다.
connection = com.mysql.cj.jdbc.ConnectionImpl@1f916219
aClass = class com.mysql.cj.jdbc.ConnectionImpl
4. 분석
DriverManager.getConnection는 다음과 같이 정의되어있다.
@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()));
}
아래 getConnection을 좀 더 들어가보면 registeredDrivers에 있는 드라이버들에 접속 시도를 하는 것을 볼 수 있다. 여기까지 보면 java.sql.connection.DriverManager는 자신한테 등록되어 있는 드라이버들에서 하나씩 조회 시도를 하고 성공한 경우를 반환하는 것을 볼 수 있다. 그렇다면 MySQL 드라이버는 언제 등록되는 걸까?
private static Connection getConnection(
//...
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.driver.getClass().getName());
}
}
//...
}
아래 com.mysql.cj.jdbc.Driver 파일을 보면 static 선언을 통해서 클래스 로드시 자기 자신을 등록하고 있는 모습을 볼 수 있다. 즉, gradle에 라이브러리를 추가하고 코드를 실행시킬 때 MySQL 드라이버가 java.sql.DriverManager에 등록되며, DriverManager.getConnection 호출 시 MySQL 드라이버를 가져와 연결을 시도할 수 있다.
```java
package com.mysql.cj.jdbc;
import java.sql.SQLException;
/**
* The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface. * * <p>
* The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to
* connect to the target URL. * * <p>
* It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast
* quantities of supporting code. * * <p>
* When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a
* driver by doing Class.forName("foo.bar.Driver"). */
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// Register ourselves with the DriverManager.
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
/**
* Construct a new driver and register it with DriverManager * * @throws SQLException
* if a database error occurs. */ public Driver() throws SQLException {
// Required for Class.forName().newInstance().
}
}
결론 (Conclusion)
지금까지 JDBC를 이용해 MySQ에 접속하는 과정에 대해서 알아보았다. 뜻밖의 수확으로, MySQL에서 드라이버를 등록할 때 static 방식으로 진행한다는 건데 이런 용법은 처음보아 다른 코드에 유용하게 활용할 수 있을 것 같다.