스프링 데이터(JPA, MongoDB 등)를 활용한 데이터 액세스 방법

스프링 데이터(JPA, MongoDB 등) 소개

스프링 프레임워크는 자바 기반의 엔터프라이즈 웹 응용 프로그램을 개발할 때 가장 많이 사용되는 프레임워크 중 하나이다. 스프링 프레임워크는 다양한 기능을 제공하며, 그 중 데이터 액세스 기능은 가장 많이 사용되는 기능 중 하나이다. 스프링 데이터는 스프링 프레임워크에서 제공하는 데이터 액세스 기능을 개선하고 확장한 것으로, JPA, MongoDB, Redis 등 다양한 데이터베이스에 대한 액세스를 지원한다.

JPA를 활용한 데이터 액세스 방법

JPA(Java Persistence API)는 ORM(Object-Relational Mapping)을 지원하는 자바 표준 기술이다. JPA를 활용하면 객체와 데이터베이스를 매핑할 수 있으며, 객체 지향적인 프로그래밍이 가능해진다.

스프링 데이터 JPA

스프링 데이터 JPA는 스프링 프레임워크에서 제공하는 JPA 기능을 개선하고 확장한 것이다. 스프링 데이터 JPA를 사용하면 JPA를 더 쉽게 사용할 수 있으며, CRUD(Create, Read, Update, Delete) 기능을 지원한다.

의존성 추가

스프링 데이터 JPA를 사용하려면 의존성을 추가해야 한다. Maven을 사용하는 경우, pom.xml 파일에 다음과 같이 의존성을 추가할 수 있다.


    org.springframework.boot
    spring-boot-starter-data-jpa

Entity 클래스 생성

JPA를 사용하려면 Entity 클래스를 생성해야 한다. Entity 클래스는 데이터베이스 테이블과 매핑되는 클래스이며, @Entity 어노테이션을 사용하여 지정할 수 있다. 예를 들어, 다음과 같이 User Entity 클래스를 생성할 수 있다.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int age;
    // getter, setter, toString
}

Repository 인터페이스 생성

Repository 인터페이스는 데이터 액세스를 위한 메소드를 정의한 인터페이스이다. 스프링 데이터 JPA는 Repository 인터페이스를 자동으로 구현해주므로, 개발자는 메소드만 정의하면 된다. 예를 들어, 다음과 같이 UserRepository 인터페이스를 생성할 수 있다.

public interface UserRepository extends JpaRepository {
}

JpaRepository는 스프링 데이터 JPA에서 제공하는 Repository 인터페이스 중 하나이며, User Entity 클래스와 Long 타입의 id를 사용하는 메소드를 제공한다.

Service 클래스 생성

Service 클래스는 비즈니스 로직을 구현한 클래스이다. 예를 들어, 다음과 같이 UserService 클래스를 생성할 수 있다.

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List findAll() {
        return userRepository.findAll();
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void delete(Long id) {
        userRepository.deleteById(id);
    }
}

@Autowired 어노테이션을 사용하여 UserRepository를 주입받고, findAll(), save(), delete() 메소드를 구현하였다.

Controller 클래스 생성

Controller 클래스는 HTTP 요청을 처리하는 클래스이다. 예를 들어, 다음과 같이 UserController 클래스를 생성할 수 있다.

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public List findAll() {
        return userService.findAll();
    }

    @PostMapping
    public User save(@RequestBody User user) {
        return userService.save(user);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable Long id) {
        userService.delete(id);
    }
}

@RestController 어노테이션을 사용하여 RESTful API를 지원하며, @RequestMapping 어노테이션을 사용하여 URL과 메소드를 매핑하였다. @RequestBody 어노테이션을 사용하여 HTTP 요청 바디에서 User 객체를 가져오고, @PathVariable 어노테이션을 사용하여 URL에서 id를 가져오는 등의 기능을 구현하였다.

JPA Query Method

JPA Query Method는 메소드 이름으로 쿼리를 생성하는 방식이다. 스프링 데이터 JPA에서는 다양한 키워드를 지원하여 쿼리를 생성할 수 있다.

메소드 이름 규칙

