ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot JPA 사용법
    프로그래밍/서버 프로그래밍 2018. 12. 20. 13:42

    오늘은 지난번에 알아보았던 JPA를 스프링에서 어떻게 적용하는 지 함께 알아보겠습니다.

    저는 Spring Boot를 이용해서 실습을 진행해보겠습니다.

    본 실습은 jojoldu님의 블로그를 보면서 진행했습니다. (https://jojoldu.tistory.com/251?category=635883)

    1.Dependency 추가

    먼저 저는 maven을 사용하므로 pom.xml에 관련 dependency를 넣어주겠습니다!

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    data jpa는 JPA관련이고, spring-boot-starter-test는 테스트 관련인데 H2는 무엇인지 들어보셨나요?

    H2는 MySQL, oracle, 등과 같은 평범한 DB이지만, 초경량이라는 점과 In Memory DB 를 지원하는 점이 특징입니다.

    그래서 타 DB에 비해 속도가 빠르지만 지속성이 없기때문에?( 컴퓨터 종료 혹은 메모리 종료시 내용이 날라감.) 테스트용으로 매우 좋고, 캐싱용으로도 많이 사용된다고 합니다. 더 자세한 설명은 Google 혹은위키로.....


    다음은 lombok이라는 라이브러리도 들어보신 분도 많겠지만, 아직 모르는 분도 계실 거라고 생각합니다. lombok 은 자바에서 클래스를 생성할 때 보통 반복적으로 적게되는 ToString, Getter, Setter, 등을 Annotation 하나로 간단히 처리해줍니다.

    @Getter
    @Setter
    public class User {
        private String userName;
        private String userAge;
    }

    이런 식으로 작동한답니다.

    lombok은 다른 라이브러리와 다르게 dependency만 추가해준다고 바로 적용되지않고, IDE에 적용을 추가적으로 해줘야하는데, 이 설치법과 lombok을 사용할 때 주의사항은 Hyoj 님의 블로그에 설명이 매우 잘되어있어서 링크로 대체하겠습니다.


    2. Entity 클래스 생성

    이제 드디어 JPA를 테스트해보기위한 Entity클래스를 하나 만들어보겠습니다!

    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @ToString
    @Getter
    @Entity
    public class Customer {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(length = 20, nullable = false)
        private String name;
    
        @Column(length = 20, nullable = false, unique = true)
        private String phone;
    
        @Builder
        public Customer(String name, String phone) {
            this.name = name;
            this.phone = phone;
        }
    
    }
    

    Annotation이 너무 많아서 처음엔 조금 헷갈리실 수도 있습니다. 혹시 lombok을 사용하지 않으시는 분들은 코드내의 @ToString, @Getter, @Builder를 빼고 직접 생성해주시고 나서 진행해주세요!

    Customer 에는 총 3개의 필드가 존재합니다.

    • id 필드는 @id 를 사용하여 기본키(PK)로 지정합니다. 이때 키를 직접할당하는 방식이 아닌, 자동으로 생성되도록 하기위해 @GeneratedValue를 사용합니다.

      GenerationType.IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 방식이고, 다른 방식들은

      • IDENTITY
      • SEQUENCE
      • TABLE
      • AUTO

      정도가 있습니다.

    • name, phone 필드의 @Column Annotation은 데이터베이스 컬럼으로 지정해줍니다. 안에 lengthnullable 또는 unique와 같은 설정들이 가능합니다. 이외에도 다영한 설정이 들어갈 수 있습니다.

      또한, @Column 을 생략할 경우 필드명을 사용하여 컬럼명과 매핑하기 때문에 만약 DB가 대소문자를 구분하는 경우에는 반드시 @Column Annotation을 사용하는 것이 좋습니다.

    3. Repository 클래스 생성

    JPA에서는 단순히 Repository 인터페이스를 생성한후 JpaRepository<Entity, 기본키 타입> 을 extends하면 기본적인 Create, Read, Update, Delete가 자동으로 생성됩니다! 그래서 저흰 그냥 인터페이스를 만들고, 상속만 잘해주면 기본적인 동작을 테스트해볼 수 있는거죠 :D

    @Repositoryd
    public interface CustomerRepository extends JpaRepository<Customer, Long>{
        //비워있어도 잘 작동함.
        // long 이 아니라 Long으로 작성. ex) int => Integer 같이 primitive형식 사용못함
    }

    4. Test 코드 작성 및 테스트

    이제 JUnit 을 이용하여 테스트를 작성해봅니다. 테스트 코드는

    import static org.hamcrest.CoreMatchers.is;
    import static org.junit.Assert.assertThat;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class CustomerRepositoryTests {
    
        @Autowired
        CustomerRepository customerRepository;
    
        @Test
        public void testCustomerRepository(){
            Customer customer = Customer.builder().name("크리스").phone("010-1224-1225").build();
            customerRepository.save(customer);
    
            List<Customer> customerList = customerRepository.findAll();
    
            Customer chris = customerList.get(0);
            assertThat(chris.getName(), is("크리스"));
            assertThat(chris.getPhone(), is("010-1224-1225"));
        }
    
        @After
        public void deleteAll() {
            customerRepository.deleteAll();
        }
    
    }

    이런 식으로 작성되어 있습니다.

    테스트 클래스를 우클릭 후 Run As 에서 JUnit Test를 눌러서 테스트를 진행하였더니,

    테스트에 성공하였습니다! :-)

    1545273276797.png

    4. Repository 수정

    이번에는 CustomerRepository에 수정을 가해서 테스트 코드의 findAll() 부분을 findByName 혹은findByPhone`으로 바꿔보기위해 CustomerRepository부분을 수정해보겠습니다.

    쿼리를 유추할 수 있는 메소드의 이름으로 쿼리를 정의하여주면 자동으로 이에 맞는 쿼리를 실행하여 줍니다.

    먼저 CustomerRepository를 수정합니다.

    @Repository
    public interface CustomerRepository extends JpaRepository<Customer, Long>{
        public List<Customer> findByName(String name);
        public List<Customer> findByPhone(String phone);
        //like검색도 가능
        public List<Customer> findByNameLike(String keyword);
    }

    findBy뒤에 컬럼명을 붙여주면 이를 이용한 검색이 됩니다. 따로 내부 구현을 하지 않아도 알아서 마법처럼 작동합니다!

    이외에도 메소드 생성시 다양한 키워드들을 지원하는 데 대표적으로는

    • And
    • Or
    • Is, Equals
    • LessThan, LessThanEqual
    • GreaterThan, GreaterThanEqual
    • 기타 등등( 아라한사님이 번역해주신 스프링 데이터 JPA 레퍼런스 를 보시면 더 많은 키워드와 정보를 얻을 수 있습니다. )

    5.컨트롤러를 이용한 테스트

    이번에는 JUnit 이 아니라 PostMan을 이용해서 작성한 메소드들이 작동하는 지 확인해보겠습니다.

    먼저 Controller를 작성해야겠죠?

    @RestController
    @EnableAutoConfiguration
    @RequestMapping(value = "/customer")
    public class CustomerController {
    
        @Autowired
        CustomerRepository customerRepository;
    
        @PostMapping("/")
        public @ResponseBody List<Customer> createCustomer(@RequestBody Map<String,String> param){
            String name = param.get("name");
            String phone = param.get("phone");
            Customer customer = Customer.builder().name(name).phone(phone).build();
            customerRepository.save(customer);
    
            return customerRepository.findAll();
        }
    }

    이렇게 확인을 위해 간단히 만들어줬습니다. 이제 PostMan으로 요청을 보내보겠습니다.

    1545279854420.png

    이렇게 응답이 오는 것을 확인할 수 있습니다. 이 상태에서 phone은 유니크이므로 변경한 후 한번 더 요청을 보내면 id2인 데이터가 하나 더 생겨있겠죠?

    1545279985811.png

    저희가 원하는 결과가 나온 걸 확인할 수 있습니다!

    다른 메소드들도 테스트해보면 잘 작동합니다.

    직접 사용해보니 JPA가 MyBatis 에 비하여 왜 더 생산속도가 빠른지 느낄 수 있었습니다.

    긴 글 읽어주셔서 감사합니다! :D

    모두 좋은하루 되세요


    sample code는 https://github.com/junwoochoi/spring-jpa-practice-sample 에 올라와 있습니다.


    아직 모르는게 많아 게시글에 잘못된 정보가 있을 수 있습니다. 혹시 잘못된 정보가 있다면, 댓글 혹은 메일로 알려주시면 최대한 빨리 수정하겠습니다!

    댓글

Designed by Tistory.