sql에는 랜덤으로 row를 가져올 수 있는 query가 존재합니다.

"select * from word order by rand() limit 3;" 이런식으로 무작위로 3개 가져오는 쿼리도 실행이 가능합니다.

 

JPA는 메소드 이름으로 쿼리문을 실행해주는 편리한 인터페이스입니다.

"find + (Entity) + By + Column"의 형식의 메소드를 작성하면 이에 맞는 쿼리를 실행해줍니다.

친구와 미니 프로젝트를 하는 도중 랜덤 조회를 하기 위해 JPA 메소드를 작성하였습니다.

그렇게 차례대로 메소드를 써나가는데 OrderBy 뒤에 random과 관련된 단어가 보이지 않았습니다.

limit은 find 뒤에 Top, First, Last와 같은 키워드로 대체할 수 있습니다.

No property 'rand' found for type 'Word'

이렇게 어거지로 써봤지만 실행조차 되지않았습니다.

JPA 메소드에서 Order By는 컬럼명만 쓸 수 있으며, 랜덤 조회가 불가능하다는 결론이 나왔습니다.

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods

 

Spring Data JPA - Reference Documentation

Example 109. Using @Transactional at query methods @Transactional(readOnly = true) interface UserRepository extends JpaRepository { List findByLastname(String lastname); @Modifying @Transactional @Query("delete from User u where u.active = false") void del

docs.spring.io

JPA 공식문서를 확인해도 random 관련 내용은 볼 수 없었습니다.

그리고 랜덤으로 N개를 조회하기 위해 "Top20"을 메소드에 추가하였지만 "20"이라는 고정된 값이기 때문에 변수로 바꾸려고 합니다.

 

우선 제가 생각해낸 방법은 @Query 어노테이션을 사용하는 방법입니다.

JPA 메소드 위에 @Query(value = "query string")를 붙이는 방법으로, 메소드명을 아무렇게 작성하더라고 위에 작성한 query대로 실행됩니다.

그래서 이렇게 쓰긴 했는데 에러가 발생합니다??

 

구글링으로 nativeQuery라는 것을 발견하였고,

nativeQuery = true면 SQL, nativeQuery = false면 JPQL 문법으로 구분한다고 합니다.

nativeQuery = false가 default라서 JPQL이라고 인식을 하고있지만 JPQL 문법에 맞지 않아 발생한 에러였습니다.

이제는 에러가 발생하지 않습니다.

실행 시에도 단어가 무작위로, cnt만큼 잘 나오는 것을 확인할 수 있습니다.

 

이 방법 외에도 다른 방법이 있는지 모르겠습니다.

@NamedQuery를 Entity에 붙여 사용해보았지만 @NamedQuery는 JPQL 문법만 사용이 가능한 것 같아 실패하였습니다. ㅠㅠ

 

@GeneratedValue는 @Id와 함께 쓰는 어노테이션이며 auto_increment와 같은 자동 생성 전략을 결정합니다.

 

JPA에서 복합키를 구현하기 위한 IdClass와 EmbeddedId를 복습하던 도중 @GeneratedValue도 함께 사용해보았습니다.

 

IdClass 사용

우선 Primary Key를 위한 UserId 클래스를 생성합니다.

 

그리고 User Entity를 생성합니다.

클래스선언 위에 @IdClass를 사용해줍니다.

 

이제 DB가 만들어지는지 실행을 해볼까요?

음~ 잘 만들어졌네요!

 

이제 User table에 데이터를 넣어봅시다.

데이터를 넣기 위해 Test를 작성하였습니다.

User 테이블은 auto_increment이므로 id는 1이됩니다.

 

하지만 에러가 떠버립니다.

 

 

EmbeddedId 사용

다음은 Embeddable 입니다.

우선 Primary Key를 위한 UserId 클래스를 생성합니다.

 

그리고 User Entity입니다.

PK 위에 @EmbeddedId를 사용합니다.

 

이번에는 auto_increment조차 생기지 않습니다.

다른 방법들도 시도해보았으나 모두 실패했습니다 ㅠ

 

 

결론

사실 제가 코드를 잘못 짠줄 알고 구글링을 여러번 시도했습니다.

https://infondgndg91.blogspot.com/2019/11/hibernate-composite-identifiers.html

 

Hibernate - Composite Identifiers @EmbeddedId @IdClass With @GeneratedValue is impossible

ndgndg91

infondgndg91.blogspot.com

그러던 중 이 블로그를 발견하였고, 이분도 어떤 커뮤니티에서 물어보셨던 것인지

@GeneratedValue를 @IdClass, @EmbeddedId와 함께 사용하는 것이 불가능하다는 답변을 받은것 같습니다.

 

앞으로 주의해서 사용해야 할 것 같습니다.

말 그대로 페이징, 데이터를 페이지 단위로 나눠서 보여주는 작업입니다.

이를 위해 JPA에서는 페이지 정보와 정렬 기준으로 페이징 처리를 하며 PageRequest, Pageable, Sort 객제를 사용합니다.

1
2
3
4
@Repository
public interface ConferenceRepository extends JpaRepository<Conference, Long> {
    Page<Conference> findAll(Pageable pageable);
}
cs

Repository 구성입니다.

return type을 Page 객체로 감싸줍니다.

Service에서 PageRequest 객체를 넘겨주면 Repository에서 Pageable 객체로 받습니다.

Pageable 객체에는 페이징 정보와 정렬 기준을 모두 포함하므로 전달받는 파라미터에 반드시 포함되어야합니다.

 

1
2
3
4
5
6
7
8
9
10
@Service
public class ConferenceServiceImpl implements ConferenceService {
    @Override
    public ConferenceFindAllGetRes findAllConference(int page, int size) {
        Sort sorts = Sort.by(Sort.Direction.DESC, "id");
        PageRequest pageRequest = PageRequest.of(page, size, sorts);
    
        return ConferenceFindAllGetRes.of(conferenceRepository.findAll(pageRequest), pageRequest, sorts);
    }
}
cs

Repository에서는 페이징 정보를 Pageable로 받지만 Service에서 호출 시 보낼 페이징 정보는 PageRequest 객체입니다.

딱 Request만 봐도 요청 잘하게 생겼죠?

PageRequest.of(page, size, sorts);

page: 현재 페이지 (코드에서 첫 페이지는 0입니다.)

size: 페이지에서 출력할 데이터의 갯수

sorts: 정렬기준, 방법 (ex. id 기준 asc or desc)

 

그럼 이런식으로 정렬기준에 맞게 출력이 됩니다!

이거를 이제 findByTitle이나 findByConferenceCategoryId와 같은 메소드에도 적용할 수 있습니다

+ Recent posts