다섯 번째 테스트 목적 : DB에 List<Hospital> INSERT 하기
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "SCOTT", "TIGER");
System.out.println("1. DB 연결성공");
String sql = "INSERT INTO hospital(id, yadmNm, pcrPsblYn, addr) VALUES(seq_hospital.nextval, ?, ?, ?)";
for문 안에서 pstmt.executeUpdate( )를 호출해서는 안된다.
executeUpdate는 내부적으로 COMMIT을 하기 때문에
5000번 이상 totalCount동안 INSERT를 하며
I/O가 발생하게 되기 때문이다.
이때 쿼리를 모아 한 번에 INSERT문을 전송해서
COMMIT도 한번에 하는 프로그램을 만들어야 한다.
이게 배치 프로그램이다.
List<Hospital> hospitals = migration();
PreparedStatement pstmt = conn.prepareStatement(sql); // 내부적으로 new하는 것!
for (Hospital hospital : hospitals) {
pstmt.setString(1, hospital.getYadmNm());
pstmt.setString(2, hospital.getPcrPsblYn());
pstmt.setString(3, hospital.getAddr());
// pstmt에 쿼리만 하나 들어온다.
pstmt.addBatch(); // 버퍼에 담기
// 항아리에 담고나서 쿼리문 초기화
pstmt.clearParameters(); // 완성된 쿼리를 원복(이유 : pstmt 한개만 쓸거니까)
}
pstmt.addBatch( )를 호출하면
pstmt.setString으로 완성시킨 쿼리문 하나가
배치 항아리에 담긴다.
이 항아리가 버퍼이다.
버퍼에 SQL문 5682개를 담아두는 것이다.
항아리에 쿼리를 하나씩 담아두고
모든 쿼리가 다 담겼을 때
한번에 INSERT 하기 위해 담아두는 것이다.
배치를 한방에 하기 위해
conn을 for문 밖에 하나만 만들었는데,
for문이 시작하고 ?(물음표)를 채워서
첫 번째 SQL 쿼리가 완성이 되고
addBatch로 항아리에 첫번째 완성 쿼리가 담기게 된다.
그리고 두 번째 for문이 돌 때
물음표가 있는 쿼리를 완성하려고
원래 있던 공간을 가리키고 있는 pstmt를 보니
이미 쿼리문이 완성되어 있어서 수정이 불가능하다.
첫 번째 for문으로 1번 쿼리는 어차피 항아리에 담겨있으니
쿼리를 최초 상태로 원복 해주면 된다.
그러면 완성되지 않은 쿼리로 돌아가, 초기화가 가능해진다.
이때 clearParameters( ) 메서드를 사용한다.
모든 쿼리가 항아리에 다 담기게 되면
한 번에 전송하여 commit 하는
executeBatch( ) 메서드를 호출한다.
pstmt.executeBatch(); // 항아리에 담긴걸 한번에 전송 후 commit
pstmt.close();
package site.metacoding.hospapp.ex05;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.Gson;
// 목적 : DB에 List<Hospital> INSERT하기
public class DBInsertTest {
// 몇개 다운로드 받을지 먼저 totalCount 확인하기
public static int getTotalCount() {
int totalCount = 0;
try {
// 1. URL 주소 만들기 - totalCount 확인용
StringBuffer sb = new StringBuffer();
sb.append("http://apis.data.go.kr/B551182/rprtHospService/getRprtHospService");
sb.append("?serviceKey="); // 서비스키
sb.append("서비스키%3D%3D");
sb.append("&pageNo=?"); // 몇번째 페이지 인지
sb.append("1");
sb.append("&numOfRows=");
sb.append("2"); // totalCount 체크만 할 것이기 때문에 2개만 받아도 된다. (왜 2개냐면 1개만 받으면 List가 아니라 Object로 받더라)
sb.append("&_type=");
sb.append("json"); // 데이터 포맷은 JSON
// 2. 다운로드 받기 - totalCount 확인용
URL url = new URL(sb.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8"));
StringBuffer sbDownload = new StringBuffer(); // 통신결과 모아두기
while (true) {
String input = br.readLine();
if (input == null) {
break;
}
sbDownload.append(input);
}
// 3. 검증 - totalCountCheck
// System.out.println(sb.toString());
// 4. 파싱
Gson gson = new Gson();
ResponseDto responseDto = gson.fromJson(sbDownload.toString(), ResponseDto.class);
// 5. totalCount 담기
totalCount = responseDto.getResponse().getBody().getTotalCount();
System.out.println("totalCount : " + totalCount);
return totalCount;
} catch (Exception e) {
e.printStackTrace();
}
return totalCount;
}
// KISS하게 만드는 것이 좋다. Keep It Small(Short) and Simple
// totalCount 기반으로 전체 데이터 다운로드한 후 자바 오브젝트로 파싱하기
public static ResponseDto download() {
ResponseDto responseDto = null;
try {
// 6. 전체 데이터 받기
// (1) URL 주소 만들기
int totalCount = getTotalCount();
if (totalCount == 0) {
System.out.println("totalCount를 제대로 받지 못하였습니다.");
return null;
}
StringBuffer sb = new StringBuffer();
sb.append("http://apis.data.go.kr/B551182/rprtHospService/getRprtHospService");
sb.append("?serviceKey="); // 서비스키
sb.append("서비스키%3D%3D");
sb.append("&pageNo=?"); // 몇번째 페이지 인지
sb.append("1");
sb.append("&numOfRows=");
sb.append(totalCount); // totalCount 체크만 할 것이기 때문에 2개만 받아도 된다. (왜 2개냐면 1개만 받으면 List가 아니라 Object로 받더라)
sb.append("&_type=");
sb.append("json"); // 데이터 포맷은 JSON
// (2) 다운로드 받기
URL url = new URL(sb.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8"));
StringBuffer sbDownload = new StringBuffer(); // 통신결과 모아두기
while (true) {
String input = br.readLine();
if (input == null) {
break;
}
sbDownload.append(input);
}
// (3) 파싱
Gson gson = new Gson();
responseDto = gson.fromJson(sbDownload.toString(), ResponseDto.class);
// 7. 사이즈 검증
System.out.println("아이템 사이즈 : " + responseDto.getResponse().getBody().getItems().getItem().size());
System.out.println("totalCount : " + totalCount);
if (responseDto.getResponse().getBody().getItems().getItem().size() == totalCount) {
System.out.println("성공~~~~~~~~~~~~~~~~");
}
} catch (Exception e) {
e.printStackTrace();
}
return responseDto;
}
public static List<Hospital> migration() {
ResponseDto responseDto = download();
List<Item> list = responseDto.getResponse().getBody().getItems().getItem();
List<Hospital> hospitals = new ArrayList<>(); // hospitals에 list로 옮기면 끝
for (Item item : list) {
Hospital hs = new Hospital();
// copy의 목적 : 다른타입의 item을 받아서 Hospital에 복제하여 넣는 것
hs.objectCopy(item);
hospitals.add(hs);
}
// 검증
System.out.println("마지막 병원 주소 : " + hospitals.get(5681).getYadmNm());
return hospitals;
}
public static void main(String[] args) {
try {
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "SCOTT", "TIGER");
System.out.println("1. DB 연결성공");
String sql = "INSERT INTO hospital(id, yadmNm, pcrPsblYn, addr) VALUES(seq_hospital.nextval, ?, ?, ?)";
List<Hospital> hospitals = migration();
PreparedStatement pstmt = conn.prepareStatement(sql); // 내부적으로 new하는 것;
for (Hospital hospital : hospitals) {
pstmt.setString(1, hospital.getYadmNm());
pstmt.setString(2, hospital.getPcrPsblYn());
pstmt.setString(3, hospital.getAddr());
// pstmt에 쿼리만 하나 들어온다.
pstmt.addBatch(); // 버퍼에 담기
// 항아리에 담고나서 쿼리문 초기화
pstmt.clearParameters(); // 완성된 쿼리를 원복(이유 : pstmt 한개만 쓸거니까)
}
pstmt.executeBatch(); // 항아리에 담긴걸 한번에 전송 후 commit
pstmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
[출처]
https://cafe.naver.com/metacoding
메타 코딩 유튜브
https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9