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

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


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


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


프로젝트 구조

├── 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


이 글을 공유하기

댓글(0)

Designed by JB FACTORY