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

 

현재 Arc browser를 이용해 번역을 사용하려고 하면 영어로만 번역하려고 한다. 다음 절차를 통해 한국어로 번역하도록 수정 할 수 있다.

 

1. 언어 설정 페이지 이동

command + L 을 입력하고 arc://settings/lanugages 를 입력한다.

2. 한국어 추가

Add languages 버튼 클릭 후 다음과 같이 한국어를 추가한다.

3. 번역 언어 설정

아래와 같이 한국어를 번역어로 설정한다.

4. 확인

아래와 같이 한국어로 번역대상이 설정된 경우 성공이다.

서론 (Introduction)

QueryDSL은 ORM을 사용하다 보면 언젠가는 사용하게 될 기술 중 하나입니다. 이를 설정하는 법을 구글링해보면 표준적인 설정 방법이 없기 때문에 각기 다른 방법을 제시하고 그만큼 설정에 에러를 많이 겪는 경우가 보입니다. 여기에서는 자바 버전에 따라 가장 간단하게 QueryDSL을 설정하는 방법을 알아보고, 각 설정의 의미에 대해서 서술해보도록 하겠습니다.

본문 (Body)


간단한 QueryDSL 원리

설정에 대해 알아보기전, 간단하게 QueryDSL의 원리에 대해 알면 설정을 이해하기 쉽습니다. 만약, 빠르게 설정만 하고 싶다면 아래 (QueryDSL 설정)를 바로 참고하시기 바랍니다.

 

QueryDSL은 "쿼리를 안전하고 쉽게 하기 위한 도메인 특화언어(DSL, Domain Specific Language)"입니다. 이를 위해선, 기존 자바코드를 바탕으로 JPA, MongoDB 같은 도메인에 맞는 언어를 생성하는 과정이 필요합니다. QueryDSL은 다음 과정을 통해 도메인 언어를 생성합니다.

 

1. compileJava 태스크를 실행합니다.

2. @Entity와 같이 쿼리와 관련된 어노테이션과 클래스를 스캔합니다.

3. 스캔한 클래스를 활용해 도메인에 맞는 언어를 생성합니다. (ex. Student.class ->  QStudent.class)

 

이제 개발자는 QueryDSL이 생성한 언어를 이용해 자바언어를 토대로 쿼리를 작성할 수 있습니다.


QueryDSL 설정

QueryDSL은 다음 코드를 build.gradle 파일에 추가해 설정할 수 있습니다.

dependencies{
    //Java EE를 사용하는 경우, (javax 패키지를 참조할 수 있는 경우)
    implementation 'com.querydsl:querydsl-jpa:5.0.0'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'
    annotationProcessor 'javax.persistence:javax.persistence-api:2.2.0'

    
    //Jakarta EE를 사용하는 경우, (jakarta 패키지를 참조할 수 있는 경우)
    implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
    annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jakarta'
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'
    
 }
 
 ...
 
 task deleteGenerated(type: Delete) {
    delete "src/main/generated"
}

clean.dependsOn deleteGenerated

 

각 설정의 이유에 대해서 설명해보겠습니다. 

 

먼저, 아래 의존성은 queryDSL에서 쿼리 기능을 구현하는데 필요한 클래스를 불러오기 위해 사용됩니다.

jakarta 의 경우 기존에 javax.persistence.* 로 선언된 패키지들이  jakarta.persistence.* 로 변경되었기 때문에, 올바른 참조를 위해 추가로 선언한 필요가 있습니다.

implementation 'com.querydsl:querydsl-jpa:5.0.0'

annotationProcessor는 Gradle에서 컴파일 타임에 어노테이션을 처리해 코드를 생성 또는 검증하는데 사용하기 위한 키워드입니다. APT는 Annotation Processing Tool의 약자입니다. 즉, querydsl에서 어노테이션 처리를 위한 기능을 가져오기 위해 사용됩니다.

이 또한, jakarta 의 경우 기존에 javax.persistence.* 로 선언된 패키지들이  jakarta.persistence.* 로 변경되었기 때문에, 올바른 참조를 위해 추가로 선언한 필요가 있습니다.

annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa'

@Entity와 같은 기본 어노테이션들은 자바에서 표준적으로 정의되어 있습니다. 따라서 각 환경에 따라 필요한 정의를 annotationProcessor를 사용해 정의할 필요가 있습니다.

 

annotationProcessor 'javax.persistence:javax.persistence-api:2.2.0'
annotationProcessor 'jakarta.persistence:jakarta.persistence-api:3.1.0'


QueryDSL 사용

위 설정이 올바르게 완료되면, compileJava 라는 태스크를 실행할 수 있습니다. ./gradlew compileJava 커맨드를 이용해 태스크를 실행하면, 위에서 설명한 DSL 생성과정이 진행됩니다.

2. @Entity와 같이 쿼리와 관련된 어노테이션과 클래스를 스캔합니다.
3. 스캔한 클래스를 활용해 도메인에 맞는 언어를 생성합니다. (ex. Student.class ->  QStudent.class)

그럼 이제 queryDSL 문법에 따라 설정이 가능합니다!

 

 

서론 (Introduction)

JPA를 사용하면 대표적인 구현체인 HIbernate를 사용하게 됩니다. HIbernate는 SQL 쿼리를 생성하주는 기능을 가지고 있는데, MySQL, Oracle 등 데이터베이스 마다 문법이 조금씩 다르기 때문에 Dialect(방언)을 설정해 주어야합니다. 이번 글에서는 Oracle 데이터베이스를 위한 Dialect 설정에 대해서 다루어보도록 하겠습니다.

 

본문 (Body)

a. org.hibernate.dialect

Hibernate에서는 데이터베이스 방언들을 org.hibernate.dialect 패키지에 정리하고 있습니다. 이 패키지의 내용은 hibernate 버전이 변경되면서 조금씩 바뀌어왔습니다. 여기에서는 이전 버전과 현재 버전의 dialect 적용에 대해서 알아보도록 하겠습니다.


B. 구버전의 Dialect

dialect 관련된 내용을 검색하면, 아래와 같이 데이터베이스 버전 별로 dialect를 설정하는 방법에 대한 질문이 많습니다. 답변을 보면 oracle 데이터베이스의 경우 버전 별로 Oracle10gDialect, Oracle12cDialect dialect를 설정하라고 하고 있습니다. 하지만 현재 hibernate 최근 버전(6.2.9-Final)에서는 이를 사용할 경우 에러가 발생합니다.


C. 최근 버전의 Dialect

최근 버전의 Dialect에서는 버전별로 각각의 Dialect를 사용하는 대신 통합된 Dialect를 사용하려는 경향을 보이고 있습니다. 따라서 이전에 사용된 버전별 Dialect는 Deprecated 처리되어 있는 것을 찾을 수 있습니다.

 따라서 OracleDialect를 사용하기 위해선 hibernate.dialect를 다음과 같이 설정해야합니다.

spring:
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.OracleDialect

결론 (Conclusion)

평소에 개발하듯이 모르는게 생기면 구글링을 했는데, 이번에는 예전 버전을 기준으로 작성된 답변이나 블로그 글이 많아 개발을 하는데 많이 해매게 되었습니다. 역시 가장 확실한 건 직접 코드를 확인하는 것인 것 같습니다. 다른 분들은 저처럼 잘못된 답변으로 해매지말고 이 글로 명쾌하게 hibernate.dialect 를 설정할 수 있기를 바랍니다.

 

참조(Reference)

1. https://docs.jboss.org/hibernate/orm/6.3/userguide/html_single/Hibernate_User_Guide.html#database-dialect

+ Recent posts