@Configuration
@EnableBatchProcessing
@EnableScheduling
public class SpringBatchScheduler {
@Scheduled(cron = "0 0 0 1 1 ?")
public void updateIpFile() {
ipTrackingService.updateIpFile();
}
}
@Service
@Transactional
public class IpTrackingService {
@Autowired
private Ipv4Repository ipv4Repository;
@Autowired
private UtmRepository utmRepository;
@Autowired
private Ipv4BatchRepository ipv4BatchRepository;
//서블릿 컨텍스트 객체
@Autowired ServletContext context;
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
public String trackingCountryByIp(String stringIp) {
//IP문자를 . 기준으로 문자배열로 만듬
String[] arr = stringIp.split("\\."); //.단위로 나눈다는 뜻. '.'는 정규식 예약어라서 \\.로 표현해야함
//String 배열을 Int 배열로 변환
int [] ipArr = new int[4];
for(int i = 0; i<4; i++) {
ipArr[i] = Integer.parseInt(arr[i]);
}
//int배열을 10진수로 변환 2의 0승, 2의 8승, 2의 16승, 2의 24승
long ip10 = 0;
for(int i=0; i<4; i++) {
if( i == 3) {
ip10 += ipArr[i];
} else {
ip10 += ipArr[i] * Math.pow(2, 8*(3-i));
}
}
System.out.println("10진수로 변환한 ip : "+ ip10);
String countryCode = utmRepository.trackingCountryByIp(ip10);
return countryCode;
}
/*
* KISA에서 IP 기준 정리한 csv파일 다운받아오기
*/
public void downloadIpFile() {
String address = "https://xn--3e0bx5euxnjje69i70af08bea817g.xn--3e0b707e/jsp/statboard/IPAS/ovrse/natal/IPaddrBandCurrentDownload.jsp\r\n"
+ ""; // 다운 받을 파일 주소
try {
URL url = new URL(address);
ReadableByteChannel rbc = Channels.newChannel(url.openStream());
String storedPath = context.getRealPath("ipFile.csv");
System.out.println(storedPath);
FileOutputStream fos = new FileOutputStream(storedPath); //다운받을 경로 설정
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); // 처음부터 끝까지 다운로드
fos.close();
System.out.println("파일 다운완료");
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 새 csv파일 다운받고, 이전 데이터 삭제하고, 새 데이터 가공한 후, DB에 저장하기
*/
@Async
public void updateIpFile() {
// 1. 새 csv파일 다운받아오기
downloadIpFile();
// 2. 이전 데이터 삭제하기 truncate와 차이점 알고 이걸로 바꾸기
ipv4Repository.delete();
//csv파일의 절대경로 구하기
String storedPath = context.getRealPath("ipFile.csv");
System.out.println("storedPath = " + storedPath);
List<Ipv4> ipv4List = new ArrayList<>();
String[] ipv4Info;
try {
//utf-8 형태로 csv 파일 파싱
CSVReader csvReader = new CSVReader(new InputStreamReader(new FileInputStream(storedPath), "EUC-KR"));
csvReader.readNext(); // 컬럼명은 저장되지 않도록 한 줄 읽기
do {
ipv4Info = csvReader.readNext(); //한 라인 읽기 (자동으로 콤마 분리해서 배열에 저장 됌)
if (ipv4Info != null) {
if (ipv4Info[0] == null || ipv4Info[1] == null || ipv4Info[2].isEmpty() || ipv4Info[3].isEmpty() || ipv4Info[4].isEmpty() || ipv4Info[5].isEmpty())//읽어온 데이터의 위도, 경도 값이 없거나 null 이면 저장하지 않고 넘김
continue;
else { //위의 두 조건에 해당사항이 없으면 데이터를 객체에 저장 후 임시 저장 ArrayList에 삽입
Ipv4 ipv4 = new Ipv4(); //객체 생성하기
try {
ipv4.setRecordDate(formatter.parse(ipv4Info[0]));
} catch (ParseException e) {
e.printStackTrace();
}
ipv4.setCountry(ipv4Info[1]);
ipv4.setStartIp(ipv4Info[2]);
ipv4.setEndIp(ipv4Info[3]);
ipv4.setPrefix(ipv4Info[4]);
//----------------------------IP 10진수로 변환하기----------------------------
String stringIp = ipv4Info[2];
String[] arr = stringIp.split("\\.");
for(int i=0; i<arr.length; i++) {
System.out.print(arr[i] + ",");
}
int [] ipArr = new int[4];
for(int i = 0; i<4; i++) {
ipArr[i] = Integer.parseInt(arr[i]);
}
long ip10 = 0;
for(int i=0; i<4; i++) {
if( i == 3) {
ip10 += ipArr[i];
} else {
ip10 += ipArr[i] * Math.pow(2, 8*(3-i));
}
}
ipv4.setStartToTen(ip10);
//----------------------------IP 10진수로 변환하기 끝----------------------------
try {
ipv4.setAssignDate(formatter.parse(ipv4Info[5]));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ipv4List.add(ipv4);
}
}
} while (ipv4Info != null);
} catch (IOException e) {
System.out.println(e.getMessage());
} catch (EntityExistsException | CsvValidationException e) {
e.printStackTrace();
}
//ipv4Repository.saveAll(ipv4List); saveAll 쓰지 말기. 속도가 너무 느림(약 2분 걸림)
ipv4BatchRepository.insert(ipv4List); //saveAll 대신 batch insert 쓰기(약 10초 걸림)
}
@Async
public void test(int i) {
System.out.println("비동기 동작 중 : " + i);
}
}
String[] arr = stringIp.split("\\.");
split(".")
으로 했다가 계속 오류가 나서 찍어보니 .
단위로 slipt 되지 않아 찾아본 결과 .
는 예약어라서 그대로 작성하면 안되고 \\.
라고 작성해야 의도대로 먹힌다.
for(int i=0; i<4; i++) {
if( i == 3) {
ip10 += ipArr[i];
} else {
ip10 += ipArr[i] * Math.pow(2, 8*(3-i));
}
}
Math.pow
가 (n,0)이라고 작성 시 n의 0승이 아닌, n의 1승을 뱉어내서 결과적으로 엄청난 오차가 생겨 저렇게 수동으로 바꿨다. 수동을 바꾸지 않는 방법도 있을까? 일단 찾아봤는데 내가 잘 못 검색했는지 아직 못 찾았다. 찾아볼 것🙌
url로 접속해 원하는 파일을 다운받아오는 메서드.
url을 받아오는 방법은, 해당 인터넷 페이지에서 '다운받기' 버튼을 클릭한 후 ctrl + shift + i
를 누른 후 network 창을 열어 get이나 port 매핑 url을 추출하면 된다
saveAll
보다 batchUpdate
가 더 빠르다!
이걸 못 깨달아서 한 시간 날렸다.
batch를 사용하려면 private final JdbcTemplate jdbcTemplate;
을 선언해야하는데, 인터페이스에서는 지역변수를 선언할 수 없다😟
그래서 class로 정의한 다음
public Ipv4BatchRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate=jdbcTemplate;
}
생성자 선언에 필드로 넣어주는 선언까지 하면 빨간 줄 에러가 발생하지 않는다.
public void insert(List<Ipv4> list) {
jdbcTemplate.batchUpdate(
"INSERT INTO IPV4 (record_date, country, start_ip, end_ip, prefix, start_to_ten, assign_date ) VALUES ("
+ "?, ?, ?, ?, ?, ?, ?)",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
String recordDate = formatter.format(list.get(i).getRecordDate());
ps.setString(2, list.get(i).getCountry());
ps.setString(1, recordDate);
ps.setString(3, list.get(i).getStartIp());
ps.setString(4, list.get(i).getEndIp());
ps.setString(5, list.get(i).getPrefix());
ps.setLong(6, list.get(i).getStartToTen());
String assignDate = formatter.format(list.get(i).getAssignDate());
ps.setString(7, assignDate);
}
@Override
public int getBatchSize() {
System.out.println("list.size : " + list.size());
return list.size();
}
});
}
ps.setDate(2, list.get(i).getCountry())
로 계속 시도하니 java.util.Date cannot be cast to java.sql.Date
에러가 계속 났다. 한 시간 붙잡은 끝에 setString으로 해보니 되었다. 애초에 지역변수를 Date로 선언했지만 String으로도 넣어진다!
그래서
String recordDate = formatter.format(list.get(i).getRecordDate());
ps.setString(2, list.get(i).getCountry());
로 코드를 변경하니 통과! 기나긴 고단한 하루였다...