Spring Boot | 데이터베이스 접근 | JPA 이용


기본

build.gradle

dependencies {
-   compile 'org.springframework.boot:spring-boot-starter-jdbc'
+   compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'org.hsqldb:hsqldb'
}

application.properties

spring.datasource.url=jdbc:hsqldb:file:./db/testdb;shutdown=true
spring.jpa.hibernate.ddl-auto=update

src/main/java/sample/springboot/jpa/MyEntity.java

package sample.springboot.jpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class MyEntity {

    @Id @GeneratedValue
    private Long id;
    private String value;

    public MyEntity(String value) {
        this.value = value;
    }

    private MyEntity() {}

    @Override
    public String toString() {
        return "MyEntity [id=" + id + ", value=" + value + "]";
    }
}

src/main/java/sample/springboot/jpa/MyEntityRepository.java

package sample.springboot.jpa;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
}

Main.java

package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.MyEntity;
import sample.springboot.jpa.MyEntityRepository;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }

    @Autowired
    private MyEntityRepository repository;

    public void method() {
        this.repository.save(new MyEntity("test"));

        this.repository.findAll().forEach(System.out::println);
    }
}

실행 결과

$ gradle bootRun
MyEntity [id=1, value=test]

$ gradle bootRun
MyEntity [id=1, value=test]
MyEntity [id=2, value=test]

$ gradle bootRun
MyEntity [id=1, value=test]
MyEntity [id=2, value=test]
MyEntity [id=3, value=test]
  • JPA를 사용하는 경우에는 org.springframework.boot:spring-boot-starter-data-jpa을 의존관계에 추가한다.
  • JPA 구현은 Hibernate가 이용된다.
  • 기본으로 테이블이 매번 다시 만들게 되므로 spring.jpa.hibernate.ddl-auto=update를 설정하고있다.
  • JpaRepository를 상속한 인터페이스를 정의하면 Spring이 데이터 접근을 해준다.

메소드 이름에서 쿼리 자동 생성

데이터베이스

hoge

id number string value
1 1 one hoge
2 1 two fuga
3 1 three piyo
4 2 four hoge
5 2 five foga
6 3 six piyo
7 3 seven hoge

엔티티

src/main/java/sample/springboot/jpa/Hoge.java

package sample.springboot.jpa;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Hoge {

    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    private int number;
    private String string;
    @Embedded
    private Fuga fuga;

    @Override
    public String toString() {
        return "Hoge [id=" + id + ", number=" + number + ", string=" + string + ", fuga=" + fuga + "]";
    }
}

src/main/java/sample/springboot/jpa/Fuga.java

package sample.springboot.jpa;

import javax.persistence.Embeddable;

@Embeddable
public class Fuga {

    private String value;

    @Override
    public String toString() {
        return "Fuga [value=" + value + "]";
    }
}

저장소 인터페이스

src/main/java/sample/springboot/jpa/HogeRepository.java

package sample.springboot.jpa;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

public interface HogeRepository extends JpaRepository<Hoge, Long> {

    List<Hoge> findByNumber(int number);

    List<Hoge> findByNumberOrderByIdDesc(int number);

    List<Hoge> findByStringLike(String string);

    List<Hoge> findByNumberLessThan(int number);

    List<Hoge> findByStringIgnoreCase(String string);

    List<Hoge> findByFugaValue(String string);

    long countByStringLike(String string);

    List<Hoge> findByNumberAndStringLike(int number, String string);

    List<Hoge> findByNumberOrString(int number, String string);
}

기동확인

src/main/java/sample/springboot/jpa/Main.java

