[Spring JPA]_DB설정, 동작확인, 기본세팅 끝!

youuu·2022년 11월 7일
0

Study

목록 보기
3/11
  • 복잡하게 설정할때엔 yml이 좋다.

  • show_sql: truesysout
    logginglog인데, 운영환경에서는 sysout을 쓰면 안된다. log로 걸어야한다.

    Repository ➡️ entity 같은걸 찾아준다. (Dao와 비슷)



💻 코딩


🪴 실제 동작하는지 확인하기

  1. Entity 만들기

📋 Member.java

package jpabook.jpashop;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
@Getter
@Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;
    private String username;
}





  1. Repository 만들기

📋 MemberRepository.java

  • EntityManager를 따로 만들어 주지 않아도 Spring Boot가 만들어 준다.

    • ▶️ build.gradle 파일에 spring-boot-starter-data-jpa부분.
  • return member.getId();에서 왜 Id만 반환하지 ❓❓

    • command와 쿼리를 분리해라 하는 원칙에 의해서.
      사이드 이펙트를 일으키는 커맨드 성이기 때문에, id 정도만 있으면 다음에 조회할수 있기 때문이다.
package jpabook.jpashop;

import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@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);
    }
}

  1. 실행

단축키 : shift + ctrl + T

  1. 강사님은 JUnit4로 실행하였지만 나는 5로 해보았다.(오류나거나 잘 안돌아가면 4로 실행해야지. ➡️ 4로 다시 실행하였다)

  2. 확인을 누르면 아래처럼 test에 생성된다

단축키 :

  • 세미콜론 자동완성 : command + shift + enter
  • memberRepository.save(member);를 친 후 option + command + option +v를 누르면 Long save = memberRepository.save(member);로 바뀐다. (🥰인텔리제이 좋아)

🏀 MemberRepositoryTest.java

@RunWith(SpringRunner.class), @SpringBootTest을 넣으면 테스트파일.

🧐 : given, when, then 구조도 그렇고 인텔리제이 사용법, 단축키들이 신기했다. 좀더 쓰다보면 손에 익겠지

package jpabook.jpashop;

import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.jupiter.api.Assertions.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MemberRepositoryTest {

    @Autowired MemberRepository memberRepository;


    @Test
    public void testMember() throws Exception {
        //given
        Member member = new Member();
        member.setUsername("memberA");

        //when
        Long savedId = memberRepository.save(member);
        Member findMember = memberRepository.find(savedId);

        //then
        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
        Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
    }
}

작성 후 테스트 실행. ▶️ 버튼을 누른다

콘솔 :

오류... ❓❓

강의에서도 오류는 나는데 다른 오류이다.

java.lang.IllegalStateException: Failed to load ApplicationContext

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:132)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)
	... 27 more
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:275)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:237)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.id.factory.internal.DefaultIdentifierGeneratorFactory.injectServices(DefaultIdentifierGeneratorFactory.java:175)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:286)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:243)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:214)
	at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.<init>(InFlightMetadataCollectorImpl.java:173)
	at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:127)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1460)
	at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1494)
	at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
	at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
	at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)
	... 42 more
Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
	at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100)
	at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:138)
	at org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator.initiateService(JdbcEnvironmentInitiator.java:35)
	at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.initiateService(StandardServiceRegistryImpl.java:101)
	at org.hibernate.service.internal.AbstractServiceRegistryImpl.createService(AbstractServiceRegistryImpl.java:263)
	... 59 more

해결하기 위해:

  1. H2 DB DB연결이 끊어져서 그런가 하고 다시 연결 한 상태로 실행
    ➡️ 똑같은 오류.
  2. Caused by: 읽어보기:
    Test에서 윗쪽에도 오류가 떠 있었다. 위를 실행 후 Caused by를 읽어본다

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: jpabook.jpashop.Member

  1. 구글링 해보며 이것저것 수정해 보았다.
  • MemberRepositoryTest.java 에 @Transactional을 걸고 실행

    ➡️ 그래도 오류 났었다.
  • 구글링 해보니 Member.java 에 @Id이 잘못 import 된경우.
    • 기존 : 스프링 프레임워크로 되어있던걸 지운 후 import javax.persistence.Entity;로 수정.
  1. 실행해보니 드디어 초록불! (해결하기 위해 java 11 설치, unit 5 ➡️ 4로 변경 등등 초기 세팅때 잘못 해줬던 것들을 잡아줬다.)

테스트를 통해 JPA가 잘 동작하고 있구나 알 수 있다.

  • 셋팅이 잘 되었구나 알 수 있다.

🚩 테스트1

@Transactional

  • @Transactional이 test케이스 안에 있으면 test케이스가 끝난 후 바로 DB rollback을 한다. (➡️ ?? 데이터가 들어가있으면 테스트를 못하기 때문에 rollback 하는게 맞다.)

🚩 테스트2

@Rollback(false)

Rollback안하고 커밋하는것이다.

실행후 H2 db로가서 실행해 보면 username이 제대로 뜨는 것을 볼 수 있다.

🚩 테스트3

findMember == member 비교 ??

정답! True

Assertions.assertThat(findMember).isEqualTo(member);

  • 같은 영속성 컨텍스트 안에서는 id값이 같으면 같은 entity로 식별한다.
    (➡️식별자가 같으면 같은 entity로 인식)
    그래서 select쿼리가 아니라 insert쿼리만 나갔다.

결과 화면 :

select쿼리 없이 insert만 나감


JPA 세팅 끝!


🍯 꿀팁

  • Entity, Repository 동작 확인
  • jar 빌드해서 동작 확인

잘 동작할까?

  • cmd를 통해 ./gradlew clean build실행
  • cmd 에서 java -jar를 넣어서 실행. -> 터미널이 멈춰있어 다음에 테스트 해봐야겠다

스프링 부트를 통해 복잡한 설정이 다 자동화 되어 persistence.xml도 없고, LocalContainerEntityManagerFactoryBean도 없다. (✓ persistence.xml는 Mybatis에서 사용한다.)


잘 모를땐 Boot메뉴얼을 보고 공부하기.


🍯🍯🍯 꿀팁

쿼리 파라미터 로그 남기기

알면 개발할 때 편하다.

기존엔 아래처럼 (?,?)로 남았다.

  insert 
    into
        member
        (username, id) 
    values
        (?, ?)

1. 쿼리 파라미터 찍기

.yml에서 org.hibernate.type: trace로 주기.


실행하면 똑같이 (?,?)로 나오지만 오른쪽으로 넘기면 아래처럼 1번 파라미터, 2번 파라미터가 나오는 로그를 남긴다.👍

2. 외부 라이브러리사용

깃 링크 를 들어가보면 아래처럼 사용할 수 있는 라이브러리들이 뜬다. 그중 P6Spy을 사용해볼 예정이다.

사용방법 :

Quick Start 를 찾아 아래를 복붙한다

  • Gradle에서 implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.8.1'입력 후 refresh

    라이브러리들일 들어 간 후 실행 ▶️
    처음실행시엔 원본이, 두번째엔 파라미터 명을 출력.

🔺🔺🔺 p6spy같은 것은 개발단계에선 좋지만 운영에선 생각해 봐야한다.
이런 것들이 성능을 저하 시킬수 있기 때문에 성능테스트는 필수! (괜찮으면 운영에서도 사용)

profile
공부중인 주니어 개발자

0개의 댓글