asap
'SPRING BOOT > SPRING DATA JPA' 카테고리의 다른 글
Spring Data JPA 조회 페이징 처리 (0) | 2022.11.19 |
---|---|
Spring Data JPA [SELECT] 쿼리메소드 (0) | 2022.11.05 |
Spring Data JPA 연동하기 + 간단한 Insert (0) | 2022.10.10 |
Spring Data JPA 개요 (0) | 2022.10.10 |
asap
Spring Data JPA 조회 페이징 처리 (0) | 2022.11.19 |
---|---|
Spring Data JPA [SELECT] 쿼리메소드 (0) | 2022.11.05 |
Spring Data JPA 연동하기 + 간단한 Insert (0) | 2022.10.10 |
Spring Data JPA 개요 (0) | 2022.10.10 |
조회할 데이터가 너무 많은 경우 자주 사용하게 되는 Paging 처리.
사실 필요한 경우는 너무 많고 예제도 다른 블로그에 잘 정리되어 있지만,
막상 만들자니 조회할 전체 건수를 불러오고 페이징할 단위별로 계산해주고 과정이 너무 복잡하다.
귀찮은 페이징을 Spring Data JPA에서 지원하는 방법으로 처리해보자.
간단한 예시로 이전 포스팅과 동일하게 게임 길드원에 대한 정보를 조회해보자.
GuildRepository에 Paging 메소드를 선언한다.
// 길드원 기여도에 따른 오름차순 Paging
public Page<Guild> findByContributionGreaterThan(int contribution, Pageable paging);
test 코드를 작성한다.
@Test
public void testFindByUserGreaterThan() {
Pageable paging = PageRequest.of(1, 10);
Page<Guild> results = guildRepo.findByContributionGreaterThan(100, paging);
results.forEach(guild -> System.out.println(guild));
}
코드를 보면 Pageable 객체와 PageRequest.of(1, 10)가 눈에 띈다.
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.contribution>? limit ?, ?
Hibernate: select count(guild0_.index_no) as col_0_0_ from tbml_guild_info guild0_ where guild0_.contribution>?
Guild(indexNo=16, level=16, userName=cookie16, userJob=도적, authority=길드원, contribution=320, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=17, level=17, userName=cookie17, userJob=마법사, authority=길드원, contribution=340, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=18, level=18, userName=cookie18, userJob=기사, authority=길드원, contribution=360, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=19, level=19, userName=cookie19, userJob=기사, authority=길드원, contribution=380, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=20, level=20, userName=cookie20, userJob=기사, authority=길드원, contribution=400, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=21, level=21, userName=cookie21, userJob=마법사, authority=길드원, contribution=420, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=22, level=22, userName=cookie22, userJob=마법사, authority=길드원, contribution=440, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=23, level=23, userName=cookie23, userJob=마법사, authority=길드원, contribution=460, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=24, level=24, userName=cookie24, userJob=도적, authority=길드원, contribution=480, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=25, level=25, userName=cookie25, userJob=기사, authority=길드원, contribution=500, guildRegDate=2022-11-05 15:21:48.0)
실행 결과를 보면 주목할 점이 두 가지 보인다.
1. 테스트는 한번 실행했지만 SELECT 작업은 두 번 실행되었다.
=> 데이터 조회를 위한 SELECT
=> COUNT(*) 를 위한 SELECT
2. 조회 조건이 기여도(contribution)가 100 이상인 유저를 조회
=> DB에 100이상인 길드원은 45명이나 실제 조회된 것은 10명분이다. 즉, 페이징이 잘 처리된 것을 확인할 수 있다.
이제 본격적으로 Paging 처리에 대해 살펴보자.
1. Pageable 인터페이스와 PageRequest.of( )
Pageable 인터페이스는 페이징에 대한 많은 메소드가 존재하여 PageRequest 클래스를 통해 사용하는 것이 편하다.
모든 쿼리 메소드는 마지막 파라미터를 Pageable 인터페이스를 사용할 수 있다.
public Page<Guild> findByContributionGreaterThan(int contribution, Pageable paging);
당연히 PageRequest에는 페이징 처리에 대해 필요한 것들이 들어있다.
그 중 가장 편리하고 자주 사용하는 것이 PageRequest.of() 이다.
Pageable paging = PageRequest.of(0, 10);
# SpringBoot 2.0 아래의 버전에서는 new PageRequest()로 생성하지만 2.0에 들어서는 PageRequest.of()를 사용한다.
2. Sort
페이징된 데이터를 정렬할 때는 Sort 클래스를 이용한다.
Order By를 통한 처리도 가능하지만 PageRequest와 Sort를 사용하면 Order By와 달리 추가적인 기능을 쓸 수 있다.
아래는 실제 PageRequest.class 에 정의된 of 메소드이다.
// 페이지 번호와 페이지당 로우 수를 지정 ( page+1 번 페이지, 10개 로우 )
public static PageRequest of(int page, int size) {
return of(page, size, Sort.unsorted());
}
// 페이지 번호, 로우 수, 정렬방향
public static PageRequest of(int page, int size, Sort sort) {
return new PageRequest(page, size, sort);
}
// 페이지 번호, 로우 수, 정렬방향, 속성
public static PageRequest of(int page, int size, Direction direction, String... properties) {
return of(page, size, Sort.by(direction, properties));
}
Order By와 Sort의 가장 큰 차이는 세 번째 of 메소드의 [String... properties] 파라미터이다.
Order By를 사용하기 위해서는 쿼리 메소드에 OrderByLevelDesc() 처럼 사용하여 메소드명이 길어지지만
Sort를 통해 정렬하게 되면 속성 파라미터를 이용하여 정렬 방향을 지정할 수 있다.
@Test
public void testFindByUserGreaterThanPageSort() {
Pageable paging = PageRequest.of(1, 10, Sort.Direction.DESC, "level");
Page<Guild> results = guildRepo.findByContributionGreaterThan(100, paging);
results.forEach(guild -> System.out.println(guild));
}
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.contribution>? order by guild0_.level desc limit ?, ?
Hibernate: select count(guild0_.index_no) as col_0_0_ from tbml_guild_info guild0_ where guild0_.contribution>?
Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)
예제의 코드와 위의 코드와의 차이점은 PageRequest.of( ... ) 의 파라미터에 Sort 관련 파라미터가 추가된 것이다.
그리고 조회결과에서도 예제와 동일한 쿼리메소드를 사용했음에도 전혀 다른 결과가 나오는 것을 볼 수 있다.
3. Page<T>
보통 JPA를 통해 조회한 결과를 List<T>를 사용하여 관리하지만, Page<T> 타입을 이용하면
SpringMVC와 연동할 때 많은 이점을 제공해 준다. 즉, 웹페이지와 관련된 작업에 유용한 기능을 제공해준다.
@Test
public void testFindByUserGreaterThanPageSort() {
Pageable paging = PageRequest.of(1, 10, Sort.Direction.DESC, "level");
Page<Guild> results = guildRepo.findByContributionGreaterThan(100, paging);
// Page<T> 부과기능 : 내용과 건수를 한번에 조회 가능
System.out.println("PAGE SIZE: " + results.getSize());
System.out.println("TOTAL PAGES: " + results.getTotalPages());
System.out.println("TOTAL COUNT: " + results.getTotalElements());
System.out.println("NEXT: " + results.nextPageable());
List<Guild> list = results.getContent();
results.forEach(guild -> System.out.println(list));
}
Page<T> 객체인 results를 통해 페이징에 대한 정보를 출력하는 것을 확인할 수 있다.
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.contribution>? order by guild0_.level desc limit ?, ?
Hibernate: select count(guild0_.index_no) as col_0_0_ from tbml_guild_info guild0_ where guild0_.contribution>?
PAGE SIZE: 10
TOTAL PAGES: 5
TOTAL COUNT: 45
NEXT: Page request [number: 2, size 10, sort: level: DESC]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
[Guild(indexNo=40, level=40, userName=cookie40, userJob=마법사, authority=길드원, contribution=800, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=39, level=39, userName=cookie39, userJob=도적, authority=길드원, contribution=780, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=38, level=38, userName=cookie38, userJob=도적, authority=길드원, contribution=760, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=37, level=37, userName=cookie37, userJob=도적, authority=길드원, contribution=740, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=36, level=36, userName=cookie36, userJob=마법사, authority=길드원, contribution=720, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=35, level=35, userName=cookie35, userJob=기사, authority=길드원, contribution=700, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=34, level=34, userName=cookie34, userJob=기사, authority=길드원, contribution=680, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=33, level=33, userName=cookie33, userJob=기사, authority=길드원, contribution=660, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=32, level=32, userName=cookie32, userJob=도적, authority=길드원, contribution=640, guildRegDate=2022-11-05 15:21:48.0), Guild(indexNo=31, level=31, userName=cookie31, userJob=기사, authority=길드원, contribution=620, guildRegDate=2022-11-05 15:21:48.0)]
Page에 대한 사이즈와 ROW의 수 등 페이지에 대한 정보를 한번에 확인이 가능하며,
Web에 페이지 정보를 전달하여 더욱 유용하게 사용할 수 있다.
Page<T> 가 제공해주는 유용한 메소드는 다음과 같다.
int getNumber() | 현재 페이지이 정보 |
int getSize() | 한 페이지의 크기 |
int getTotalPages() | 전체 페이지의 수 |
int getNumberOfElements() | 결과 데이터 수 |
boolean hasPreviousPage() | 이전 페이지의 존재 여부 |
boolean hasNextPage() | 다음 페이지의 존재 여부 |
boolean isLastPage() | 마지막 페이지인가 여부 |
Pageable nextPageable() | 다음 페이지 객체 |
Pageable previousPageable() | 이전 페이지 객체 |
List<T> getCount() | 조회된 데이터 |
boolean hasContent() | 결과 존재 여부 |
Sort getSrot() | 검색에 사용된 Sort 정보 |
※ Spring Data JPA는 구멍가게코딩단의 [초급 개발자들을 위한 가볍고 넓은 스프링부트 스타트 스프링 부트2.0] 책을 참고하였습니다.
Spring Data JPA : @Qeury를 이용한 데이터 조회 (0) | 2022.11.19 |
---|---|
Spring Data JPA [SELECT] 쿼리메소드 (0) | 2022.11.05 |
Spring Data JPA 연동하기 + 간단한 Insert (0) | 2022.10.10 |
Spring Data JPA 개요 (0) | 2022.10.10 |
지난 글에서 만든 Guild 테이블과 길드 구성원의 데이터를 통해
Spring Data JPA에서 데이터를 조회(SELECT) 하는 방법을 정리한다.
Spring Data JPA는 메소드의 이름을 통해 쿼리를 실행하도록 지원한다.
이때 메소드 이름은 몇 가지의 규칙을 가지게 된다. 이 규칙이 곧 쿼리 문법이라 봐도 무방할 것 같다.
List<T> find[EntityName]By[Colunm]( String str ) ;
Return의 경우 Collection<T> 를 사용하며 그 중 List<T> , Page<T> 를 자주 사용한다.
- 장점
JPA의 특징인 "특정 DB에 종속되지 않는다"는 방식을 그대로 따르기에
설정된 DB에 대한 SQL 자동 처리를 지원해준다. 즉, 생산성이 향상된다.
- 단점
쿼리메소드는 간단한 쿼리를 수행할 수 있는 기능을 제공하지만 실제로 실무의 쿼리는
단순한 [SELECT * FROM TBL WHERE COL1 = 'A'] 처럼 간단하지 않고 훨씬 복잡하다.
이때 쿼리메소드를 통한 조회는 한계가 있기에 QueryDsl , JPQL에 대해 공부해야 한다.
* spring 공식 docs 참고
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.core-concepts
가장 많이 쓰는 기능들에 대해 코드를 정리하며, 그 외의 조회 조건은 하단의 표를 참고하자.
Repository Interface 를 통해 이번에 정리할 내용을 대략적으로 볼 수 있다.
중요하게 봐야하는 부분은 다음과 같다.
- 각 예제코드에서 guildRepo 객체를 통해 호출하는 메소드
- 각 예제의 로그에서 볼 수 있는 생성된 쿼리문 (Hibernate)
public interface GuildRepository extends CrudRepository<Guild, Long>{
// SELECT 조회
public List<Guild> findGuildByUserName(String userName);
// SELECT LIKE String% 구문 조회
public Collection<Guild> findByUserJobStartingWith(String userJob);
// SELECT LIKE %String 구문 조회
public Collection<Guild> findByUserJobEndingWith(String userJob);
// SELECT LIKE %String% 구문 조회
public Collection<Guild> findByUserJobContaining(String userJob);
// OR 조건 처리
public Collection<Guild> findByUserJobContainingOrUserJobContaining(String userJob1, String userJob2);
// AND 조건 처리
public Collection<Guild> findByUserJobContainingAndUserJobContaining(String userJob1, String userJob2);
// 부등호 처리 ( level > ? :: GreaterThan)
public Collection<Guild> findByLevelGreaterThan(int level);
// 부등호 처리 ( level < ? :: LessThan)
public Collection<Guild> findByLevelLessThan(int level);
// 부등호 처리 ( level <= ? :: LessThanEqual)
public Collection<Guild> findByLevelLessThanEqual(int level);
// userJob% AND Level > 40 Order by 처리
public Collection<Guild> findByUserJobStartingWithAndLevelGreaterThanOrderByLevelDesc(String userJob, int level);
}
2-1 단순한 SELECT - WHERE
길드 유저의 이름을 통해 길드원을 조회
@Test
public void testByTitle() {
System.out.println(":::: find - By ::::");
guildRepo.findGuildByUserName("cookie27").forEach(guild -> System.out.println(guild));
}
:::: find - By ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.user_name=?
Guild(indexNo=27, level=27, userName=cookie27, userJob=도적, authority=길드원, contribution=540, guildRegDate=2022-11-05 15:21:48.0)
2-2 LIKE 조회
LIKE 검색은 와일드카드(%)의 위치에 따라 별도의 메소드를 사용한다. (String%, %String , %String%)
String% :: StartingWith
%String :: EndingWith
%String% :: Contatining
@Test
public void testByUserJobStartingWith() {
System.out.println(":::: Like String% ::::");
Collection<Guild> results = guildRepo.findByUserJobStartingWith("기");
results.forEach(guild -> System.out.println(guild));
}
@Test
public void testByUserJobEndingWith() {
System.out.println(":::: Like %String ::::");
Collection<Guild> results = guildRepo.findByUserJobEndingWith("적");
results.forEach(guild -> System.out.println(guild));
}
@Test
public void testByUserJobContaining() {
System.out.println(":::: Like %String% ::::");
Collection<Guild> results = guildRepo.findByUserJobContaining("법");
results.forEach(guild -> System.out.println(guild));
}
:::: Like String% ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.user_job like ? escape ?
Guild(indexNo=3, level=3, userName=cookie3, userJob=기사, authority=길드원, contribution=60, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=4, level=4, userName=cookie4, userJob=기사, authority=길드원, contribution=80, guildRegDate=2022-11-05 15:21:47.0)
:::: Like %String ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.user_job like ? escape ?
Guild(indexNo=5, level=5, userName=cookie5, userJob=도적, authority=길드원, contribution=100, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=8, level=8, userName=cookie8, userJob=도적, authority=길드원, contribution=160, guildRegDate=2022-11-05 15:21:47.0)
:::: Like %String% ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.user_job like ? escape ?
Guild(indexNo=1, level=1, userName=cookie1, userJob=마법사, authority=길드원, contribution=20, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=2, level=2, userName=cookie2, userJob=마법사, authority=길드원, contribution=40, guildRegDate=2022-11-05 15:21:47.0)
2-3 OR / AND 연산
OR와 AND는 조건이 여러개인 경우를 나타내므로 [Colunm1] Or [Colunm2] 와 같이 메소드이름을 정한다.
@Test
public void testByTitleContainingOrContentContaining() {
System.out.println(":::: OR ::::");
Collection<Guild> results = guildRepo.findByUserJobContainingOrUserJobContaining("도", "법사");
results.forEach(guild -> System.out.println(guild));
}
@Test
public void testFindByUserJobContainingAndUserJobContaining() {
System.out.println(":::: AND ::::");
Collection<Guild> results = guildRepo.findByUserJobContainingAndUserJobContaining("마", "법사");
results.forEach(guild -> System.out.println(guild));
}
:::: OR ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.user_job like ? escape ? or guild0_.user_job like ? escape ?
Guild(indexNo=1, level=1, userName=cookie1, userJob=마법사, authority=길드원, contribution=20, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=2, level=2, userName=cookie2, userJob=마법사, authority=길드원, contribution=40, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=5, level=5, userName=cookie5, userJob=도적, authority=길드원, contribution=100, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=7, level=7, userName=cookie7, userJob=마법사, authority=길드원, contribution=140, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=8, level=8, userName=cookie8, userJob=도적, authority=길드원, contribution=160, guildRegDate=2022-11-05 15:21:47.0)
:::: AND ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where (guild0_.user_job like ? escape ?) and (guild0_.user_job like ? escape ?)
Guild(indexNo=1, level=1, userName=cookie1, userJob=마법사, authority=길드원, contribution=20, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=2, level=2, userName=cookie2, userJob=마법사, authority=길드원, contribution=40, guildRegDate=2022-11-05 15:21:47.0)
2-4 부등호 연산
부등호 연산자를 처리하기 위해 아래와 같은 메소드를 지원한다.
초과 : num > 10 → GreaterThan
미만 : num < 10 → LessThan
이상 : num >= 10 → GreaterThanEquals
이하 : num <= 10 → LessThanEquals
@Test
public void testFindByLevelGreaterThan() {
System.out.println(":::: Greater ::::");
Collection<Guild> results = guildRepo.findByLevelGreaterThan(20);
results.forEach(guild -> System.out.println(guild));
}
@Test
public void testFindByLevelLessThan() {
System.out.println(":::: Less ::::");
Collection<Guild> results = guildRepo.findByLevelLessThan(20);
results.forEach(guild -> System.out.println(guild));
}
@Test
public void testFindByLevelLessThanEqual() {
System.out.println(":::: LessThanEqual ::::");
Collection<Guild> results = guildRepo.findByLevelLessThanEqual(20);
results.forEach(guild -> System.out.println(guild));
}
:::: Greater ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.level>?
Guild(indexNo=21, level=21, userName=cookie21, userJob=마법사, authority=길드원, contribution=420, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=22, level=22, userName=cookie22, userJob=마법사, authority=길드원, contribution=440, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=23, level=23, userName=cookie23, userJob=마법사, authority=길드원, contribution=460, guildRegDate=2022-11-05 15:21:48.0)
:::: Less ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.level<?
Guild(indexNo=1, level=1, userName=cookie1, userJob=마법사, authority=길드원, contribution=20, guildRegDate=2022-11-05 15:21:47.0)
Guild(indexNo=2, level=2, userName=cookie2, userJob=마법사, authority=길드원, contribution=40, guildRegDate=2022-11-05 15:21:47.0)
:::: LessThanEqual ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where guild0_.level<=?
Guild(indexNo=19, level=19, userName=cookie19, userJob=기사, authority=길드원, contribution=380, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=20, level=20, userName=cookie20, userJob=기사, authority=길드원, contribution=400, guildRegDate=2022-11-05 15:21:48.0)
2-5 ORDER BY (정렬)
정렬 기준이 되는 컬럼을 통해 메소드 명을 작성한다
find...By...OrderBy[Colunm][Asc/Desc] ();
이번 예는 조금 더 복잡하게
"직업이 '도' 로 시작하며 레벨이 40 보다 높은 유저정보를 오름(내림)차순으로 정렬" 한 예이다.
@Test
public void testFindByUserJobStartingWithAndLevelGreaterThanOrderByLevelDesc() {
System.out.println(":::: LIKE + GREATER + ORDER BY(DESC) ::::");
Collection<Guild> results = guildRepo.findByUserJobStartingWithAndLevelGreaterThanOrderByLevelDesc("도", 40);
results.forEach(guild -> System.out.println(guild));
}
@Test
public void testFindByUserJobStartingWithAndLevelGreaterThanOrderByLevelAsc() {
System.out.println(":::: LIKE + GREATER + ORDER BY(ASC) ::::");
Collection<Guild> results = guildRepo.findByUserJobStartingWithAndLevelGreaterThanOrderByLevelAsc("도", 40);
results.forEach(guild -> System.out.println(guild));
}
:::: LIKE + GREATER + ORDER BY(ASC) ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where (guild0_.user_job like ? escape ?) and guild0_.level>? order by guild0_.level asc
Guild(indexNo=43, level=43, userName=cookie43, userJob=도적, authority=길드원, contribution=860, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=44, level=44, userName=cookie44, userJob=도적, authority=길드원, contribution=880, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=46, level=46, userName=cookie46, userJob=도적, authority=길드원, contribution=920, guildRegDate=2022-11-05 15:21:48.0)
:::: LIKE + GREATER + ORDER BY(DESC) ::::
Hibernate: select guild0_.index_no as index_no1_0_, guild0_.authority as authorit2_0_, guild0_.contribution as contribu3_0_, guild0_.guild_reg_date as guild_re4_0_, guild0_.level as level5_0_, guild0_.user_job as user_job6_0_, guild0_.user_name as user_nam7_0_ from tbml_guild_info guild0_ where (guild0_.user_job like ? escape ?) and guild0_.level>? order by guild0_.level desc
Guild(indexNo=46, level=46, userName=cookie46, userJob=도적, authority=길드원, contribution=920, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=44, level=44, userName=cookie44, userJob=도적, authority=길드원, contribution=880, guildRegDate=2022-11-05 15:21:48.0)
Guild(indexNo=43, level=43, userName=cookie43, userJob=도적, authority=길드원, contribution=860, guildRegDate=2022-11-05 15:21:48.0)
# 더 많은 쿼리메소드 표현방식은 Spring 공식 docs에 정리되어 있다. (Qeury Creation 항목)
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
Table 3. Supported keywords inside method namesKeywordSampleJPQL snippet
Distinct | findDistinctByLastnameAndFirstname | select distinct … where x.lastname = ?1 and x.firstname = ?2 |
And | findByLastnameAndFirstname | … where x.lastname = ?1 and x.firstname = ?2 |
Or | findByLastnameOrFirstname | … where x.lastname = ?1 or x.firstname = ?2 |
Is, Equals | findByFirstname,findByFirstnameIs,findByFirstnameEquals | … where x.firstname = ?1 |
Between | findByStartDateBetween | … where x.startDate between ?1 and ?2 |
LessThan | findByAgeLessThan | … where x.age < ?1 |
LessThanEqual | findByAgeLessThanEqual | … where x.age <= ?1 |
GreaterThan | findByAgeGreaterThan | … where x.age > ?1 |
GreaterThanEqual | findByAgeGreaterThanEqual | … where x.age >= ?1 |
After | findByStartDateAfter | … where x.startDate > ?1 |
Before | findByStartDateBefore | … where x.startDate < ?1 |
IsNull, Null | findByAge(Is)Null | … where x.age is null |
IsNotNull, NotNull | findByAge(Is)NotNull | … where x.age not null |
Like | findByFirstnameLike | … where x.firstname like ?1 |
NotLike | findByFirstnameNotLike | … where x.firstname not like ?1 |
StartingWith | findByFirstnameStartingWith | … where x.firstname like ?1 (parameter bound with appended %) |
EndingWith | findByFirstnameEndingWith | … where x.firstname like ?1 (parameter bound with prepended %) |
Containing | findByFirstnameContaining | … where x.firstname like ?1 (parameter bound wrapped in %) |
OrderBy | findByAgeOrderByLastnameDesc | … where x.age = ?1 order by x.lastname desc |
Not | findByLastnameNot | … where x.lastname <> ?1 |
In | findByAgeIn(Collection<Age> ages) | … where x.age in ?1 |
NotIn | findByAgeNotIn(Collection<Age> ages) | … where x.age not in ?1 |
True | findByActiveTrue() | … where x.active = true |
False | findByActiveFalse() | … where x.active = false |
IgnoreCase | findByFirstnameIgnoreCase | … where UPPER(x.firstname) = UPPER(?1) |
※ Spring Data JPA는 구멍가게코딩단의 [초급 개발자들을 위한 가볍고 넓은 스프링부트 스타트 스프링 부트2.0] 책을 참고하였습니다.
Spring Data JPA : @Qeury를 이용한 데이터 조회 (0) | 2022.11.19 |
---|---|
Spring Data JPA 조회 페이징 처리 (0) | 2022.11.19 |
Spring Data JPA 연동하기 + 간단한 Insert (0) | 2022.10.10 |
Spring Data JPA 개요 (0) | 2022.10.10 |
Spring Data JPA 첫 정리하기 위해 세팅한 환경은 다음과 같다.
# DB : MySQL 5.7
# STS : 4.16.0.RELEASE
# Spring Boot : 2.7.4
# Dependencies
* Lombok
* MySQL Driver
* Spring Boot DevTools
* Spring Data JPA
* Spring Web
1. DataSource 설정
Spring Boot에서 JPA를 사용하기 위해서는 DataSource 설정이 필요하다.
src/main/resources 에서 application.properties 파일을 열어 아래의 내용을 입력한다.
# MySql Connector 버전에 따라 입력 내용이 다름
# MySql Connector8 이상 버전 com.mysql.cj.jdbc.Driver (8 미만의 Connector도 모두 지원)
# MySql Connector8 미만 버전 com.mysql.jdbc.Driver
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/cookie?useSSL=false
spring.datasource.username=cookie
spring.datasource.password=cookie_1234
# 스키마 생성(create)
spring.jpa.hibernate.ddl-auto = create
# DDL 생성 시 DB 기능 사용 여부
spring.jpa.generate-ddl = true
# 실행된 SQL 표시 여부
spring.jpa.show-sql = true
# 데이터베이스 지정
spring.jpa.database = mysql
# 로그 레벨
logging.level.org.hibernate = info
# MySQL상세 지정
spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
설정 부분 중 spring.jpa.hibernate.ddl-auto 부분에서 DDL 처리 옵셜을 지정할 수 있다.
옵션 | 설명 |
create | 기존 테이블을 삭제 후 다시 생성 (Drop -> Create) |
create-drop | 테이블 생성 후 테이블을 삭제 (Create -> Drop) ==> 테스트용으로 좋음 |
update | 변경된 부분만 DB에 반영 |
validate | Entity와 테이블의 맵핑 성공 여부만 확인 |
none | spring.jpa.hibernate.ddl-auto 기능을 사용하지 않음 |
2. Entity 클래스 (구 DTO, VO)
테이블에서 사용할 컬럼을 지정해준다.
이번 연습의 컨셉은 게임에서 길드원의 정보를 기록하는 테이블이다.
경로는 package 정보를 참고하자.
package com.cookie.domain;
import java.sql.Timestamp;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.CreationTimestamp;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
@Table(name = "tbml_GuildInfo")
public class Guild {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long indexNo; // 번호(pk)
private int level; // 레벨
private String userName; // 유저명
private String userJob; // 직업명
private String authority; // 길드권한
private Long contribution; // 기여도
@CreationTimestamp
private Timestamp guildRegDate; // 가입일
}
테이블을 생성할 때 꼭 필요한 것은 @Entity 와 @Id 어노테이션이다.
클래스는 테이블이 되며 멤버변수는 테이블의 컬럼들이 된다.
여기서 어노테이션을 통해 테이블 생성에 조건을 추가할 수 있다.
@어노테이션 | 설명 |
@Id | 지정한 변수를 Entity 구별용 식별자로 지정 = PK 지정 |
@GeneratedValue | @Id로 지정된 식별자의 관리 전략을 지정 |
@Column | 컬림이 되는 멤버변수들에 제약조건을 지정한다. 아래 표 참고 |
@Table | 생성될 테이블은 클래스의 명칭을 따라가지만 명칭을 다르게 하고자 할 때 사용 속성값을 사용하여 인덱스 생성 등을 지정할 수도 있다. |
@Entity | 해당 클래스의 인스턴스가 Entity 임을 명시해 줌. |
* @GeneratedValue 전략
strategy | 설명 |
AUTO | 현재 DB에 맞게 자동 선택 |
IDENTITY | PK 생성 방식을 DB에 위임하는 방식으로 DB에 의존적임. (MySQL에서 주로 사용) |
SEQUENCE | 시퀀스 컬럼을 이용 (오라클에서만 사용) |
TABLE | 유일성이 보장되는 테이블을 이용 |
* @Column 속성
속성 | 타입 | 설명 |
name | String | 컬럼명을 지정 |
unique | boolean | 유니크 여부를 지정 |
nullable | boolean | null 허용 여부를 지정 |
insertable | boolean | insert가 가능한 컬럼인가 지정 |
updatable | boolean | update가 가능한 컬럼인가 지정 |
table | String | 테이블 이름 |
length | int | 컬럼 사이즈를 지정 |
precision | int | 소수점 정밀도를 지정 |
scale | int | 소수점 이하 자리수를 지정 |
3. Repository Interface 생성 (구 DAO)
Spring Data JPA는 별도의 클래스 생성없이 인터페이스를 구현하는 것으로 JPA관련 처리를 끝낼 수 있다.
실제 DB 작업에서 많이 쓰이는 CRUD 방식은 CrudRepository 인터페이스에서 대부분의 기능을 지원해준다.
따라서 새로 만들 GuildRepository 인터페이스는 CrudRepository 인터페이스를 상속받아 사용하면 된다.
Spring Data JPA의 인터페이스 구조는 다음과 같다. (T : Table명 , ID : 식별자(PK) 타입)
Repository<T,ID>
↑
CrudRepository<T, ID>
↑
PagingAndSortingRepository<T, ID>
package com.cookie.persistence;
import org.springframework.data.repository.CrudRepository;
import com.cookie.domain.Guild;
public interface GuildRepository extends CrudRepository<Guild, Long>{
}
4. 데이터 INSERT
Repository까지 준비되었다면 실제 데이터를 넣어 보자.
간단하게 데이터가 잘 들어가는지만 확인하기 위한 용도이므로 src/test/java 에서 테스트를 진행해본다.
package com.cookie;
import java.util.Random;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.cookie.domain.Guild;
import com.cookie.persistence.GuildRepository;
@SpringBootTest
class StudyJpaApplicationTests {
@Autowired
private GuildRepository guildRepository;
@Test
void testInsert50() {
Random ran = new Random();
int jobNo = 0;
String job[] = {"기사","도적","마법사"};
for(int i = 1; i <= 50; i++) {
jobNo = ran.nextInt(3);
Guild guild = new Guild();
guild.setLevel(i);
guild.setUserName("cookie" + i);
guild.setUserJob(job[jobNo]);
guild.setAuthority("길드원");
guild.setContribution(i*20);
guildRepository.save(guild);
}
}
}
소스코드를 보면 SQL에 관련된 아무런 쿼리도 존재하지 않는다.
직접 쿼리를 작성할 필요가 없어지고 개발만 하면 되므로 편리성이 매우 좋다.
코드 마지막에는 save()를 통해 insert함을 추측해 볼 수 있다.
마지막으로 MySQL 워크벤치 확인 결과이다.
Guild.java 에서 설정한 대로 테이블 명은 "tbml_GuildInfo" 로 지정했으나 실제로는 "tbml_guild_Info" 로 들어갔다.
자동으로 대소문자 구분없이 "_" 로 구분된 모습이다.
※ Spring Data JPA는 구멍가게코딩단의 [초급 개발자들을 위한 가볍고 넓은 스프링부트 스타트 스프링 부트2.0] 책을 참고하였습니다.
Spring Data JPA : @Qeury를 이용한 데이터 조회 (0) | 2022.11.19 |
---|---|
Spring Data JPA 조회 페이징 처리 (0) | 2022.11.19 |
Spring Data JPA [SELECT] 쿼리메소드 (0) | 2022.11.05 |
Spring Data JPA 개요 (0) | 2022.10.10 |
Spring Frameword의 강점 중 하나는 다른 프레임워크와 Adaptor를 지원하여 통합이 가능하다는 점이다.
그 중 가장 대표적인 것이 MyBatis와 같은 ORM(Object Relational Mapping) 프레임워크가 아닐까 싶다.
Spring Data는 JDBC부터 ORM까지 Data 관련 개발에 필요한 라이브러리(프레임워크)를 쉽게 사용하도록 도와준다.
따라서 Spring Data JPA는 JPA(Java Persistence API) 기술을 Spring에서 사용할 수 있도록 도와주는 라이브러리이다.
(JPA에 대해서는 인프런 김영한님의 강의 수강 후 정리..)
https://spring.io/projects/spring-data-jpa
Spring Data JPA
Spring Data JPA, part of the larger Spring Data family, makes it easy to easily implement JPA based repositories. This module deals with enhanced support for JPA based data access layers. It makes it easier to build Spring-powered applications that use dat
spring.io
※ Spring Data JPA는 구멍가게코딩단의 [초급 개발자들을 위한 가볍고 넓은 스프링부트 스타트 스프링 부트2.0] 책을 참고하였습니다.
Spring Data JPA : @Qeury를 이용한 데이터 조회 (0) | 2022.11.19 |
---|---|
Spring Data JPA 조회 페이징 처리 (0) | 2022.11.19 |
Spring Data JPA [SELECT] 쿼리메소드 (0) | 2022.11.05 |
Spring Data JPA 연동하기 + 간단한 Insert (0) | 2022.10.10 |