Dev/JPA

JPA Repository Interface 상세 2

OK-가자 2022. 3. 2. 17:23

먼저 Data.sql 사용해보자.

Data.sql 파일을 리소스 하위에 두면 jPA가 로딩할때 해당 쿼리를 한번 실행해 준다.!!!!
Test할때 개 꿀~

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

call next value for hibernate_sequence;
insert into user (`id`,`name`,`email`,`created_at`,`updated_at`) values (2, 'demis','demis@fast.com',now(),now());

call next value for hibernate_sequence;
insert into user (`id`,`name`,`email`,`created_at`,`updated_at`) values (3, 'sopia','sopia@slow.com',now(),now());

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

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

이렇게 미리 들어갈 dml을 넣어준다.

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Sequence "HIBERNATE_SEQUENCE" not found; SQL statement: 😡😡
nested exception is org.h2.jdbc.JdbcSQLSyntaxErrorException: Table "USER" not found; SQL statement:
insert into user (id,name,email,created_at,updated_at) values (1, 'martin','martin@fast.com',now(),now()) 😡😡😡😡

😡만약 위의같은 애러가 나면 아래와 같이 설정해주면 된다.😡

application.yml 설정을 변경해준다.

spring:
  h2:
    console:
      enabled: true
  jpa:
    hibernate:
      ddl-auto: create // 버젼 이슈때문에 넣었음..ddl을 자동으로 만들어줘야함..
    show-sql: true
    properties:
      hibernate:
        format_sql: true
    defer-datasource-initialization: true // 버젼 이슈때문에 넣었음..

😡 버전이 맞지 않는다면 고생 할 수 있다 오류를 잘 보고 고쳐보자...ㅠㅠ내 3시간...


✅실행하면서 DDL 생성되고

✅data.sql 잘 읽었고!!

✅Test 출력 값 잘 나온다. 휴.....

😎 JPA Repository Interface 상세 1에서 살펴봤던 JPA 기능들 사용해보자!!

기능의 설명은 JPA Repository Interface 상세 1 에 있으니 참고바라며
이 결과 값의 포인트는
사용한 메서드별로 Hibernate가 무슨 쿼리를 만들었는지
결과값이 어떻게 나오는지가 중요하다!!!😎😎

FIND


 /*FIND = SELECT */
   List<User> users = userRepository.findAll(Sort.by(Sort.Direction.DESC,"name"));


    List<User> users = userRepository.findAllById(Lists.newArrayList(1L,3L,5L));


SAVE

 /*SAVE = INSERT*/
   User user1 = new User("jack","jack@fast.com");
   User user2 = new User("steve","steve@fast.com");
   userRepository.saveAll(Lists.newArrayList(user1,user2));


 userRepository.save(user1);


*** GET & FIND

/*GET*/
  User user = userRepository.getOne(1L); 
  System.out.println(user);

😡😡😡 에러가 뜰꺼다.

