[Spring] Repository Interface 메서드 실습

WOOK JONG KIM·2022년 11월 17일
0

패캠_java&Spring

목록 보기
51/103
post-thumbnail

entity

@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
@Data
@Builder
@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;

    @NonNull
    private String name;

    @NonNull
    private String email;

    private LocalDateTime createdAt;

    private LocalDateTime updatedAt;
}

repository

public interface UserRepository extends JpaRepository<User, Long> {
}

data.sql(/resources)

call next value for hibernate_sequence;

→ insert를 하는 경우 id를 자동 생성하는데 그것이 충돌나지 않기 위하여 id값을 증가

저 구문을 없애고 추가할려하면 기존에 id 1인 레코드가 존재하기에 충돌 일어남

call next value for hibernate_sequence;
insert into user(`id`, `name`, `email`, `created_At`, `updated_At`) values (1, 'martin', 'martin@fastcampus.com', now(), now());

call next value for hibernate_sequence;
insert into user(`id`, `name`, `email`, `created_At`, `updated_At`) values (2, 'dennis', 'dennis@fastcampus.com', now(), now());

call next value for hibernate_sequence;
insert into user(`id`, `name`, `email`, `created_At`, `updated_At`) values (3, 'sophia', 'sophia@slowcampus.com', now(), now());

call next value for hibernate_sequence;
insert into user(`id`, `name`, `email`, `created_At`, `updated_At`) values (4, 'james', 'james@slowcampus.com', now(), now());

call next value for hibernate_sequence;
insert into user(`id`, `name`, `email`, `created_At`, `updated_At`) values (5, 'martin', 'martin@another.com', now(), now());

여기서 칼럼명에서 '가 아닌 ₩(백틱)임!

Repository의 여러 동작 테스트

getOne

User user = userRepository.getOne(1L); 
// LAZY 패치 지원하므로 @Transactional 필요
System.out.println(user);
	Lazy 방식 -> 우선 레퍼런스만 가지고 있음
        @Override
        public T getOne(ID id) {

            Assert.notNull(id, ID_MUST_NOT_BE_NULL);
            return em.getReference(getDomainClass(), id);
        }

findbyId

User user = userRepository.findById(1L).orElseThrow(null); 
// orElseThrow 처리시 Optional 붙일 필요 X
System.out.println(user);

save

	@Test
    void crud(){ //save, read, update, delete
        // Flush는 쿼리를 조절하는 것이 아니라 DB 반영 시점 조절 -> 로그 상 변화 X
        
        userRepository.saveAndFlush(new User("new martin", "newmartin@abcd.com"));

        userRepository.findAll().forEach(System.out::println);
    }

count(갯수 출력)

	@Test
    void crud() { //save, read, update, delete
        long count = userRepository.count();

        System.out.println(count);
    }

existBy(id) -> 아이디에 해당 하는 값이 있는지 없는지 boolean으로 리턴

boolean exists = userRepository.existsById(1L);
Hibernate: 
    select
        count(*) as col_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?

쿼리에 카운트가 포함되어있음

delete(deletebyId)(All)(Batch)

userRepository.delete(userRepository.findById(1L).orElseThrow(RuntimeException::new));
userRepository.deleteById(1L);
userRepository.deleteAll();
// 각각을 delete 해서 성능이슈(개수 많을 시)
userRepository.deleteAll(userRepository.findAllById(Lists.newArrayList(1L, 3L)));

// 위는 Iterable을 통해 갯수당 delete 한번씩 수행
//delete 한번 수행
userRepository.deleteInBatch(userRepository.findAllById(Lists.newArrayList(1L, 3L)))

// 처음부터 delete 바로
userRepository.deleteAllInBatch();

실행전에 인덱스에 해당하는 레코드가 있는지 조회(select) 후 삭제하는 것을 쿼리를 통해 볼 수 있다

페이징

Page<User> users = userRepository.findAll(PageRequest.of(0,3));
        System.out.println("page:" + users);
        System.out.println("totalElements:  " + users.getTotalElements());
        System.out.println("totalPages:  " + users.getTotalPages());
        System.out.println("numberOfElements: " + users.getNumberOfElements());
        System.out.println("sort : " + users.getSort());
        System.out.println("size : " + users.getSize()); // 페이징 할때 나누는 크기

        users.getContent().forEach(System.out::println); 
