HospitalDao
// 쿼리문 저장하는 DAO
@Component
public class HospitalDao {
private final JdbcTemplate jdbcTemplate;
public HospitalDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
// select 구문으로 받은 객체를 자신이 원하는 형태로 받을수 있도록 rowMapper함
RowMapper<Hospital> rowMapper = (rs, rowNum) -> {
Hospital hospital = new Hospital();
hospital.setId(rs.getInt("id"));
hospital.setOpenServiceName(rs.getString("open_service_name"));
hospital.setOpenLocalGovernmentCode(rs.getInt("open_local_government_code"));
hospital.setManagementNumber(rs.getString("management_number"));
hospital.setLicenseDate(rs.getTimestamp("license_date").toLocalDateTime());
hospital.setBusinessStatus(rs.getInt("business_status"));
hospital.setBusinessStatusCode(rs.getInt("business_status_code"));
hospital.setPhone(rs.getString("phone"));
hospital.setFullAddress(rs.getString("full_address"));
hospital.setRoadNameAddress(rs.getString("road_name_address"));
hospital.setHospitalName(rs.getString("hospital_name"));
hospital.setHealthcareProviderCount(rs.getInt("healthcare_provider_count"));
hospital.setPatientRoomCount(rs.getInt("patient_room_count"));
hospital.setTotalNumberOfBeds(rs.getInt("total_number_of_beds"));
hospital.setTotalAreaSize(rs.getFloat("total_area_size"));
return hospital;
};
public void add(Hospital hospital){
this.jdbcTemplate.update("insert into nation_wide_hospitals(id, open_service_name, open_local_government_code, management_number, license_date, business_status, business_status_code, phone, full_address, road_name_address, hospital_name, business_type_name, healthcare_provider_count, patient_room_count, total_number_of_beds, total_area_size) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
hospital.getId(),
hospital.getOpenServiceName(),
hospital.getOpenLocalGovernmentCode(),
hospital.getManagementNumber(),
hospital.getLicenseDate(),
hospital.getBusinessStatus(),
hospital.getBusinessStatusCode(),
hospital.getPhone(),
hospital.getFullAddress(),
hospital.getRoadNameAddress(),
hospital.getHospitalName(),
hospital.getBusinessTypeName(),
hospital.getHealthcareProviderCount(),
hospital.getPatientRoomCount(),
hospital.getTotalNumberOfBeds(),
hospital.getTotalAreaSize());
}
public Hospital findById(int id){
String sql = "SELECT * FROM nation_wide_hospitals WHERE id = ?";
return this.jdbcTemplate.queryForObject(sql,rowMapper,id);
}
public int getcount(){
return this.jdbcTemplate.queryForObject("select count(id) from nation_wide_hospitals", Integer.class); // integer로 반환
}
// this.jdbcTemplate.queryForObject(sql,어떤 자료형으로 내보낼지 정함)
// queryForObject(sql,String.class)를 사용했을 때 String으로 return되는 것을 확인
public void deleteAll(){
this.jdbcTemplate.update("delete from nation_wide_hospitals");
}
}
queryForObject란?
- 쿼리문 수행결과가 한개이다
- 객체 그대로 반환해준다.
query( ) queryForObject( ) 쿼리문 1개 이상 수행 쿼리문 1개 수행 리스트형식 반환 객체 그대로 반환
형식
Object jdbcTemplate.queryForObject(SQL구문, 반환 타입, 인자);
- SQL 구문
: SQL 구문(insert,delete,select...)을 말한다.- 반환 타입
: 어떤 타입으로 반환할 건지를 선택. 단, 데이터 형만 가능하다.
(Long, Integer, String 등)- 인자
: SQL 구문에 필요한 인자를 넣는다.
예시String name = jdbcTemplate.queryForObject( "SELECT name FROM USER WHERE id=?", String.class, 1000L);
- SELECT 쿼리문 입력
- String.class로 String형으로 반환
- 1000L을 통해 id가 1000번째인 값을 찾음
RowMapper란?
- queryForObject는 데이터형만 입력이 가능하다고 했다. 하지만 자신이 필요한 형태로 받고 싶을때는 어떻게 해야할까? 이때 사용하는 것이 RowMapper이다.
- RowMapper를 사용하면, 원하는 형태로 바꿔서 반환값을 받을 수 있다.
SELECT로 나온 여러개의 값과 사용자가 원하는 형태로 반환을 받을 수 있다.
다음과 같이 사용이 가능하다.RowMapper<Hospital> rowMapper = new RowMapper<Hospital>() { @Override public Hospital mapRow(ResultSet rs, int rowNum) throws SQLException { return null; } };
람다 사용
RowMapper<Hospital> rowMapper = (rs, rowNum) -> { Hospital hospital = new Hospital(); hospital.setId(rs.getInt("id")); hospital.setOpenServiceName(rs.getString("open_service_name")); hospital.setOpenLocalGovernmentCode(rs.getInt("open_local_government_code")); hospital.setManagementNumber(rs.getString("management_number")); hospital.setLicenseDate(rs.getTimestamp("license_date").toLocalDateTime()); hospital.setBusinessStatus(rs.getInt("business_status")); hospital.setBusinessStatusCode(rs.getInt("business_status_code")); hospital.setPhone(rs.getString("phone")); hospital.setFullAddress(rs.getString("full_address")); hospital.setRoadNameAddress(rs.getString("road_name_address")); hospital.setHospitalName(rs.getString("hospital_name")); hospital.setHealthcareProviderCount(rs.getInt("healthcare_provider_count")); hospital.setPatientRoomCount(rs.getInt("patient_room_count")); hospital.setTotalNumberOfBeds(rs.getInt("total_number_of_beds")); hospital.setTotalAreaSize(rs.getFloat("total_area_size")); return hospital; };
HospitalController
@RestController
@RequestMapping("/hospital")
public class HospitalController {
private final HospitalDao hospitalDao;
public HospitalController(HospitalDao hospitalDao) {
this.hospitalDao = hospitalDao;
}
// ex) localhost:8080/hospital/1 id=1
@GetMapping("/{id}")
public ResponseEntity<Hospital> get(@PathVariable("id") Integer id){
Hospital hospital = hospitalDao.findById(id);
Optional<Hospital> opt = Optional.of(hospital);
// 요즘은 null을 줄이는 추세이다. 따라서 null 대신 Optional을 사용함
// Optional.of(hospital) => hospital에 대한 Optional(null)검사를 함
if(!opt.isEmpty()){ // 비어있지 않다면
return ResponseEntity.ok().body(hospital); // ok -> 정상 , body에 hospital 출력
}else{ // 비어있다면
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new Hospital()); // BAE_REQUEST 출력, body에 새로운 객체 출력
}
}
}
Optional이란?
- null이 올 수 있는 값을 감싸는 Wrapper 클래스로, 참조하더라도 NPE가 발생하지 않도록 도와준다. 위와 같이 null이 발생해도 다른 길을 제공해준다.
Controller, Service, Repository에 관한 내용은 이전에 정리해 놓은 연결 구조정리 블로그에서 확인이 가능하다.
@Component : Spring에서 관리되는 객체임을 표시하기 위해 사용하는 가장 기본적인 annotation이다. DI를 위한 기본 annotation이다. Spring application context에서 Bean으로서 자동으로 등록되도록 하는 annotation이라 생각하면 될 듯 하다.
@Controller, @Service, @Repository의 기능은 @Component와 동일하지만, 보다 class를 특정화하여 나타내기 위해 @Controller, @Service, @Repository를 구분하여 사용한다. business layer를 구분하기 사용한다.
@Transactional : 데이터 추가, 갱신, 삭제 등 과정 중 오류가 발생했을 때 모든 작업들을 원상태로 되돌릴 수 있다. 모든 작업들이 성공해야만 최종적으로 데이터베이스에 반영하도록 한다.
CODE
@Service
public class HospitalService {
private final ReadLineContext<Hospital> hospitalReadLineContext; // ReadLineContext 클래스 사용하기 위해
private final HospitalDao hospitalDao; // HospitalDao 사용하기 위해
public HospitalService(ReadLineContext<Hospital> hospitalReadLineContext, HospitalDao hospitalDao) {
this.hospitalReadLineContext = hospitalReadLineContext;
this.hospitalDao = hospitalDao;
}
/* 위의 코드는 아래와 같은 의미
@Autowired
private final ReadLineContext<Hospital> hospitalReadLineContext;
@Autowired
private final HospitalDao hospitalDao;
*/
// 반영된 개수를 반환함
@Transactional // 성능을 좀더 높여줌
public int insertLargeVolumeHospitalData(String filename){
int cnt = 0;
try {
List<Hospital> hospitalList = hospitalReadLineContext.readByLine(filename);
System.out.println("파싱이 끝났습니다.");
for(Hospital hospital: hospitalList){ // loop 구간
try {
this.hospitalDao.add(hospital); // db에 insert하는 구간
cnt++;
} catch (Exception e) {
System.out.printf("id:%d 레코드에 문제가 있습니다.",hospital.getId());
throw new RuntimeException(e);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return cnt;
}
}
참고자료 : queryForObject/RowMapper 참고