could not initialize proxy [com.practice.jpa.bookmanager.domain.User#1] - no Session

중요한건 getOne는 Lazy한 패치를 지원하고 있다.
그래서 @Transactional을 메서드 위에 붙여주면 된다.

 User user = userRepository.findById(1L).orElse(null); //orElse는 "결과값이 없으면 널로 보여줘" 라는 뜻이다.

GET의 getOne과 findbyId는 Lazy Loading과 Eager Loading 차이가 있는데 이건 나중에 자세히 다뤄야 할거 같다.


FLUSH & COUNT

/*flush*/
userRepository.flush(); // flush는 쿼리에 영향을 가지 않는데 다음에 배울 영속성 파트에서 더 자세히 알아볼수 있다.

/*count*/
        long count = userRepository.count();
        System.out.println(count);


EXISTS

/*exists */
        boolean exists = userRepository.existsById(1L);
        System.out.println(exists);

🤔 exists(존재) 함수를 썼는데 왜 쿼리는 count(*)를 쓸까?

existsById 메서드를 구현해놓은 라이브러리에 들어가서
QueryUtils 클래스의 getExistsQueryString 메서드가 리턴하는 걸 보면,

return String.format(COUNT_QUERY_STRING, countQueryPlaceHolder, entityName) + whereClause;

COUNT_QUERY_STRING을 사용하여 호출한다.
COUNT_QUERY_STRING = "select count(%s) from %s x"; 이렇게 정의 되어있다.

그러니 count가 나오는 거 같다.


DELETE

🤚 delete는 Null이면안됨. delete 쿼리 실행하기전 Select 쿼리로 존재 여부를 확인하기때문!!

//    entity를 사용하여 지우는 쿼리
    userRepository.delete(userRepository.findById(1L).orElseThrow(RuntimeException::new)); 


📝 실행 쿼리를 보면 딜리트 되기전엔 그 엔티티를 찾는다.
🤔🤚 하지만, 엔티티를 찾는 쿼리를 한번 더 실행하니.... 그럼 하나만 지울때는 ID(PK)를 사용하는게 좋겠다.


//    ID 값을 이용하여 삭제    
    userRepository.deleteById(1L);



📝 이렇게 하면 엔티티를 찾는쿼리를 안한다. 그럼 하나만 지울때는 ID(PK)를 사용하는게 좋겠다.
🤔🤚 그럼 여러게 지워 볼까?


        // ID를 사용하여 여러 컬럼 지우기
        userRepository.deleteAllById(Lists.newArrayList(1L,3L));


** 🤔🤚 잠깐만... deleteAllById 하니까 entity개수 만큼 쿼리를 때려버리네??? 성능이슈가 발생하겠군! 딜리트 하나 할때 마다 셀렉트도 하니... 컬럼 5개 지우면 쿼리를 10번 때려?? **



//      다 지우기
        userRepository.deleteAll();


** 🤔🤚 잠깐만... 위랑 똑같이 deleteAll을 하니까 entity개수 만큼 쿼리를 때려버리네??? 성능이슈가 발생하겠군! 어떻하지???? **


✅ 그래서 사용하는 deleteAllInBatch

        // 모든 엔티티 지우기
        userRepository.deleteAllInBatch();
        // ID를 사용하여 여러 컬럼 지우기
        userRepository.deleteAllInBatch(userRepository.findAllById(Lists.newArrayList(1L,3L)));


✅📝 deleteAllInBatch를 사용하여 모든 컬럼 다 지울때 깔끔하게 하나의 쿼리로 지워준다. 그리고 여러 id값을 이용하여 지울때도 where절에 OR문을 사용하여 깜끔하지 지워준다.


PAGEING

✅ 페이징은 정말 많은 곳에서 사용된다. 하지만 직접 페이징을 하려면 야간 복잡한 과정을 거쳐야한다. 하지만 이 페이징 기능을 사용 하면 편하고 쉽게 페이징이 가능하다.

        // paging !!! 사용
        Page<User> users = userRepository.findAll(PageRequest.of(1,3)); // page는 0페이지 부터 시작한다.

        System.out.println(" 페이징 정보 : " + users);
        System.out.println(" Size(한 페이지에 들어갈 컬럼 수) : 한 페이지에 " + users.getSize() + "개의 게시물이 들어가도록 해뒀다.");
        System.out.println(" totalElements(총 컬럼 수) : 총 " + users.getTotalElements() + "개의 게시물이 있다.");
        System.out.println(" totalPages(전체 페이지 수) : 총 " + users.getTotalPages() + "페이지가 있다.");
        System.out.println(" Number(현재 페이지) : 지금 선택된 페이지는 " + users.getNumber() + "페이지 이다.");
        System.out.println(" numberOfElements(내가 선택한 페이지의 컬럼개수) : 현재 이 페이지에는 " + users.getNumberOfElements() + "개의 게시물이 있다.");
        System.out.println(" Sort(정렬 정보) : " + users.getSort() + "정렬 되어있다.");
        users.getContent().forEach(System.out::println); // 현재 선택한 페이지의 컬럼들.

JPA 페이징 하기. 정말 간단하구나!!


QueryByExample (QBE)

✅ 쿼리바이이그젬플익스큐터 (QBE) : 엔티티를 이그젬플로 만들고 메쳐를 선언해 줌 으로서 필요한 쿼리를 만드는 방법
✅ 검색 조건문

        /*exampleMatcher*/

        ExampleMatcher matcher = ExampleMatcher.matching()                                  //
                .withIgnorePaths("name")                                                    // name은 매칭 하지 않겠다.
                .withMatcher("email",endsWith());                                // email에 대해서 확인하겠다. 끝에있다는걸
        Example<User> example = Example.of(new User("ma","fast.com"),matcher);  //

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


음... 이해가 잘 안되는데.... 다른 방법으로 예시를 보자...

        User user = new User();
        user.setEmail("slow");

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

설명을 들어보니 ... 검색을 할때는 QueryDSL 같은거 사용한다고 한다. 그게 뭔지 모르겠지만. 일단 Example은 문자만 가능하다는 한계가 있어서 안쓴다고 한다. 다행인건가?


UPDATE

        /*UPDATE*/
        userRepository.save(new User("david", "david@fast.com"));
        // 만약 존재하는 엔티티이면 UPDATA를 한다.
        // 존재하지 않는 엔티티이면 insert한다.
        User user = userRepository.findById(1L).orElseThrow(RuntimeException::new);
        user.setEmail("david-update@fast.com");

        userRepository.save(user);

'Dev > JPA' 카테고리의 다른 글

JPA 쿼리메소드 정렬  (0) 2022.03.02
JPA 쿼리메소드(QueryMethod)  (0) 2022.03.02
JPA Repository Interface 상세 1  (0) 2022.03.02
영속성 컨텍스트  (0) 2022.03.02
JPA 실습 해보자 1(H2 DB)  (0) 2022.03.02