서론 (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 방식으로 진행한다는 건데 이런 용법은 처음보아 다른 코드에 유용하게 활용할 수 있을 것 같다.

 

+ Recent posts