메소드 이름은 다음과 같이 작성해야 한다.

  • findBy: 조회
  • countBy: 개수 조회
  • deleteBy: 삭제

예를 들어, 다음과 같이 User Entity 클래스와 UserRepository 인터페이스를 작성하면, 스프링 데이터 JPA가 메소드 이름에 따라 쿼리를 생성해준다.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int age;
    // getter, setter, toString
}

public interface UserRepository extends JpaRepository {
    List findByName(String name);
    List findByAgeGreaterThan(int age);
    Long countByAgeGreaterThan(int age);
    void deleteByName(String name);
}

키워드

메소드 이름에 사용할 수 있는 키워드는 다음과 같다.

  • And: 두 개의 조건을 and로 연결
  • Or: 두 개의 조건을 or로 연결
  • Between: 값이 두 개 사이에 있는 경우
  • LessThan: 값이 주어진 값보다 작은 경우
  • LessThanEqual: 값이 주어진 값보다 작거나 같은 경우
  • GreaterThan: 값이 주어진 값보다 큰 경우
  • GreaterThanEqual: 값이 주어진 값보다 크거나 같은 경우
  • IsNull: 값이 null인 경우
  • IsNotNull: 값이 null이 아닌 경우
  • Not: 조건을 부정하는 경우
  • In: 값이 주어진 값들 중에 있는 경우
  • NotIn: 값이 주어진 값들 중에 없는 경우
  • StartingWith: 값이 주어진 문자열로 시작하는 경우
  • EndingWith: 값이 주어진 문자열로 끝나는 경우
  • Containing: 값이 주어진 문자열을 포함하는 경우
  • NotContaining: 값이 주어진 문자열을 포함하지 않는 경우
  • IgnoreCase: 대소문자를 무시하는 경우

JPA Named Query

JPA Named Query는 이름으로 쿼리를 생성하는 방식이다. 스프링 데이터 JPA에서는 @NamedQuery 어노테이션을 사용하여 Named Query를 정의할 수 있다.

Named Query 작성

다음과 같이 @NamedQuery 어노테이션을 사용하여 Named Query를 정의할 수 있다.

@Entity
@NamedQuery(name = "User.findByName", query = "SELECT u FROM User u WHERE u.name = :name")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private int age;
    // getter, setter, toString
}

Named Query 사용

Named Query를 사용하려면 EntityManager를 사용하여 createNamedQuery() 메소드를 호출해야 한다.

public interface UserRepository extends JpaRepository {
    @Query("SELECT u FROM User u WHERE u.name = :name")
    List findByName(@Param("name") String name);

    List findByAgeGreaterThan(int age);

    Long countByAgeGreaterThan(int age);

    void deleteByName(String name);
}

EntityManager를 사용하지 않고, @Query 어노테이션을 사용하여 Named Query를 호출할 수도 있다. 이때, Named Query의 이름을 사용한다.

MongoDB를 활용한 데이터 액세스 방법

MongoDB는 NoSQL 데이터베이스 중 하나로, JSON 형태의 도큐먼트를 저장하고 검색할 수 있다. MongoDB를 사용하면 스키마를 사전에 정의하지 않아도 되므로, 개발 속도가 빠르다.

스프링 데이터 MongoDB

스프링 데이터 MongoDB는 스프링 프레임워크에서 제공하는 MongoDB 기능을 개선하고 확장한 것이다. 스프링 데이터 MongoDB를 사용하면 MongoDB를 더 쉽게 사용할 수 있으며, CRUD(Create, Read, Update, Delete) 기능을 지원한다.

의존성 추가

스프링 데이터 MongoDB를 사용하려면 의존성을 추가해야 한다. Maven을 사용하는 경우, pom.xml 파일에 다음과 같이 의존성을 추가할 수 있다.


    org.springframework.boot
    spring-boot-starter-data-mongodb

Document 클래스 생성

Document 클래스는 MongoDB에 저장되는 도큐먼트와 매핑되는 클래스이다. Document 클래스는 @Document 어노테이션을 사용하여 지정할 수 있다. 예를 들어, 다음과 같이 User Document 클래스를 생성할 수 있다.

@Document
public class User {
    @Id
    private String id;
    private String name;
    private int age;
    // getter, setter, toString
}

