JPA 실습을 위한 기본 세팅

guswls·2023년 2월 2일

JPA 활용 1편

목록 보기
1/9
post-thumbnail

이번 포스팅은 김영한 이사님의 스프링 부트 JPA 활용 1편을 듣고 제작되었습니다.

1. 초기 세팅

1-1. 파일 생성

초기 세팅은 이렇게 진행하였고 강의가 나온 시점과 달리 스프링 부트가 3.XX로 업데이트도 됐고 이에 따라 자바도 17버전 이상을 사용해야하는 것을 유의해야 한다.

1-2. warning 제거

HHH90000021: Encountered deprecated setting [javax.persistence.sharedCache.mode], use [jakarta.persistence.sharedCache.mode] instead
spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning

또한 위와 같은 warning이 뜨는 것을 없애기 위해 yml파일을 수정하였다. 참고로 세팅은 properties가 아닌 yml형식을 사용하기로 하였다.

1-3. Lombok Setting

Lombok플러그인을 미리 깐 다음과 같이 Enable annotation processing을 체크해주어야 한다.

1-4. devtools 활용

html파일을 변경할 때 마다 모든 파일을 다시 컴파일 하면 시간이 오래 걸린다. 이때 devtools를 사용하면 변경이 발생한 html파일만 리컴파일이 가능하다.

implementation 'org.springframework.boot:spring-boot-devtools'

dependencies에 다음과 같이 추가하고 서버가 실행된 상태에서
이렇게 html파일만 리컴파일 할 수 있다.

1-5. H2설정

H2는 파일모드로 최소 한번 실행 후 네트워크모드로 설정해서 사용해야 한다.
먼저 JDBC URLjdbc:h2:~/jpashop로 한번 진행한 다음에 연결을 끊고 다시 jdbc:h2:tcp://localhost/~/jpashop이 주소로 실행하면 된다.

1-6. application.yml 설정

spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/jpashop
    username: sa
    password:
    driver-class-name: org.h2.Driver

  jpa:
    hibernate:
      ddl-auto: create
    properties:
      hibernate:
        #show_sql: true
        format_sql: true
      jakarta:
        persistence:
          sharedCache:
            mode: ALL

    open-in-view: false

logging:
  level:
    org.hibernate.SQL: debug

앞서 warning을 지웠던 세팅에 이어서 db커넥션과 jpa에 대한 세팅을 해준다. 참고로 show_sql=truesout을 sql을 찍게 되는데 이보단 org.hibernate.SQL옵션을 통해 logger에 실행 SQL을 남기는 것이 좋다고 한다.

2. JPA Test

이제 간단한 예제를 통해서 테스트를 진행해보자.

2-1. Member 구현

@Entity
@Getter @Setter
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
}

2-2. MemberRepository 구현

@Repository
public class MemberRepository {

    @PersistenceContext
    private EntityManager em;

    public Long save(Member member){
        em.persist(member);
        return member.getId();
    }

    public Member find(Long id){
        return em.find(Member.class, id);
    }
}

참고로 @PersistenceContext 어노테이션을 사용하면 별도의 주입 과정 없이 바로 스프링 빈으로 등록된 EntityManager스프링 컨테이너에서 가져올 수 있다고 한다.

2-3. Test

@SpringBootTest
@Transactional
@Commit
class MemberRepositoryTest {

    @Autowired MemberRepository memberRepository;

    @Test
    void save() {
        //given
        Member member = new Member();
        member.setUsername("MemberA");
        //when
        Long saveId = memberRepository.save(member);
        Member findMember = memberRepository.find(saveId);
        //then
        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
        Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        Assertions.assertThat(findMember).isEqualTo(member);
    }

    @Test
    void find() {
    }
}

강의에선 Junit4를 사용하지만 아무래도 요즘은 Junit5로 많이 넘어간 추세인 것 같아 Junit5로 테스트를 생성하였다.

