JPA 3, Hibernate 6, queryDSL 5 프로젝트 설정법 (Java 17 사용)

식빵·2023년 8월 20일
1

JPA 이론 및 실습

목록 보기
4/17
post-thumbnail

🆙 JPA 의 버전업

JPA 2.2 에서 JPA 3 으로 버전업이 되면서 내부의 패키지 명칭이 javax.* 에서
jakarta.* 로 바뀌었고, 명칭 또한 Java Persistence API 에서
Jakarta Persistence API 로 바뀌게 되었습니다.

이런 변화로 인해서 기존 JPA 2.2 을 베이스로 Java 프로젝트를
생성하던 방식과 JPA 3.0 버전의 생성 방식에 차이가 생겼습니다.

패지키 명칭이 바뀐 이유가 궁금하다면 이 링크를 참조하세요.


🥅 글의 목표

이 글은 현재 많은 블로그들에 기재되어 있는 기존 JPA 2.2 방식의 프로젝트 생성법이 아닌,
JPA 3 방식의 프로젝트를 생성하는 방법을 알아내는 것이 이 글의 목표입니다.

이 글에서는 많은 라이브러리를 maven 으로 추가하는데,
이때 사용되는 라이브러리들의 버전들은 글을 작성할 당시의 가장 최신
Spring Boot 인 Spring Boot 3.1.2 에서 사용되는 dependency version 을 참고했습니다. 또한 사용하는 JDK 버전도 Spring Boot 3 에서 요구하는 최소 버전인 17을 사용했습니다.

혹시라도 아래 과정이 다 귀찮은 분들이 있다면 그냥 제 Git Repository 에 있는
프로젝트를 clone 받아서 하셔도 좋습니다.

https://github.com/CodingToastBread/jpa3-hibernate6-querydsl5

이 프로젝트에서는 h2 2.2.224 버전 및 jdk 21 을 사용한다는 점만 주의하시면 됩니다!



🥝 JPA 3 + Hibernate 6 프로젝트

참고링크: 프로젝트 세팅 완성본 - 깃 리포지토리

혹여 세팅이 완료된 프로젝트 구조를 먼저 빠르게 보고 싶다면 제 깃 리포지토리
오셔서 clone 을 받아 보셔도 됩니다.

참고: 필자의 개발환경
IDE: IntelliJ Ultimate
OS : Window 10 (Home Edition)
DB : H2 Database (v1.4.200) (설치하는 방법)
JDK : 17 (Spring Boot 3 의 최소 JDK 버전을 고려함)



1. 프로젝트 생성

  • Intellij 에서 새로운 New Project 를 생성해줍니다.
  • 위의 빨간색 네모 친 부분만 저랑 같게 해주시면 됩니다.
    • Langauge : Java
    • Build System : Maven
    • JDK : 17 버전, Vendor 는 상관없음

2. pom.xml

프로젝트 생성 직후 pom.xml 은 아래와 같이 생겼습니다.

이제 이 파일을 수정하여 dependency 를 추가해보겠습니다. 아래처럼
pom.xml<properties> 태그 바로 아래에 <dependencies> 태그를 넣어줍니다.

<properties>
	<maven.compiler.source>17</maven.compiler.source>
	<maven.compiler.target>17</maven.compiler.target>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>


<!-- 추가된 태그 -->
<dependencies>
    <!-- Spring Boot 3.1.2 의 Version Dependecy 참고 -->
    <!-- 필수 라이브러리 -->
    <dependency>
        <groupId>org.hibernate.orm</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>6.2.6.Final</version>
    </dependency>
  
    <!-- Spring Boot 3.1.2 의 Version Dependecy 참고 -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-hikaricp</artifactId>
        <version>6.2.6.Final</version>
    </dependency>
  
    <!-- Spring Boot 3.1.2 의 Version Dependecy 참고 -->
    <!-- slf4j-api 2.0.7 버전과 호환 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.8</version>
    </dependency>
  
    <!-- 자신이 설치한 h2 데이터베이스의 버전과 통일 -->
    <!-- 필수 라이브러리. (DB Client 는 당연히 필수임) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.200</version>
    </dependency>
  
    <!-- 롬복은 그냥 최신 버전으로 함 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.28</version>
    </dependency>
</dependencies>

