剛接到這個需求感覺很簡單,看大致意思主要是,從Excel中讀取數據並寫入到數據庫中,唯一特殊之處是有一列大字段(超過4000個字符,在數據庫中需要用CLOB類型存儲)需特殊處理。想了下把代碼寫好了。開始測試發現,導入一條數據需要8秒左右,按這速度導20萬數據的話,不敢往下想了,於是想着代碼調優,優化以後速度變成了一分鐘導入一千多條。以此爲背景,分享下修改過程中的心得。
過程中遇到很多問題,但都順利解決,下面羅列下主要遇到的問題以及有效解決方式。
問題1:如何獲取合併單元格的值?
解決方式:
public static void InsertData(String sjly, File excelFile, String tableName) {
Workbook rwb = null;
//創建輸入流
InputStream stream = new FileInputStream(excelFile);
//獲取Excel文件對象
rwb = Workbook.getWorkbook(stream);
//選擇第一個工作表
Sheet sheet1 = rwb.getSheet(0);
String str = null;
for (int j = 1; j < sheet1.getRows(); j++) {
for (int k = 0; k < sheet1.getColumns(); k++) {
str = sheet1.getCell(k, j).getContents();
Range[] ranges = sheet1.getMergedCells();//合併單元格範圍
for (Range r : ranges) {
if (j > r.getTopLeft().getRow()&& j <= r.getBottomRight().getRow() && k == r.getTopLeft().getColumn()) {
str = sheet1.getCell(r.getTopLeft().getColumn(), r.getTopLeft().getRow()).getContents();
}
}
}
}
問題2:獲取當前時間比系統時間晚8個小時
解決方法:
在獲取當前時間的代碼前設置時區,代碼如下所示:
TimeZone tz =TimeZone.getTimeZone("Asia/Shanghai");
TimeZone.setDefault(tz);
String Nowtime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
問題3:CLOB類型字段處理
解決方式:由於Excel中部分數據列超過4000字符,varchar2不夠用,因此可使用clob數據類型對這些大數據量的數據列進行存儲。而LOB數據不能像其它類型數據一樣直接插入(INSERT)。插入前必須先插入一個空的LOB對象,CLOB類型的空對象爲EMPTY_CLOB (),之後使用帶“for update”的查詢語句鎖定更新行,繼而將空對象修改爲所要插入的LOB對象。對於clob數據的修改,可在修改該表的其他字段信息時同時將clob字段修改爲EMPTY_CLOB (),然後纔對clob字段單獨修改。
問題4:向CLOB對象中寫入數據
解決方式:
String update_sql = "select GSXX from " + tableName + " where ID='"+id+"' for update";
conn.setAutoCommit(false);
rs = conn.createStatement().executeQuery(update_sql);
if (rs.next()) {
/* 取出此CLOB對象 */
oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("GSXX");
/* 向CLOB對象中寫入數據 */
BufferedWriter out = new BufferedWriter(clob .getCharacterOutputStream());
try {
out.write(impvo.getGSXX().toString());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
rs.close();
conn.commit();
conn.setAutoCommit(true);
這樣寫單條的時候問題不大,當有大數據時會報:ORA-00604: 遞歸 SQL 級別 1 出現錯誤,ORA-01000: 超出打開遊標的最大數的錯誤,
解決方式:將 rs = conn.createStatement().executeQuery(update_sql);
改爲:stmt = conn.createStatement();
rs = stmt.executeQuery(update_sql);
並在關閉rs對象時同時關閉stmt 對象即可解決。
解決完以上問題,最大的問題是效率,調試代碼發現比較慢的地方,第一個就是創建數據庫鏈接:Connection conn = DBUtil.getOracleConnection();耗時大概3秒,檢查代碼發現,這一段代碼竟然是在一個For循環裏,,於是把這一段放在For循環外面,然後當參數傳遞。這個修改以後稍微快了兩三秒,但還是很慢;於是把單條處理做成批量處理。速度是1分鐘大概一千多條。大功告成。
代碼下載鏈接:數據導入實現參考源碼