참고로 then 부분의 3번째 줄을 보면 우리가 생성한 멤버와 db에서 찾은 맴버가 동일하다는 것을 알 수 있는데 이것은 영속성 컨텍스트때문이라고 한다. 영속성 컨텍스트의 경우 JPA의 가장 핵심요소 중 하나라고 알고 있기 때문에 곧 따로 정리를 할 예정이다.


참고: P6spy 활용하기

P6spy를 통해 JPA가 생성한 쿼리의 파라미터를 쉽게 볼 수 도 있고 포매팅도 할 수 있어 편하다. 하지만 스프링부트 3.XX부터 제작자가 공식적으로 지원을 하지 않아서 약간의 세팅을 우리가 해주어야 한다.

1. dependencies 추가

build.gradledependencies에 다음을 추가해주자.

implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.1'

2. 파일 생성


resources에서 META-INF디렉토리와 하위에 spring디렉토리를 생성하고 다음과 같은 이름으로 파일을 생성하고 내용을 집어넣자.

파일명 : org.springframework.boot.autoconfigure.AutoConfiguration.imports
내용 : com.github.gavlyukovskiy.boot.jdbc.decorator.DataSourceDecoratorAutoConfiguration

그 다음 마찬가지로 resources에서 다른 파일을 추가적으로 생성하자.

파일명 : spy.properties
내용 : appender=com.p6spy.engine.spy.appender.Slf4JLogger

이렇게만 해도 동작을 하지만 줄바꿈을 적용해서 가독성을 좋게 하려면 프로젝트 패키지 하위에 다음 클래스를 생성하자.

package jpabook.jpashop;

import com.p6spy.engine.logging.Category;
import com.p6spy.engine.spy.P6SpyOptions;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;
import jakarta.annotation.PostConstruct;
import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.springframework.context.annotation.Configuration;

import jakarta.annotation.PostConstruct;
import java.util.Locale;

@Configuration
public class P6SpySqlFormatter implements MessageFormattingStrategy {

    @PostConstruct
    public void setLogMessageFormat() {
        P6SpyOptions.getActiveInstance().setLogMessageFormat(this.getClass().getName());
    }

    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql, String url) {
        sql = formatSql(category, sql);
        return String.format("[%s] | %d ms | %s", category, elapsed, formatSql(category, sql));
    }

    private String formatSql(String category, String sql) {
        if (sql != null && !sql.trim().isEmpty() && Category.STATEMENT.getName().equals(category)) {
            String trimmedSQL = sql.trim().toLowerCase(Locale.ROOT);
            if (trimmedSQL.startsWith("create") || trimmedSQL.startsWith("alter") || trimmedSQL.startsWith("comment")) {
                sql = FormatStyle.DDL.getFormatter().format(sql);
            } else {
                sql = FormatStyle.BASIC.getFormatter().format(sql);
            }
            return sql;
        }
        return sql;
    }
}

보기 좋게 쿼리와 파라미터가 포맷팅 된것을 확인할 수 있다.

자세한 정보는 이 글을 참고하자.

3. 총정리

이렇게 본격적인 JPA 활용 실습 전에 모든 세팅을 완료하였다. 이미 스프링 입문 강의에서 다뤘던 내용들도 많고 세팅적인 부분도 이미 한번 다뤄봤기 때문에 빠르게 넘어갈 수 있었다.

기본편을 안듣고 활용편부터 듣다보니 개념적으로 부족할 수 는 있으나 우선 스프링에 대해서 아주 조금 찍먹을 해본 이 시점에서 바로 활용을 해보는 것도 괜찮을 것 같아 바로 이번 강의를 듣기로 결정하였다.

영속성 컨텍스트를 비롯한 핵심 개념들은 추후에 기본편을 들으면서 자세히 정리할 생각이다.

profile
안녕하세요

2개의 댓글

comment-user-thumbnail
2023년 2월 3일

도움이 됐습니다. 감사합니다!

1개의 답글