각 의존성에 대한 설명:

  • hibernate-core: JPA 3.1 API + Hibernate Core 가 내장된 라이브러리
  • hibernate-hikaricp : hibernate ormHickariCp 를 적용
  • h2 : H2 DB 사용을 위한 것. version 은 반드시 자신이 설치한 h2 와 일치시킬 것!
  • logback-classic : 로그를 이쁘게 출력하기 위함
  • lombok : 개발편의성을 위해 추가

위 의존성 중에서 hibernate-coredb-client (여기서는 h2)만
필수입니다. 그외 것들은 필수적인 것은 아닙니다.

이후에 화면 상단 우측에 작게 생긴 Load Maven Changes 버틍르
클릭 해줍니다. (아래 그림참고)



3. META-INF/persistence.xml

resource 디렉토리 내부에...
1. META-INF 디렉토리를 생성하고,
2. 그 안에 persistence.xml 파일을 생성합니다.


그리고 내용을 아래처럼 채워줍니다.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence version="3.0" xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence
             https://jakarta.ee/xml/ns/persistence/persistence_3_0.xsd">
  
    <persistence-unit name="my-unit">
        <!-- https://jakarta.ee/specifications/persistence/3.0/jakarta-persistence-spec-3.0#a12384 참고 -->
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="jakarta.persistence.jdbc.url"    value="jdbc:h2:tcp://localhost/~/test" />
            <property name="jakarta.persistence.jdbc.user"   value="sa" />
            <property name="jakarta.persistence.jdbc.password" value="sa" />
          
            <!-- ( 이것도 세팅하고 싶으면 하셔도 됩니다. 필수는 아닙니다. )
            <property name="jakarta.persistence.lock.timeout" value="100"/>
            <property name="jakarta.persistence.query.timeout" value="100"/>
			-->

            <property name="hibernate.dialect"    value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.show_sql"   value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.use_sql_comments" value="true" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.physical_naming_strategy" value="org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy"/>
            <property name="hibernate.jdbc.batch_size" value="10"/>
        </properties>
    </persistence-unit>
</persistence>
  • 각 프로퍼티의 의미를 설명하진 않겠습니다. 구글링 부탁드립니다.
  • 다만 jdbc.url, jdbc.password, jdbc.url 은 자신의 환경에 맞게 세팅해주세요.
  • 그리고 hibernate.physical_naming_strategy 세팅은 추천사항입니다.
    • 이건 Java 에서 camelCase 작성되어 있어도, DB Object 들과 매칭시킬 때는
      snake_case 로 변환시켜서 서로 매칭시키기 위한 설정입니다.
    • ex: (Java) userNm(DB) user_nm
    • 이런 동작 방식은 Spring Boot 에서의 기본 전략이기도 합니다.



4. logback.xml

로그가 이쁘게 찍히도록 logback.xml 을 작성하겠습니다.
먼저 resource 디렉토리 하단에 logback.xml 파일을 생성합니다.

그리고 아래처럼 내용을 작성해줍니다.
(추후에 로그가 별로 안 이쁘다고 생각되면 자기 입맛에 맞게 수정하시기 바랍니다)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta(%-4relative) --- [ %thread{10} ]
    %cyan(%logger{20}) : %msg%n
            </pattern>
        </encoder>
    </appender>

    <logger name="org.hibernate.SQL" level="info"/>
    
    <!-- hibernate 5 버전에서는 org.hibernate.type.descriptor.sql 였는데 -->
    <!-- hibernate 6 버전부터는 org.hibernate.orm.jdbc.bind 로 변경되었습니다. -->
    <logger name="org.hibernate.orm.jdbc.bind" level="TRACE"/>

    <root level="info">
        <appender-ref ref="CONSOLE"/>
    </root>

</configuration>



5. 테스트 실행 클래스 생성

테스트를 위한 main 메소드를 갖는 Class 를 생성합니다.

package coding.toast;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

public class MainClass {
	public static void main(String[] args) {
    
    	// persistence.xml 에 작성된 
        // <persistence-unit name="my-unit"> 의 name 을
        // Persistence.createEntityManagerFactory 메소드의 인자로 전달합니다.
		try (EntityManagerFactory emf = Persistence.createEntityManagerFactory("my-unit");
		     EntityManager em = emf.createEntityManager()) {
			
			EntityTransaction tx = em.getTransaction();
			tx.begin();
			
			try {
		
				// 여기에 자기가 테스트해 보고 싶은 코드를 작성합니다.
		
				tx.commit();
			} catch (Exception e) {
				tx.rollback();
				throw new RuntimeException(e);
			}
		}
	}
}