page:Page 1 of 2 containing com.example.bookmanager2.domain.User instances
totalElements:  5
totalPages:  2
numberOfElements: 3
sort : UNSORTED
size : 3
User(id=1, name=martin, email=martin@fastcampus.com, createdAt=2022-11-17T19:31:50.567268, updatedAt=2022-11-17T19:31:50.567268)
User(id=2, name=dennis, email=dennis@fastcampus.com, createdAt=2022-11-17T19:31:50.570374, updatedAt=2022-11-17T19:31:50.570374)
User(id=3, name=sophia, email=sophia@slowcampus.com, createdAt=2022-11-17T19:31:50.570543, updatedAt=2022-11-17T19:31:50.570543)

Request.of(1,3)으로 할 시 id = 4,5에 해당하는 페이지가 출력됨

QueryByExample

ExampleMatcher matcher = ExampleMatcher.matching()
                .withIgnorePaths("name")
                .withMatcher("email", endsWith());

        // Example.of 의 인자인 Probe는 일종의 가짜 객체 의미
        // IgnorePath 뺀다면 이름에 해당되는 ma 까지 매칭에 사용
        
        Example<User> example = Example.of(new User("ma", "fastcampus.com"), matcher);

        userRepository.findAll(example).forEach(System.out::println);
User(id=1, name=martin, email=martin@fastcampus.com, createdAt=2022-11-17T19:38:59.734576, updatedAt=2022-11-17T19:38:59.734576)
User(id=2, name=dennis, email=dennis@fastcampus.com, createdAt=2022-11-17T19:38:59.738029, updatedAt=2022-11-17T19:38:59.738029)

매처가 없는 이 경우엔 두 파라미터에 해당되는 레코드만 조회

Example<User> example = Example.of(new User("martin", "martin@fastcampus.com"));
		User user = new User();
        user.setEmail("slow");

        ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("email", contains());
        Example<User> example = Example.of(user, matcher);

        userRepository.findAll(example).forEach(System.out::println);
User(id=3, name=sophia, email=sophia@slowcampus.com, createdAt=2022-11-17T19:47:50.714419, updatedAt=2022-11-17T19:47:50.714419)
User(id=4, name=james, email=james@slowcampus.com, createdAt=2022-11-17T19:47:50.714594, updatedAt=2022-11-17T19:47:50.714594)

SimpleJpaRepository 분석

create : insert

update는 ?

userRepository.save(new User("david", "david@fastcampus.com"));

User user = userRepository.findById(1L).orElseThrow(RuntimeException::new);
user.setEmail("martin-updated@fastcampus.com");

userRepository.save(user);
Hibernate: 
    insert 
    into
        user
        (created_at, email, name, updated_at, id) 
    values
        (?, ?, ?, ?, ?)
        
        ...
Hibernate: 
    update
        user 
    set
        created_at=?,
        email=?,
        name=?,
        updated_at=? 
    where
        id=?

경우에 따라 save가 insert또는 save로 동작하는 것을 볼 수 있다

SimpleJpaRepository에서는 JpaRepositoryImplementation 구현

JpaRepositoryImplementationJpaRepository 상속

-> 보통 우리가 JpaRepository에서 사용하는 메서드는 SimpleJpaRepository에서 구현체를 제공

save 메서드

@Transactional이 붙은 메서드는 메서드가 포함하고 있는 작업 중에 하나라도 실패할 경우 전체 작업을 취소

	@Transactional
	@Override
	public <S extends T> S save(S entity) {

		Assert.notNull(entity, "Entity must not be null.");

		if (entityInformation.isNew(entity)) {
			em.persist(entity); // 엔티티 매니저
			return entity;
		} else {
			return em.merge(entity);
		}
	}

persist -> insert , merge -> update

isNew

	@Transient // DATAJPA-622
	@Override
	public boolean isNew() {
		return null == getId();
	}

@Id에 해당하는 값이 Null이 아니라면 update

SaveAll(앞서 사용 시 각각에 대해 insert 쿼리문 생성하였음)

	@Transactional
	@Override
	public <S extends T> List<S> saveAll(Iterable<S> entities) {

		Assert.notNull(entities, "Entities must not be null!");

		List<S> result = new ArrayList<S>();

		for (S entity : entities) {
			result.add(save(entity));
		}

		return result;
	}

for로 save를 실행하는 것을 볼 수 있다

다른 메서드들도 구현 내용을 보면서 왜 이러한 쿼리가 실행되었는지 이해해보자!

profile
Journey for Backend Developer

0개의 댓글