blah blah

groovy 사용 간단 예 – gpath, withBatch 등

이번에는 groovy.

******

프로젝트 지원 나와서 간단한 작업 하나를 받았습니다.
특정 디렉토리 안에 있는 동일한 구조의 xml 파일들에서 몇 개의 element 값을 추출해서 DB 에 넣는거랍니다.

일단 뭘로 짤까 잠시 고민했는데 텍스트 파일 처리하는 이 정도의 작업이면 역시나 스크립트 언어가 적격이라 판단했습니다.
그리고 이 작업 맡을 분이 자바 사용자니 딴거보다는 적응이 용이하겠다 싶어서 스크립트 언어 중에서 groovy 를 선택했습니다.

그럼 본격적으로 …

xml 은 대충 이런 식.

<?xml version=”1.0″ encoding=”UTF-8″?>
<abouthjs>
<titleimg>../img/news/tit_notice.gif</titleimg>
<contenttitle>어쩌구 저쩌구 24HR FAQ)</contenttitle>
<appendimg/> <viewdate>2010-11-10 13</viewdate>
<contents>
<contentimg/>
<content>어쩌구 저저꾸 24HR rule 관련 Customer Advisory…</content>
</contents>
<servicekind>T</servicekind>
<localekind/>

</abouthjs>

여기서 contenttitle, content, viewdate 등의 값을 추출해야 합니다.

groovy 소스부터 보겠습니다.

import groovy.io.FileType
import groovy.sql.*

def getXmlElement(xmlFile) {
def xml = new XmlSlurper().parse((xmlFile.path).replace(“\\”, “/”))   // (3)
def eleList = []
eleList.add(xml.contenttitle.text())                                  // (4)
eleList.add(xml.contents.content.text())
eleList.add(xml.viewdate.text())
return eleList
}

def insertDb(resultMap) {
def db = Sql.newInstance(“jdbc:oracle:thin:@dbUrl:port:dbSid”, “id”, “pw”, “oracle.jdbc.driver.OracleDriver”) // (5)
db.withBatch(100, “INSERT INTO test (val1, val2, val3) VALUES (?, ?, ?)”) { ps ->                               // (6)
resultMap.each { k, v ->
ps.addBatch(v)                                                // (6-1)
}
}

db.close()
}

def folder = args?.size() ? args[0] : “.”                            // (1)
resultMap = [:]
new File(folder).traverse(type:FileType.FILES, nameFilter:~/.*\.xml/, maxDepth:0) {    // (2)
resultMap[it.getName()] = getXmlElement(it)                                                         // (2-1)
}

insertDb(resultMap)

단순한 작업이라 달랑 이게 전부입니다.

groovy 의 아주 기초 문법들은 일단 패스하고 이 작업에 사용한 groovy 의 몇가지 기능만 추려보겠습니다.

  • job1. 디렉토리에서 파일 목록 구하기 : File
  • job2. XML 에서 데이타 추출 : gpath
  • job3. DB 처리 : groovy sql

소스 실행 방법은

C:\>groovy XmlTest.groovy C:\data\xml

( 소스 파일명은 XmlTest.groovy, XML 파일이 있는 디렉토리는 C:\data\xml 이라고 했습니다. )

소스 실행하면 진입점은 (1)이 됩니다.
인자로 준 디렉토리를, 인자가 없을 경우에는 현재 디렉토리를  변수로 받습니다.

job1에 해당하는게 (2)입니다.
지정한 디렉토리에서 파일 목록 구하는 방법은 꽤 많은데 이번 경우에는 traverse(…)라는 함수를 사용해봤습니다.
xml 파일 목록만 구하기 위해 nameFilter:~/.*\.xml/ 를, 검색할 디렉토리의 하위 디렉토리는 찾을 필요 없기에 maxDepth:0 등의 패러미터를 주었습니다.
(2-1)에서는 루프 돌면서 각각의 파일 정보(it)를 넘겨서 원하는 element 값들을 되돌려받아 resultMap 이라는 Map 에 파일명을 키값으로 담습니다.

job2에 관련된게 getXmlElement(…)입니다.
(3)의 XmlSlurper().parse 에서 xml 파일 읽어들였고 (4)에서 원하는 element 값들 추출합니다.

(4)에서 쓰인게 gpath 란 기능인데 이건 xpath 의 groovy 구현체라고 생각하면 됩니다.
xml 샘플의 구조와 xml.contents.content.text() 를 매칭시켜보면 gpath 가 얼마나 간편하게 특정 노드의 데이타를 가져올 수 있는지 알 수 있습니다.
xpath 란거 이름만 들어봤지 실제 써보지 않았는데 xml 데이타 추출하는데서 만큼은 DOM이나 SAX 쓰는 것보다 무지 편한 것 같습니다.
자바5 부터는 javax.xml.xpath 패키지가 기본 포함되었다 하니 앞으로 자바 코딩할 일 있을 때면 xpath 를 애용하게 될 것 같네요.

job3에 해당하는건 insertDb(…)입니다.
groovy 역시 sql 사용 위해서는 jdbc 쓰므로 자바 코딩할 때 처럼 (5)번 써주면 되고 기타 groovy sql 기본 사용법은 패스.
이번 코딩에서는 groovy 1.8.1 버전 부터 지원되기 시작한 자바의 batchUpdate 에 해당하는 withBatch를 써보았습니다.

자바의 batchUpdate는 아시다시피 다수의 sql문을 배열에 담은 후 한꺼번에 처리해주어 성능상의 이점을 얻을 수 있죠.
마찬가지로 (6-1) addBatch 를 통해 복수의 xml 파일에서 추출한 값들을 순차적으로 넣어줍니다.

(6)의 insert 문 보면 필요한 bind 변수가 3개 인데 (6-1) ps.addBatch(v) 한 줄로 처리되었나 궁금하신 분도 계실겁니다.
여기서 v 는 소스 따라가보면 getXmlElement 에서 리턴한 eleList 라는 List 타입의 변수값입니다.
즉, v 에는 [“Test page”, “테스트 컨텐츠”, “2011-11-10 13”] 과 같은 형태로 값이 들어가 있는 셈이므로 저 한 줄 깔끔하게 처리됩니다.

batchUpdate 안쓰고 일반적인 방식으로 하자면 다음 정도로 하면 될겁니다.

def db = Sql.newInstance(…)
new File(folder).traverse(
type:FileType.FILES,
nameFilter:~/.*\.xml/,
maxDepth:0
) {
resultList = getXmlElement(it)
db.execute “INSERT INTO test (val1, val2, val3) VALUES (?, ?, ?)”, resultList
};db.close()

이상입니다.

*****

위 내용은 프로토타입으로 작성한거고 실제 구현했던거는 제약 조건이 꽤 많아서 소스 전면 수정( 한 300백 라인 정도)했습니다.

이 때 추가된 걸로 몇가지 간단하지만 나중에 재활용할만한거 두 어가지만 추가 정리

def getDurationTime() {
def timeStart = new Date()

….

def timeStop = new Date()
TimeDuration duration = TimeCategory.minus(timeStop, timeStart)

log.info duration
}

첨부 파일 처리할 때 쓴거로 유니크한 파일명 부여 위해서 uuid 사용한거

def getUuidFileName(fExt) {
uuid32 = getUUID()
if (!fExt.isAllWhitespace() && fExt.size() < 4) {
uuid32 = uuid32.substring(0, uuid32.size() – fExt.size() – 1) + “.” + fExt
}

return uuid32
}

 

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s