@Id 어노테이션은 MongoDB의 _id 필드와 매핑되는 필드를 지정한다.

Repository 인터페이스 생성

Repository 인터페이스는 데이터 액세스를 위한 메소드를 정의한 인터페이스이다. 스프링 데이터 MongoDB는 Repository 인터페이스를 자동으로 구현해주므로, 개발자는 메소드만 정의하면 된다. 예를 들어, 다음과 같이 UserRepository 인터페이스를 생성할 수 있다.

public interface UserRepository extends MongoRepository {
}

MongoRepository는 스프링 데이터 MongoDB에서 제공하는 Repository 인터페이스 중 하나이며, User Document 클래스와 String 타입의 id를 사용하는 메소드를 제공한다.

Service 클래스 생성

Service 클래스는 비즈니스 로직을 구현한 클래스이다. 예를 들어, 다음과 같이 UserService 클래스를 생성할 수 있다.

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List findAll() {
        return userRepository.findAll();
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void delete(String id) {
        userRepository.deleteById(id);
    }
}

@Autowired 어노테이션을 사용하여 UserRepository를 주입받고, findAll(), save(), delete() 메소드를 구현하였다.

Controller 클래스 생성

Controller 클래스는 HTTP 요청을 처리하는 클래스이다. 예를 들어, 다음과 같이 UserController 클래스를 생성할 수 있다.

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @GetMapping
    public List findAll() {
        return userService.findAll();
    }

    @PostMapping
    public User save(@RequestBody User user) {
        return userService.save(user);
    }

    @DeleteMapping("/{id}")
    public void delete(@PathVariable String id) {
        userService.delete(id);
    }
}

@RestController 어노테이션을 사용하여 RESTful API를 지원하며, @RequestMapping 어노테이션을 사용하여 URL과 메소드를 매핑하였다. @RequestBody 어노테이션을 사용하여 HTTP 요청 바디에서 User 객체를 가져오고, @PathVariable 어노테이션을 사용하여 URL에서 id를 가져오는 등의 기능을 구현하였다.

Querydsl

Querydsl은 타입 세이프한 쿼리를 작성할 수 있게 해주는 라이브러리이다. Querydsl을 사용하면 쿼리 작성 시 오타나 잘못된 타입으로 인한 오류를 줄일 수 있으며, IDE의 자동완성 기능을 지원하여 쿼리 작성 시간을 단축시킬 수 있다.

의존성 추가

Querydsl을 사용하려면 의존성을 추가해야 한다. Maven을 사용하는 경우, pom.xml 파일에 다음과 같이 의존성을 추가할 수 있다.


    com.querydsl
    querydsl-mongodb
    ${querydsl.version}

QuerydslPredicateExecutor

QuerydslPredicateExecutor 인터페이스는 Querydsl을 사용하여 동적 쿼리를 생성하는 기능을 제공하는 인터페이스이다. 예를 들어, 다음과 같이 UserRepository 인터페이스를 수정하면, Querydsl을 사용하여 동적 쿼리를 생성할 수 있다.

public interface UserRepository extends MongoRepository, QuerydslPredicateExecutor {
}

Querydsl 사용

Querydsl을 사용하려면 QuerydslMongoPredicateBuilder를 사용하여 Predicate를 생성해야 한다. 예를 들어, 다음과 같이 UserService 클래스를 수정하면, Querydsl을 사용하여 동적 쿼리를 생성할 수 있다.

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public List findAll(String name, int age) {
        BooleanBuilder builder = new BooleanBuilder();
        if (name != null) {
            builder.and(QUser.user.name.eq(name));
        }
        if (age > 0) {
            builder.and(QUser.user.age.eq(age));
        }
        Predicate predicate = builder.getValue();
        return (List) userRepository.findAll(predicate);
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void delete(String id) {
        userRepository.deleteById(id);
    }
}

BooleanBuilder를 사용하여 조건을 추가하고, Predicate를 생성하여 findAll() 메소드에 전달하였다.

스프링 데이터를 활용한 데이터 액세스 패턴 소개

스프링 데이터는 다양한 데이터베이스에 대한