이전에 했던 전국 병원 대용량 데이터 API를 통해 파싱한 데이터를 가지고 진행을 해보겠다.
DB 파일 Export와 Import하는 Dump과정은 정리해둔 블로그가 있으니 블로그 대로 진행하면 된다.
DB Dump 정리
파싱한 대용량 데이터를 한번에 Rest API를 통해 View로 출력하려면 로딩이 너무 오래 걸린다. 따라서 페이징이라는 것이 필수이다.
페이징이란?
- 한번에 많은 데이터를 한페이지에 출력하려면 해당 페이지를 클릭할때마다 많은 로딩시간이 걸린다. 따라서 미리 정해둔 갯수만큼 페이지를 나누어 출력시키도록 하는 것
페이징 처리를 위해서 JPA는 Page와 Pageable을 사용한다.
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Table(name = "nation_wide_hospitals")
public class Hospital {
@Id
private Integer id;
@Column(name = "hospital_name") // DB 변수와 entity 변수명이 다를경우 Column을 통해 직접 연결할 수 있다.
private String hospitalname;
@Column(name = "full_address")
private String fulladdress; // cammel 형식으로 작성시 자동으로 DB에 _ 형식으로 들어감 fullAddress => full_address
}
public interface HospitalRepository extends JpaRepository<Hospital,Integer> {
}
@Service
public class HospitalService {
private final HospitalRepository hospitalRepository;
public HospitalService(HospitalRepository hospitalRepository) {
this.hospitalRepository = hospitalRepository;
}
public Page<Hospital> getHospitalList(Pageable pageable){
return hospitalRepository.findAll(pageable);
}
}
@Controller
@RequestMapping("/hospital")
@Slf4j
public class HospitalController {
private final HospitalService hospitalService;
public HospitalController(HospitalService hospitalService) {
this.hospitalService = hospitalService;
}
// Hospital 대용량 데이터 리스트 만들기(페이징 하여 출력)
@GetMapping("/list")
public String list(Pageable pageable,Model model){
Page<Hospital> hospitals = hospitalService.getHospitalList(pageable); // 페이징 추가
model.addAttribute("hospitals",hospitals);
model.addAttribute("previous",pageable.previousOrFirst().getPageNumber());
model.addAttribute("next",pageable.next().getPageNumber());
return "hospital/list";
}
}
Pageable
- Page hospitals = hospitalRepository.findAll(pageable);
👉 findAll의 매개변수로 pageable를 넣어주고 List가 아닌 Page로 값을 넣어준다면 자동으로 페이징이 됨- getPageNumber() : 현재 페이지 번호를 리턴
- previousOrFirst()
👉 이전 Pageable 또는 현재 페이지가 이미 첫 번째 인 경우 첫 번째 Pageable를 반환- next()
👉 다음 페이지를 요청하는 Pageable를 반환
{{>layouts/header}}
<table class="table">
<thead>
<tr>
<th scope="col">번호 </th>
<th scope="col">병원이름</th>
<th scope="col">병원주소</th>
</tr>
</thead>
<tbody class="table-group-divider">
{{#hospitals}}
<tr>
<th>{{id}}</th>
<td><a href="/articles/{{id}}">{{hospitalname}}</a></td>
<td>{{fulladdress}}</td>
</tr>
{{/hospitals}}
</tbody>
</table>
<ul class="pagination justify-content-center">
<li class="page-item">
<a class="page-link" href="?page={{previous}}">Previous</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{next}}">Next</a>
</li>
</ul>
{{>layouts/footer}}
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Table(name = "nation_wide_hospitals")
public class Hospital {
@Id
private Integer id;
@Column(name = "hospital_name") // DB 변수와 entity 변수명이 다를경우 Column을 통해 직접 연결할 수 있다.
private String hospitalname;
@Column(name = "full_address")
private String fulladdress; // cammel 형식으로 작성시 자동으로 DB에 _ 형식으로 들어감 fullAddress => full_address
@Column(name = "business_type_name")
private String businesstypename;
}
public interface HospitalRepository extends JpaRepository<Hospital,Integer> {
List<Hospital> findByBusinesstypenameIn(List<String> businessTypes); // Businesstypename을 통해 데이터 찾기
}
@SpringBootTest
class HospitalRepositoryTest {
@Autowired
HospitalRepository hospitalRepository;
@Test
@DisplayName("BusinessTypeName이 보건소, 보건진료소, 보건지소 중 1개이상을 포함한 데이터가 잘 나오는지 확인")
void findByBusinessTypeNameIn(){
List<String> inClues = new ArrayList<>();
inClues.add("보건소");
inClues.add("보건진료소");
inClues.add("보건지소");
List<Hospital> hospitals = hospitalRepository.findByBusinesstypenameIn(inClues);
for(var hospital:hospitals)
System.out.println(hospital.getHospitalname());
}
}
public interface HospitalRepository extends JpaRepository<Hospital,Integer> {
List<Hospital> findByBusinesstypenameIn(List<String> businessTypes); // Businesstypename을 통해 데이터 찾기
// In, And, Like를 통해 데이터 찾기
List<Hospital> findByBusinesstypenameInAndRoadNameAddressLike(List<String> businessTypes, String roadnameaddress);
}
@Test
@DisplayName("In, And, Like모두 사용한 데이터 찾기")
void findByBusinesstypenameInAndRoadNameAddressLike(){
List<String> inClues = new ArrayList<>();
inClues.add("보건소");
inClues.add("보건진료소");
inClues.add("보건지소");
String str = "%광진구%";
List<Hospital> hospitals = hospitalRepository.findByBusinesstypenameInAndRoadNameAddressLike(inClues,str);
for(var hospital:hospitals)
System.out.println(hospital.getHospitalname());
}
List<Hospital> findByRoadNameAddressContaining(String keyword); // keyword를 포함 (%keyword% 와 같음)
List<Hospital> findByHospitalnameStartsWith(String keyword); // keyword로 시작 (%keyword 와 같음)
List<Hospital> findByHospitalnameEndingWith(String keyword); // keyword로 끝남 (keyword% 와 같음)
@Test
@DisplayName("Like 대신 Containing 사용하여 데이터 찾기")
void Containing(){
List<Hospital> hospitals = hospitalRepository.findByRoadNameAddressContaining("송파구");
for(Hospital hospital : hospitals)
System.out.printf("%s, %s\n",hospital.getHospitalname(),hospital.getRoadNameAddress());
}
@Test
@DisplayName("시작 값 비교해서 해당 데이터 찾기")
void startsWith(){
List<Hospital> hospitals = hospitalRepository.findByHospitalnameStartsWith("경희");
for(Hospital hospital : hospitals)
System.out.printf("%s, %s\n",hospital.getHospitalname(),hospital.getRoadNameAddress());
}
@Test
@DisplayName("마지막 값 비교해서 해당 데이터 찾기")
void endWith(){
List<Hospital> hospitals = hospitalRepository.findByHospitalnameEndingWith("병원");
for(Hospital hospital : hospitals)
System.out.printf("%s, %s\n",hospital.getHospitalname(),hospital.getRoadNameAddress());
}
주소 중 "송파구"를 포함한 데이터를 모두 출력함
병원이름 중 "경희"로 시작하는 데이터 모두 출력
병원이름 중 "병원" 끝나는 데이터 모두 출력
List<Hospital> findByPatientroomcountBetweenOrderByPatientroomcountDesc(int a, int b); // a~b 사이의 데이터만 출력
@Test
@DisplayName("2개의 값 사이의 데이터 호출")
void findByPatientRoomCountAndPatientRomCount(){
List<Hospital> hospitals = hospitalRepository.findByPatientroomcountBetweenOrderByPatientroomcountDesc(10,20);
for(Hospital hospital: hospitals)
System.out.printf("%s, %d\n",hospital.getHospitalname(),hospital.getPatientroomcount());
}