🥝 JPA 3 + QueryDSL 적용

QueryDSL 5.0.0 을 적용해보겠습니다.

이 버전을 사용한 이유는 앞에서도 잠깐 말했지만, spring boot 에서 지정한 버전과 동일하게 하고 싶어서 그런겁니다. (참고링크).

참고로 이 방법은 querydsl 공식 문서의 방식으로하면 계속 에러가 나서
제가 검색해보고 찾아낸 방법이며, 가장 심플한 방법이기도 합니다.

1. pom.xml 에 의존성 추가

이전에 수정했던 pom.xml 에 이어서 의존성을 추가하겠습니다.
pom.xml 파일에 가서 <dependencies> 태그 안에 아래와 같이
2가지 의존성을 추가해줍니다.

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
    <classifier>jakarta</classifier>
</dependency>

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>5.0.0</version>
    <classifier>jakarta</classifier>
</dependency>
  • classifier 를 반드시 jakarta 로 지정해줘야 합니다!

이러면 pom.xml 세팅은 끝입니다.

참고: 기존에 JPA2.2 를 쓰시던 분들을 위한 보충 설명


"어라? Annotation Processor 는 등록 안하나?" ... 라고 생각하실 수 있습니다.

위의 의존성 2개만 있다면 안하는 게 맞습니다. 하면 에러나요.

이게 가능한 이유는 querydsl-api:jakarta:5.0.0 의존성을 사용하는 경우에는
해당 라이브러리 안에 이미 AnnotationProcessor 와 관련된 내용이 포함되어 있기
때문입니다. (참고 링크)


이로 인해서 결과적으로 target/generated-sources/annotations 폴더 안에
annotation processing 의 처리 결과물, 즉 Q 클래스들이 생성됩니다.
이후에는 저희는 소스상에서 그 Q 클래스를 사용하면 됩니다.


2. maven reload

의존성을 추가한 후에는 꼭 아래처럼 순서대로 한번씩 클릭해주시기 바랍니다.
특히 4번을 클릭 안하면 Q 클래스를 import 하지 못하는 경우가 종종 있습니다.
그러니 꼭! 클릭해주시기 바랍니다. 한번하고 이후에는 안해도 됩니다.


3. maven compile

이러고 나서 maven compile (1) 을 한번 해줍니다.
그리고 Reload All Maven Project (2) 버튼도 눌러줍니다.

이후에 target 폴더에 정상적으로 Q 클래스가 생성된 것을 확인할 수 있습니다.


소스 상에서 Q 라고만 입력해도 자동완성 목록에서
QueryDSL 의 Q클래스 들이 표출되는 것을 확인할 수 있습니다.

혹시라도 뭔가 잘 안되면, invalidate cache 를 한번 해주고,
maven reload 목차부터 다시 시도해주시기 바랍니다.

저도 어쩌다 한번씩 안되네요 😅



설정, 세팅에 의해서 고생 중이신 분들에게 도움이 됐길 바라며
이만 글을 마치겠습니다. 긴 글 읽어주셔서 감사합니다.






참고: spring-boot 의 최신 버전의 Dependeny 를 사용하고 싶으신 분들을은...

아래 xml 에서 <artifactId>spring-boot-dependencies</artifactId> 아래에
기재된 <version> 값을 자신이 원하는 spring boot 버전에 맞춰주기만 하면 됩니다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-dependencies</artifactId>
        <version>3.2.4</version>
    </parent>

    <groupId>coding.toast</groupId>
    <artifactId>jpa3-retry</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>21</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-hikaricp</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--querydsl bom 이 spring-boot-dependencies 에 의해서 이미 추가된 상태 -->

        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-jpa</artifactId>
            <version>5.0.0</version>
            <classifier>jakarta</classifier>
        </dependency>
        <dependency>
            <groupId>com.querydsl</groupId>
            <artifactId>querydsl-apt</artifactId>
            <scope>provided</scope>
            <version>5.0.0</version>
            <classifier>jakarta</classifier>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerArgs>
                        <arg>-proc:full</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <argLine>-XX:+EnableDynamicAgentLoading</argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

👍 참고한 링크

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글