Installing ri documentation for fastercsv-1.5.5...
Installing RDoc documentation for fastercsv-1.5.5...
# gem install bundler
Fetching: bundler-1.5.3.gem (100%)
Successfully installed bundler-1.5.3
1 gem installed
Installing ri documentation for bundler-1.5.3...
Installing RDoc documentation for bundler-1.5.3...
Installation
redmine 설치
# apt-get install python-software-properties
# add-apt-repository ppa:ondrej/redmine
You are about to add the following PPA to your system:
Redmine, Rails and other dependencies needed to run Redmine on Ubuntu Precise.
The repository seem to be stable again. However please note that many plugins are not compatible with 2.x releases, so think before you update. Also there might be some glitches when upgrading, so if you think you found one, please send the report with as many information you can gather to me.
NOTE: Unfortunatelly it seems to be virtually impossible to backport Redmine to anything older than Ubuntu 12.04 due entangled circular build dependencies in ruby packages.
NOTE: It should be possible to run redmine in this repository with ruby1.9.1 (1.9.3).
PLEASE READ: If you like my work and want to give me a little motivation, please consider donating: https://deb.sury.org/pages/donate.html
More info: https://launchpad.net/~ondrej/+archive/redmine
Press [ENTER] to continue or ctrl-c to cancel adding it
엔터
gpg: keyring `/tmp/tmpMM_hPJ/secring.gpg' created
gpg: keyring `/tmp/tmpMM_hPJ/pubring.gpg' created
gpg: requesting key E5267A6C from hkp server keyserver.ubuntu.com
gpg: /tmp/tmpMM_hPJ/trustdb.gpg: trustdb created
gpg: key E5267A6C: public key "Launchpad PPA for Ondřej Surý" imported
일반적으로 스프링과 com.fasterxml.jackson를 사용하여 컨트롤러 메소드에서 @ResponseBody 어노테이션을 이용하면 알아서 객체가 JSON 으로 변환되어 나가게 된다.
@RequestMapping(value = "/file/list", method = RequestMethod.GET)
@ResponseBody
public ModelMap list() {
ModelMap mm = new ModelMap();
List<File> list = fileService.list();
mm.put("list", list);
return mm;
}
만약 변환해야 하는 객체가 아래와 같이 순환 참조[각주:1]되는 객체일 경우 JSON 으로 변환을 하다가 에러가 나게 된다.
아래 소스 코드와 같이 간단하게 돌려보면 아래와 같은 에러를 볼 수 있다.
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonTest {
public static void main(String[] args) throws Exception {
A a = new A();
for (int i = 0; i < 10; i++) {
B b = new B();
b.setA(a);
a.addB(b);
}
ObjectMapper om = new ObjectMapper();
String json = om.writeValueAsString(a);
System.out.println(json);
}
}
A 객체의 bs 속성(List)을 보고 B 객체의 A 객체를 보고 다시 A 객체의 bs 속성을 보고 무한으로 반복 된다.
그러다가 최종적으로 java.lang.StackOverflowError 가 떨어지게 된다.
Solution
순환 참조를 하지 말아야 하는 속성에 @JsonBackReference 어노테이션을 달아주자.
import com.fasterxml.jackson.annotation.JsonBackReference;
public class B {
private String name = "B";
// 이거
@JsonBackReference
private A a;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
음.. 어렵다.. -_-... 위 3개의 경우를 쿼리(오라클)로 뽑아보면 아래와 같다. 쿼리로 보는게 더 편할지도? ㅋㅋ
select
/* 우편번호 */
t3.zipcode as zipcode
/* 도로명 주소 */
, t1.sido_name
|| case when t1.sigun_name is not null then ' ' || t1.sigun_name end
|| ' ' || t1.road_name
|| decode(t2.base_yn, '1', ' ' || '지하')
|| ' ' || t2.bldg_no1
|| case when t2.bldg_no2 != 0 then '-' || t2.bldg_no2 end
|| ' (' || t1.eup_name
|| case when t3.apt_yn = '1' and t3.sigun_bldg_name is not null then ', ' || t3.sigun_bldg_name end
|| ')'
as address
/* 도로명 주소 영문 */
, decode(t2.base_yn, '1', 'Jiha' || ' ')
|| t2.bldg_no1
|| case when t2.bldg_no2 != 0 then '-' || t2.bldg_no2 end
|| ', ' || t1.road_name_eng
|| case when t1.sigun_name_eng is not null then ', ' || t1.sigun_name_eng end
|| ', ' || t1.sido_name_eng
as address_eng
/* 지번 주소 (~2013 주소) */
, t4.sido_name
|| case when nvl(t4.gugun_name, t1.sigun_name) is not null then ' ' || nvl(t4.gugun_name, t1.sigun_name) end
|| case when nvl(t4.eup_name, t1.eup_name) is not null then ' ' || nvl(t4.eup_name, t1.eup_name) end
|| case when t4.ri_name is not null then ' ' || t4.ri_name end
|| decode(t4.san_yn, '1', '산')
|| ' ' || t4.ji_no1
|| case when t4.ji_no2 != 0 then '-' || t4.ji_no2 end
|| case when t3.sigun_bldg_name is not null then ' ' || t3.sigun_bldg_name end
as address_jibun
/* 관리번호(PK) */
, t2.num
from
tbl_road t1
join
tbl_road_addr t2
on
t1.road_code = t2.road_code
and t1.eup_seq = t2.eup_seq
join
tbl_road_info t3
on
t2.num = t3.num
join
tbl_road_jibun t4
on
t2.num = t4.num
where
t1.using_yn = '0'
and t4.dele_yn = '1'
and to_char(sysdate, 'YYYYMMDD') between nvl(t1.noti_date, '11110101') and nvl(t1.cancel_date, '99991231')
and to_char(sysdate, 'YYYYMMDD') >= nvl(t2.noti_date, '11110101')
;
뭐 보면 대충 이렇게 나온다.
데이터 가공
데이터의 양이 너무 많다. 4개의 테이블만 덤프를 떠도 1.8GB 정도 한다.
내가 이 도로명주소 데이터를 뽑는 이유는 우편번호를 사용하기 위함이기 때문에 필요없는거 다 날리고 다른 테이블에 다시 넣어보자.
아래 ERD 는 도로명 주소를 표현할 수 있는 최소한의 데이터만 사용했을 때의 테이블이다.
insert into zipcode_road (
zipcode
, road
, sido
, sigun
, eup
, under_yn
, bldg_no1
, bldg_no2
, bldg
, num
, apply_date
)
select
t3.zipcode
, t1.road_name
, t1.sido_name
, t1.sigun_name
, t1.eup_name
, decode(t2.base_yn, '1', 'Y', 'N') as under_yn
, t2.bldg_no1
, decode(t2.bldg_no2, 0, null, t2.bldg_no2) as bldg_no2
, case when t3.apt_yn = '1' and t3.sigun_bldg_name is not null then t3.sigun_bldg_name end as bldg
, t2.num
, to_char(sysdate, 'YYYYMMDD') as apply_date
from
tbl_road t1
join
tbl_road_addr t2
on
t1.road_code = t2.road_code
and t1.eup_seq = t2.eup_seq
join
tbl_road_info t3
on
t2.num = t3.num
where
t1.using_yn = '0'
and to_char(sysdate, 'YYYYMMDD') between nvl(t1.noti_date, '11110101') and nvl(t1.cancel_date, '99991231')
and to_char(sysdate, 'YYYYMMDD') >= nvl(t2.noti_date, '11110101')
;
이렇게 넣으니 데이터는 5,975,824건에 650MB 정도 된다. 이래도 많다... 예전 우편번호는 5만건 정도밖에 안되는데 ㅜㅜ
컴퓨터 프로그램 내에의 연도 표기 중에서 두 자리 숫자인 세기(century)를 생략한 연도 표현으로 인해 2000년에 컴퓨터 운영 체제 및 각종 업무용 응용 프로그램 등에서 2000년을 1900년과 동일하게 「00」년으로 인식함으로써 발생될 수 있는 각종 문제를 말한다. 또한 밀레니엄 버그를 Y2K 문제라고도 하는데, Y는 연도(year)의 첫 글자를 딴 것이고, K는 1000(kilo)에서 딴 것으로 2000년을 의미한다. [본문으로]