Spring Boot에서 JPA와 Spring Data 활용

* 이 글은 2016년 1월 개인 위키에 올렸던 글을 옮긴 것임을 알려드립니다.

공식 참조 문서 29.3. JPA and ‘Spring Data’ 내용 설명

라이브러리 추가

빌드 스크립트에 아래와 같이 두 줄을 추가한다.

1
2
3
4
5
6
dependencies {
...
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    compile("com.h2database:h2")
...
}

첫 줄 라이브러리는 Hibernate, Spring ORM, Spring Data JPA를 한번에 참조하게 한다. 다음으로 데이터베이스 접속을 위한 라이브러리를 써준다. 부트는 별도 설정없이 라이브러리에 따라 H2HSQLDerby 내장 데이터베이스를 지원한다. 실제로 확인한 것은 아니지만 인터넷 검색을 해보면 대체로 h2가 셋 중에 빠르다는 기록이 있어서 이를 쓰기로 한다.

For most operations, the performance of H2 is about the same or better than HSQLDB, Derby 출처: http://lightjavadatabase.blogspot.kr/2012/12/choosing-light-weight-java-database.html

엔티티 클래스 작성

전형적인 JPA 사용 방식에 따르면 persistence.xml 파일이 필요하지만, 부트는 엔티티 스캐닝(Entity Scanning) 기능을 사용하기 때문에 xml 파일이 필요 없다. 대신 SpringBootApplication이나 EnableAutoConfiguration 애노테이션을 부착한 설정 기준 클래스(main configuration class) 하위에 있는 모든 패키지에서 엔티티를 찾는다. 따라서, 설정에 대한 별도 고민없이 JPA의 Entity, Embeddable, MappedSuperClass 애노테이션을 사용하여 작성하고, 패키지 위치만 주의하면 된다.

city 패키지를 만들고 간단한 엔티티 클래스를 작성하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package city;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.io.Serializable;
@Entity
public class City implements Serializable {
    @Id
    @GeneratedValue
    private Long id;
    @Column(nullable = false)
    private String name;
    @Column(nullable = false)
    private String country;
    protected City() {
        // no-args constructor required by JPA spec
        // this one is protected since it shouldn't be used directly
    }
    public City(String name, String country) {
        this.name = name;
        this.state = country;
    }
    public String getName() {
        return this.name;
    }
    public String getCountry() {
        return this.country;
    }
    // ... etc
}

Spring Data JPA 리포지토리 작성

Spring Data 에서 JPA로 데이터 접근을 할 수 있는 편리한 인터페이스를 제공한다. 메소드 이름 등으로 유추하여 JPA 질의를 자동으로 생성한다. 복잡한 질의는 Query 애노테이션을 활용할 수 있다. 보통, Repository 나 CrudRepository 를 확장하여 작성한다. 자동 설정을 사용하는 경우 엔티티와 마찬가지 기준으로 리포지토리 역시 탐색 대상이다.

1
2
3
4
package city;
import org.springframework.data.repository.*;
public interface CityRepository extends CrudRepository<City, Long> {
}

결과 확인을 위한 컨트롤러 작성

Spring Boot 공식 참조 문서 예제 설명에서 사용했던 Example 클래스를 다음과 같이 수정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package city;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
@RestController
@EnableAutoConfiguration
public class Example {
    @Autowired
    private CityRepository repository;
    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }
    @RequestMapping("/cities")
    Collection<City> cities(){
        repository.save(new City("Seoul", "Korea"));
        repository.save(new City("Busan", "Korea"));
        repository.save(new City("Tokyo", "Japan"));
        repository.save(new City("Beijing", "China"));
        repository.save(new City("Shanghai", "China"));
        return makeCollection(repository.findAll());
    }
    public static <E> Collection<E> makeCollection(Iterable<E> iter) {
        Collection<E> list = new ArrayList<E>();
        for (E item : iter) {
            list.add(item);
        }
        return list;
    }
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Example.class, args);
    }
}

입출력 결과 확인

실행 시키고 브라우저에 아래와 같이 URL을 입력하면 JSON 형태로 결과가 출력되는 것을 확인할 수 있다.

JSON 형태의 데이터 출력

JSON 형태의 데이터 출력

h2 콘솔 사용

부트 개발도구를 사용하거나 spring.h2.console.enabled 프로퍼티 값을 true로 설정하면 h2 콘솔을 사용할 수 있다. main 메소드에서 프로퍼티 설정을 한줄 추가한다.

1
2
3
4
public static void main(String[] args) throws Exception {
    System.setProperty("spring.h2.console.enabled", "true");
    SpringApplication.run(Example.class, args);
}

이제 브라우저에서 경로명으로 /h2-console 을 입력하면 h2 콘솔을 사용할 수 있다. 기본(default) 접속 정보는 다음과 같다.

  • JDBC URL: jdbc:h2:mem:testdb
  • User Name: sa

중간에 겪은 문제 공유

드라이버 라이브러리 참조가 빠진 경우

다음과 같은 오류 로그를 만난다.

1
Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).

기준 설정 클래스(Example.java)가 디폴트 패키지에 있는 경우

다음과 같은 예외가 발생한다.

1
2
3
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'example': Injection of autowired dependencies failed; nested exception is 
org.springframework.beans.factory.BeanCreationException: Could not autowire field: private city.CityRepository Example.repository; nested exception is 
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [city.CityRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

의존 객체 주입에 실패했다는 내용인데, 로그 상단에 보면 다음과 같은 내용을 발견할 수 있다. 디폴트 패키지에 선언한 EnableAutoConfiguration 애노테이션으로 인해 자동 스캐닝이 불가해진다는 경고다. Example 클래스를 특정 패키지 아래 두어 문제를 해결할 수 있다.

1
2016-01-07 18:32:23.651  WARN 3459 --- [           main] o.s.b.a.AutoConfigurationPackages        : @EnableAutoConfiguration was declared on a class in the default package. Automatic @Repository and @Entity scanning is not enabled.

H2 콘솔 사용 관련 문제

JDBC URL을 잘못 넣은 경우

JDBC URL을 jdbc:h2:mem:test 와 같이 jdbc:h2:mem:testdb가 아닌 다른 이름으로 넣으면 로그인은 가능하나 테이블이 보이지 않는다. 스프링 부트의 기본 프로퍼티 조합을 보면 아래와 같이

1
spring.datasource.name=testdb

User Name을 잘못 넣은 경우

sa가 아닌 사용자 이름을 넣을 경우 다음과 같은 접속 오류 안내를 만난다.

1
Wrong user name or password [28000-190]

참고 문서


Popit은 페이스북 댓글만 사용하고 있습니다. 페이스북 로그인 후 글을 보시면 댓글이 나타납니다.