#始めに
英次郎の辞書データをOracleのテーブルに挿入した時の手順を記載します
今回は、前回作成したHTMLファイルから単語情報を抜き出し、DBにインサートするとこまで記載します
#HTMLファイルの情報解析
##やりたいこと
やりたいことを端的に書くと
- Jsoupを使ってtrタグ要素に属するテキスト情報を取得する
- 1.で取得したテキスト情報をQuerryRunnerのbatchメソッドで実行できる型(Object[])に変換する
- 1.,2.で作成したList<Objects[]>のリストを1万件ずつinsertする
です
##実装
WordInserter.java
package word;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jsoup.Jsoup;
import word.sql.WordDictionarySql;
public class WordInserter {
private static String WORD_DICTIONARY =
"C:/Users/*****/Desktop/puisan_102.txt";
private static int LIMMIT = 10000;
public static void main(String args[])throws Exception{
long start = System.currentTimeMillis();
System.out.println("List作成処理を開始します");
List <Object[]>list =getDictionarySource()
.stream().collect(Collectors.toList());
long end = System.currentTimeMillis();
System.out.println("List作成処理が完了しました");
System.out.println(String.format("処理時間 %d秒",(end-start)/1000));
start = System.currentTimeMillis();
System.out.println("DB処理を開始します");
OperateDB.createConnection();
operateList(list);
end = System.currentTimeMillis();
System.out.println("DB作成処理が完了しました");
System.out.println(String.format("処理時間 %d秒",(end-start)/1000));
OperateDB.closeConnection();
}
//Jsoupを使ってHTMLタグを解析し、結果をリストにする関数
public static List<Object[]> getDictionarySource(){
try{
return Jsoup.parse(new File(WORD_DICTIONARY),"UTF-8")
//タグ trに属する子要素をすべて取得する
.getElementsByTag("tr")
.stream()
//タグ trに属する子要素のテキストを取得し、
//map関数を使ってDBにインサートできるデータ構造
//(この場合はObject[])に変換する
.map(s->new DictionaryWord(
s.getAllElements().eachText()).params)
//QueryLinnerのbatchでインサートできるようにList<Object[]>
//に集約する
.collect(Collectors.toList());
}catch(Exception e){
e.printStackTrace();
return new ArrayList<Object[]>();
}
}
//引数で渡されたListに対して、1万件単位でInsert処理を実行する
public static void operateList(List<Object[]> word){
Stream.iterate(1, s->s+1)
.limit((int)Math.ceil(word.size()/LIMMIT))
.forEach(i->executeSQL(word,i));
}
public static void executeSQL(List<Object[]>list,int from){
if(list.size()<((from+1)*LIMMIT))
WordDictionarySql.insertToWordDictionary(
list.subList(from*LIMMIT, list.size()));
else
WordDictionarySql.insertToWordDictionary(
list.subList((from-1)*LIMMIT, from*LIMMIT));
}
public static class DictionaryWord{
public long wordIndex;
public int wordLevel;
public String word;
public String searchWord;
public String meaning;
public String shortVer;
public Object[] params=new Object[6];
public DictionaryWord(List<String>list){
this.wordIndex = Long.parseLong(list.get(1));
params[0] = this.wordIndex;
this.wordLevel = Integer.parseInt(list.get(3).replaceAll("■", ""));
params[1] = this.wordLevel;
this.word = list.get(2);
params[2] = this.word;
this.searchWord = list.get(2).toLowerCase();
params[3] = this.word;
this.meaning = list.get(4);
params[4] = this.meaning;
this.shortVer = list.get(4).length()>1000?
list.get(4).substring(0,1000):list.get(4);
params[5] = this.shortVer;
}
public String toString(){
String format = "wordIndex=%d wordLevel=%d "
+ "word=%s meaning=%s shortVer=%s ";
return String.format(format,this.wordIndex,this.wordLevel,
this.word,this.meaning,this.shortVer);
}
}
}
##雑感
Jsoupの解析結果として作られるDOMとJava8のStream関数の相性が良すぎてびっくりしてます
Stream関数がなければとてもじゃないけど、Javaで実装しようなんて思わなかったかも
(その場合は、rubyあたりを使っていたと思いますが)
#DBへの挿入処理
##やりたいこと
引数でもらったList<Ojbect[]>のlistを前回の記事で作ったOperateDB#insertに渡すだけです
##実装
WordDictionarySql.java
package word.sql;
import java.util.List;
import word.OperateDB;
public class WordDictionarySql {
public static String INSERT_SQL = "insert into WORD_DICTIONAY "
+ "(WORD_INDEX,WORD_LEVEL,WORD,SEARCH_WORD,MEANING,SHORT_VER) values(?,?,?,?,?,?)";
public static void insertToWordDictionary(List<Object[]> word){
try{
OperateDB.insert(word,INSERT_SQL);
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException("DBのインサート処理でエラーが発生しました");
}
}
}
##感想
約33万件のデータをOracleのテーブルに投入しましたが、実行時間は102秒程度でした
ちなみに,autocommitを有効にしている場合は、実行時間が230秒と倍程度かかっていました
autocommitを有効するとパフォーマンスが落ちる、ということを数字の上からも実感することができました