[Spring JPA #14] 스프링 데이터 커스텀 리포지터리 만들기

2021. 3. 25. 23:39 Spring Data/Spring Data JPA

 

| 스프링 데이터 커스텀 리포지터리 

 

스프링 데이터에서 제공하는 쿼리 메서드로 어떤 특정 비즈니스 로직을 구현하는 데 어려움이 있을 경우 직접 코딩으로 구현할 수 있습니다.

 

스프링 데이터 커스텀 리포지터리 예제

 

프로젝트 구조

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── demo
│   │   │               ├── DemoApplication.java
│   │   │               └── post
│   │   │                   ├── PostCustomRepositoryImpl.java
│   │   │                   ├── PostCustomRepository.java
│   │   │                   ├── Post.java
│   │   │                   └── PostRepository.java
│   │   └── resources
│   │       └── application.properties

 

의존성 관리

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

 

application.properties

spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.type.descriptor.sql=trace

 

소스 코드

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}
@Entity
@Data
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @Lob
    private String content;

    @Temporal(TemporalType.TIMESTAMP)
    private Date created;
}
public interface PostCustomRepository<T> {

    List<Post> findMyPost();

    void delete(T entity);
}

 

  • 사용자가 정의한 커스텀 Repository 코드입니다. 스프링에서 제공하는 어떤 기능에 대한 의존성없이 존재하는 POJO입니다.
  • 이 인터페이스를 구현한 접미사가 Impl 인 클래스를 작성하면 이 Repository를 쓸 때 해당 클래스의 구현체가 자동적으로 할당되어 사용되어 집니다.
  • 스프링 데이터에서 기본 기능으로써 제공하는 delete 메서드도 Custom Repository에서 구현하면 덮어쓰기가 가능해집니다.
  • @Data 어노테이션은 참고로 lombok에서 가져온 기능입니다. Setter, Getter, ToString, Constructor에 대한 정보를 자동적으로 생성해줍니다.
@Repository
@Transactional
public class PostCustomRepositoryImpl implements PostCustomRepository {

    @Autowired
    EntityManager entityManager;

    @Override
    public List<Post> findMyPost() {
        System.out.println("custom findMyPost");
        return entityManager.createQuery("SELECT p FROM Post as p", Post.class)
                .getResultList();
    }

    @Override
    public void delete(Object entity) {
        System.out.println("custom delete");
        entityManager.remove(entity);
    }
}

 

  • PostCustomRepository를 구현한 클래스입니다. 위 코드에서 EntityManager는 엔티티를 저장하고 관리하는 역할을 합니다. 
  • PostCustomRepository를 구현한 메서드들은 PostRepository에 의존성이 주입될 때 사용할 수 있게 됩니다.

 

public interface PostRepository extends JpaRepository<Post, Long>, PostCustomRepository<Post> {
}
  • PostCustomRepository 인터페이스를 확장한 PostRepository를 통하여 PostCustomRepository에서 정의한 메서드들을 사용할 수 있습니다.

 

테스트 코드

@RunWith(SpringRunner.class)
@DataJpaTest
public class DemoApplicationTests {

    @Autowired
    PostRepository postRepository;

    @Test
    public void crud() {
        postRepository.findMyPost();

        Post post = new Post();
        post.setTitle("hibernate");
        postRepository.save(post);

        postRepository.findMyPost();

        postRepository.delete(post);
        postRepository.flush();
    }
}

 

만일 접미사를 Impl이 아닌 것으로 Custom Repository를 구현하고 싶다면 다음과 같이 접미사에 대한 설정을 다르게 해야합니다.

@SpringBootApplication
@EnableJpaRepositories(repositoryImplementationPostfix = "Default")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

 

| 기본 리포지터리 커스터마이징

 

프로젝트 구조

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── tutorial
│   │   │           └── springdatarepository
│   │   │               ├── MyRepositoryImpl.java
│   │   │               ├── MyRepository.java
│   │   │               ├── Post.java
│   │   │               ├── PostRepository.java
│   │   │               └── SpringDataRepositoryApplication.java
│   │   └── resources
│   │       └── application.properties
│   └── test
│       └── java
│           └── com
│               └── tutorial
│                   └── springdatarepository
│                       └── DemoApplicationTests.java

 

소스 코드

@NoRepositoryBean
public interface MyRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {

    boolean contains(T entity);
}
  • JpaRepository 인터페이스를 확장한 Repository 인터페이스를 작성하였습니다. @NoRepositoryBean은 이 인터페이스가 Repository로서 직접적으로 기능하지 않을 것이라는 것을 명시합니다.
public class MyRepositoryImpl<T, ID extends Serializable>
        extends SimpleJpaRepository<T, ID> implements MyRepository<T, ID>{

    private EntityManager entityManager;

    public MyRepositoryImpl(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
        super(entityInformation, entityManager);
        this.entityManager = entityManager;
    }

    @Override
    public boolean contains(T entity) {
        return entityManager.contains(entity);
    }
}
  • 위 Repository의 구현체는 JpaRepository 구현체중 Default 구현체인 SimpleJpaRepository를 상속받아 작성할 수 있습니다. 이것을 통해 개발자에게 노출되는 Repository 인터페이스에서 JpaRepository를 따로 인터페이스를 확장하지 않고 아래와 같이 MyRepository 인터페이스만 확장하여 쓸 수 있습니다.
public interface PostRepository extends MyRepository<Post, Long> {
}

 

테스트 코드

@RunWith(SpringRunner.class)
@DataJpaTest
public class DemoApplicationTests {

    @Autowired
    PostRepository postRepository;

    @Test
    public void crud() {
        Post post = new Post();
        post.setTitle("hibernate");

        assertThat(postRepository.contains(post)).isFalse();

        postRepository.save(post);

        assertThat(postRepository.contains(post)).isTrue();
    }
}

 

https://www.inflearn.com/course/스프링-데이터-jpa



출처: https://engkimbs.tistory.com/826?category=772527 [새로비]