[스프링(spring)]JpaRepository 원리

allnight5·2023년 2월 1일
0

스프링

목록 보기
42/62

SpringData 구조

SpringData 기능 목록

  • 강력한 리포지토리 및 사용자 지정 객체 매핑 추상화
  • 리포지토리 메서드 이름에서 동적 쿼리 파생
  • 기본 속성을 제공하는 구현 도메인 기본 클래스
  • 명료한 추적기능 지원(생성일시, 마지막 변경일시, 생성자, 마지막 변경자)
  • 사용자 지정 리포지토리 코드 통합 가능성
  • JavaConfig 및 사용자 지정 XML 네임스페이스를 통한 간편한 Spring 통합
  • Spring MVC 컨트롤러와의 고급 통합
  • 교차 스토어 지속성에 대한 실험적 지원

SpringData Jpa 와 JpaRepository 원리

  • Repository ~ JpaRepository 까지는 @NotRepositoryBean 이 붙어있는 인터페이스이다.
    • JpaRepository<Entity,ID> 붙이면 알맞은 프로그래밍 된 SimpleJpaReository 구현체 빈이 등록된다.
      • 어떻게? @SpringBootApplication 을 통해 자동으로 붙여지는 @EnableJpaRepositoriesJpaRepositoriesRegistrar 를 통해서 등록된다.
        • JpaRepositoriesRegistrar 는 ImportBeanDefinitionRegistrar 의 구현체이다
        • ImportBeanDefinitionRegistrar 는 프로그래밍을 통해 빈을 주입해준다.

1. extends JpaRepository없이 프로그래밍을 통해 빈을 주입하기

MyRepository.java


@Setter
public class MyRepository {

  private HashMap<Long, String> dataTable; // DB 테이블을 의미

  public String find(Long id) {
    return dataTable.getOrDefault(id, "");
  }

  public Long save(String data) {
    var newId = Long.valueOf(dataTable.size());
    this.dataTable.put(newId, data);
    return newId;
  }
}

MyRepositoryRegistrar.java


public class MyRepositoryRegistrar implements ImportBeanDefinitionRegistrar {

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
      BeanDefinitionRegistry registry) {

    // 주입할 빈에 대해 프로그래밍 하는 부분!!
    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
    beanDefinition.setBeanClass(MyRepository.class);
    beanDefinition.getPropertyValues().add("dataTable", Map.of(1L, "data"));
    // 여기까지!

    registry.registerBeanDefinition("myRepository", beanDefinition);
  }
}

TestApplication.java

@Import(MyRepositoryRegistrar.class) // 빈 주입!
@SpringBootTest
public class MyRepositoryTest {

  @Autowired
  MyRepository myRepository;

  @Test
  void myRepositoryTest() {
    // given
    var newData = "NEW DATA";
    var savedId = myRepository.save(newData);

    // when
    var newDataList = myRepository.find(savedId);

    // then
    System.out.println(newDataList);
  }
}

기존 Repository vs 새로운 JpaRepository

  • 기존 Repository
    • @Repository 을 클래스에 붙인다.
    • 앞서배운 RawJPA의 Repository 기능만 가진 구현체가 생성된다. (DB별 예외처리 등)
  • 새로운 JpaRepository
    • JpaRepository<Entity,ID> 인터페이스를 인터페이스에 extends 붙인다.
      • @NotRepositoryBean 된 상위 인터페이스들의 기능을 포함한 구현체가 프로그래밍된다. (@NotRepositoryBean = 빈생성 막음)
      • SpringDataJpa 에 의해 엔티티의 CRUD, 페이징, 정렬 기능 메소드들을 가진 빈이 등록된다. (상위 인터페이스들의 기능)

Repository 를 JpaRepository 로 간단하게 바꾸기!

// 변경 전
@Repository
public class UserRepository {

  @PersistenceContext
  EntityManager entityManager;

  public User insertUser(User user) {
    entityManager.persist(user);
    return user;
  }

  public User selectUser(Long id) {
    return entityManager.find(User.class, id);
  }
}

// 변경 후
public interface UserRepository extends JpaRepository<User, Long> {
  
}

2. Repository 기능을 제한하기!

  • JpaRepository 에서 사용할 메소드 제한하기
    • JpaRepository 는 기본적으로 모든 기능을 제공하기 때문에 리스크가 있을 수 있다.
    • 따라서, 아래와 같은 방법으로 원하는 기능 메소드만 구현하도록 제한할 수 있다.
    • findAll, saveAll, deleteAll등 쓰지 않는것들도 있을탠데 자동으로 나온다.

@RepositoryDefinition 을 인터페이스에 붙이는법

@RepositoryDefinition(domainClass = Comment.class, idClass = Long.class)
public interface CommentRepository {

    Comment save(Comment comment);

    List<Comment> findAll(); 
}

@NoRepositoryBean 인터페이스로 한번더 감싸는법

  • 상위 인터페이스 개념을 하나 더 만들어서 열어줄 메소드만 선언해준다.
@NoRepositoryBean
public interface MyRepository<T, ID extends Serializable> extends Repository<T, ID> { 
    <E extends T> E save(E entity); 
    List<T> findAll(); 
}

3. Repository 에 기능 추가하기!

delete() 메소드의 내부 기능 확인하기

  • delete 호출시 영속성 상태인지 확인한다.
    • 영속성 컨텍스트에 없다면(!em.contains(entity)) 엔티티를 조회해서 영속성 상태로 바꾼다.
    • 어차피 삭제할껀데 굳이?!?!
      • Cascade, orphanRemoval 에 의한 자식도 삭제가 누락되지 않도록!!!
  • JpaRepository 의 delete() 는 해당 엔티티를 바로 삭제하지 않는다.
    • remove() 메소드를 통해 remove 상태로 바꾼다.
public interface MyRepository {
	public void delete(User user);
    public List<String> findNameAll();
}

기능추가 예제

@Repository
@Transactional
public class MyRepositoryImpl implements MyRepository {

	@Autowired
	EntityManager entityManager; 

	//1. delete 쿼리가 바로 날아가도록 기능을 개선해보자
	@Override
	public void delete(User user) {
		entityManager.remove(user);
   	}
  	//2. findAll 할때 이름만 가져오도록 기능을 추가해보자
	@Override
	public List<String> findNameAll() {
    	return entityManager.createQuery("SELECT u.username FROM User AS u", String.class).getResultList();
  	}

}
profile
공부기록하기

0개의 댓글