[SpringBoot(2)] 대용량데이터 API 출력 및 JPQL 사용

배지원·2022년 11월 14일
0

실습

목록 보기
14/24

이전에 했던 전국 병원 대용량 데이터 API를 통해 파싱한 데이터를 가지고 진행을 해보겠다.

📄 대용량 데이터 API로 띄우기

  • 이전에 파싱한 데이터를 저장하고 있는 DB파일을 가져와 현재 프로젝트 DB에 저장하여 띄운다.
  • 또한 자신이 찾고싶은 데이터만 찾을 수 있도록 JPQL을 사용한다.

1. DB 가져오기

DB 파일 Export와 Import하는 Dump과정은 정리해둔 블로그가 있으니 블로그 대로 진행하면 된다.
DB Dump 정리


2. 대용량 데이터 View로 출력

  • 파싱한 대용량 데이터를 한번에 Rest API를 통해 View로 출력하려면 로딩이 너무 오래 걸린다. 따라서 페이징이라는 것이 필수이다.

    페이징이란?

    • 한번에 많은 데이터를 한페이지에 출력하려면 해당 페이지를 클릭할때마다 많은 로딩시간이 걸린다. 따라서 미리 정해둔 갯수만큼 페이지를 나누어 출력시키도록 하는 것
  • 페이징 처리를 위해서 JPA는 Page와 Pageable을 사용한다.


Back

Hospital

  • DB에서 데이터를 가져와 출력시키기 위한 Entity
@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

}

Repository

public interface HospitalRepository extends JpaRepository<Hospital,Integer> {
}

Service

  • 기존에는 Controller에서 바로 Repository를 연결해서 DB쿼리문을 통해 데이터를 호출했었는데 이제는 중간에 Service클래스를 통해 Controller -> Service -> Repository로 3개를 구분하였다.
  • Service에서는 Repository와 연결하고 쿼리문을 통해 DB에서 값을 주고 받고 기능들을 가공하는 공간이라고 생각하면 된다.
@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

@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

  1. Page hospitals = hospitalRepository.findAll(pageable);
    👉 findAll의 매개변수로 pageable를 넣어주고 List가 아닌 Page로 값을 넣어준다면 자동으로 페이징이 됨
  2. getPageNumber() : 현재 페이지 번호를 리턴
  3. previousOrFirst()
    👉 이전 Pageable 또는 현재 페이지가 이미 첫 번째 인 경우 첫 번째 Pageable를 반환
  4. next()
    👉 다음 페이지를 요청하는 Pageable를 반환

Front

Hospital List

{{>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}}


3. JPQL 사용하여 원하는 데이터 찾기

  • JPQL란 무엇인가?
  • 여러가지 데이터들 중 찾고 싶은 데이터가 있다면 JPQL 기능을 통해 단어를 입력 시 해당 단어를 포함한 데이터 리스트가 출력됨
  • JPQL은 Repository 클래스에 작성함

(1) In을 통한 데이터 찾기

  • sql 쿼리문에서 In은 2개 이상의 데이터를 입력받아 그 중 1개라도 포함되면 출력되는 구조이다.

Entity

@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;
}

Repository

public interface HospitalRepository extends JpaRepository<Hospital,Integer> {
  List<Hospital> findByBusinesstypenameIn(List<String> businessTypes);    // Businesstypename을 통해 데이터 찾기
}
  • findBy변수명 : 변수를 통해 값을 찾는다
  • Businesstypename : Entity에 저장된 변수명을 그래도 작성 해줘야함(DB와 Entity는 1대1 연결이기 때문에)
  • In(List businessTypes) : 2개 이상의 값을 입력받아야 하므로 List를 통해 입력을 받고 IN 쿼리문을 통해 전체 데이터중 List의 값들 중 한개라도 포함이 되는 값이라면 출력함

Test Case

@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());
  }
}
  • TestCase를 통해 수시로 검사를 해줘야함 그 이유는 마지막에 다룸
  • 보건소, 보건진료소, 보건지소를 입력하여 JPQL문안에 넣으면 해당하는 데이터 출력

(2) In, And, Like 을 통한 데이터 찾기

Repository

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);
}
  • 1개의 값만 비교할때는 Like를 사용하고 2개 이상의 값이 포함되어 있는지 확인할때는 In을 사용한다.
  • 즉 위의 코드는 In을 통해 businessTypes의 데이터 중 한개 가 포함이 되어 있고 Like를 통해 roadnameaddress 글자가 포함된 데이터가 출력되게끔 작성한 쿼리문이다.

Test Case

    @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());
    }

  • 모든 조건을 충족하는 데이터는 1개만 출력된 것을 확인할 수 있다.

(3) Like 대신 Containing 사용

Repository

List<Hospital> findByRoadNameAddressContaining(String keyword); // keyword를 포함 (%keyword% 와 같음)
List<Hospital> findByHospitalnameStartsWith(String keyword); // keyword로 시작 (%keyword 와 같음)
List<Hospital> findByHospitalnameEndingWith(String keyword); // keyword로 끝남 (keyword% 와 같음)

일반 Test Case

  @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());
  }
  • 주소 중 "송파구"를 포함한 데이터를 모두 출력함

  • 병원이름 중 "경희"로 시작하는 데이터 모두 출력

  • 병원이름 중 "병원" 끝나는 데이터 모두 출력


(4) Between을 통한 사이의 값 구하기

  • 병상수가 10~20개 사이인 데이터 출력

Repository

List<Hospital> findByPatientroomcountBetweenOrderByPatientroomcountDesc(int a, int b);  // a~b 사이의 데이터만 출력
  • Between을 통해 a~b 사이의 데이터만 출력함
  • OrderBy를 통해 정렬을 할 수 있음
  • 정렬 기준을 Desc(내림차순)으로 설정함

Test Case

  @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());
  }

  • 10~20사이의 값들이 내림차순으로 정렬되어 출력됨
profile
Web Developer

0개의 댓글