package sample.springboot;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.Hoge;
import sample.springboot.jpa.HogeRepository;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }

    @Autowired
    private HogeRepository repository;

    public void method() {
        print("findByNumber",              repository.findByNumber(1));
        print("findByNumberAndStringLike", repository.findByNumberAndStringLike(1, "%e"));
        print("findByNumberOrString",      repository.findByNumberOrString(2, "seven"));
        print("findByNumberOrderByIdDesc", repository.findByNumberOrderByIdDesc(2));
        print("findByStringLike",          repository.findByStringLike("t%"));
        print("findByNumberLessThan",      repository.findByNumberLessThan(3));
        print("findByStringIgnoreCase",    repository.findByStringIgnoreCase("FIVE"));
        print("findByFugaValue",           repository.findByFugaValue("hoge"));
        print("countByStringLike",         repository.countByStringLike("%o%"));
    }

    private void print(String methodName, List<Hoge> list) {
        System.out.println("<<" + methodName + ">>");
        list.forEach(System.out::println);
        System.out.println();
    }

    private void print(String methodName, long number) {
        System.out.println("<<" + methodName + ">>");
        System.out.println(number);
        System.out.println();
    }
}

실행 결과

<<findByNumber>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

<<findByNumberOrderByIdDesc>>
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]

<<findByStringLike>>
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

<<findByNumberLessThan>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]

<<findByStringIgnoreCase>>
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]

<<findByFugaValue>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=7, number=3, string=seven, fuga=Fuga [value=hoge]]

<<countByStringLike>>
3

<<findByNumberAndStringLike>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

<<findByNumberOrString>>
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]
Hoge [id=7, number=3, string=seven, fuga=Fuga [value=hoge]]

hoge

id number string value
1 1 one hoge
2 1 two fuga
3 1 three piyo
4 2 four hoge
5 2 five foga
6 3 six piyo
7 3 seven hoge
  • Repository를 상속한 인터페이스에 find~ 같은 메소드를 정의하면 Spring이 해석해서 쿼리를 자동으로 생성해 준다.
  • 기본은 findBy[조건으로하는 속성의 이름]으로 정의한다.
  • And와 Or로 연결이 가능하다.
  • OrderBy[속성 이름][Asc|Desc]으로 정렬을 할 수 있다.
  • Like를 붙이면 문자열 포함 검색 수 있다.
  • LessThan, GreaterThan, Between 등도 사용할 수 있다.
  • IgnoreCase를 붙이면 대소 문자 구분없이 비교할 수 있다.
  • count~ 하면 검색 결과의 엔티티 수를 얻을 수 있다.
  • 내장 가능 클래스의 속성을 조건으로하는 경우는 findBy[조합 가능한 클래스] [내장 가능 클래스의 속성]와 연결한다.

JPQL을 사용

src/main/java/sample/springboot/jpa/HogeRepository.java

package sample.springboot.jpa;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface HogeRepository extends JpaRepository<Hoge, Long> {

    @Query("SELECT h FROM Hoge h WHERE (h.id % 2) = 0")
    List<Hoge> findEvenIdEntities();
}

src/main/java/sample/springboot/Main.java

package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.HogeRepository;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }

    @Autowired
    private HogeRepository repository;

    public void method() {
        this.repository.findEvenIdEntities().forEach(System.out::println);
    }
}

실행 결과

Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=6, number=3, string=six, fuga=Fuga [value=piyo]]

hoge

id number string value
1 1 one hoge
2 1 two fuga
3 1 three piyo
4 2 four hoge
5 2 five foga
6 3 six piyo
7 3 seven hoge

@Query 어노테이션을 메소드에 부여하여 JPQL을 지정할 수 있다. JPQL은 @Query의 value로 설정한다.

EntityManager을 이용

src/main/java/sample/springboot/Main.java

package sample.springboot;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.Hoge;

@SpringBootApplication
public class Main {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }

    @Autowired
    private EntityManager em;

    public void method() {
        TypedQuery<Hoge> query = this.em.createQuery("SELECT h FROM Hoge h WHERE h.id=:id", Hoge.class);
        query.setParameter("id", 3L);

        Hoge hoge = query.getSingleResult();

        System.out.println(hoge);
    }
}

실행 결과

Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

@Autowired를 사용하여 일반적으로 EntityManager를 인젝션(주입)할 수 있다.