筆者曾在專案中碰到這樣一個問題:系統每隔15min將本地oracle資料庫中的交易打包形成檔案上傳到FTP,上傳完成後將交易的標記位更改為已上傳,大致sql操作順序如下
1. select * from a where balance_status='00' //00為初始態(未上傳),01為已上傳
2. ......//打包形成檔案上傳
3. update a set balance_status='01' where id in
<foreach collection="list" index="index" item="item" separator="," open="(" close=")">
#{item.id}
</foreach> //第3步的list就是來自第一步中取的集合複製程式碼
專案上線跑了一段時間沒有任何問題,直到有一天突然出現了問題,把日誌down下來發現在執行第3步的時候報了錯
java.sql.SQLException: ORA-01795: 列表中的最大表示式數為 1000
一看list的大小超過了1000條,百度這個錯誤程式碼,發現是oracle對in()中的數量有限制,最高不能超過1000,發現了問題怎麼改呢?最終經過討論決定使用jdbc的批量更新,廢話少說,直接上程式碼
int m=list.size(); //select獲取的集合
String[] sqls=new String[m];
for(int t=0;t<m;t++){
sqls[t]="update a set balance_status='01' where id ='"+list.get(t).id+"'";
}
//呼叫工具類的批量更新方法,本質上使用了Statement介面定義的executeBatch()方法
DbUtils.executeBatchUpdate(sqls); 複製程式碼
public class DbUtils {
private static String driverClass = PropertiesUtils.getPropertiesValue("DB_DRIVER_CLASS");
private static String url = PropertiesUtils.getPropertiesValue("DB_URL");
private static String user = PropertiesUtils.getPropertiesValue("DB_USER");
private static String pwd = PropertiesUtils.getPropertiesValue("DB_PWD");
private static Statement stm = null;
private static PreparedStatement sta = null;
private static ResultSet rs = null;
private static Connection conn;
private static CallableStatement cs = null;
/**
* 載入驅動程式
*/
static {
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* @return 連線物件
*/
public static Connection getConn() throws ClassNotFoundException, SQLException {
try {
conn = DriverManager.getConnection(url, user, pwd);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 批量更新資料
* @param sqls
* @throws Exception
*/
public static void executeBatchUpdate(String[] sqls) throws Exception {
if (sqls!=null&&sqls.length>0) {
try {
getConn();
//關閉自動提交
conn.setAutoCommit(false);
stm=conn.createStatement();
for(String sql: sqls){
// 用PreparedStatement的批量處理
stm.addBatch(sql);
}
stm.executeBatch();// 執行批處理
conn.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
close();
}
}
}
/**
* 關閉資源
*/
public static void close() {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (sta != null) {
sta.close();
}
if (cs != null) {
cs.close();
}
} catch (SQLException e2) {
e2.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}複製程式碼
總結
產生這個問題的根源在於筆者存在知識盲點,就是in後面的列舉數不能超過1000,測試的時候又沒有達到這個資料量所以沒有發現這個問題。為了儘量避免這類問題,一方面是開發人員提高自身,另一方面是測試人員有條件的情況下要儘可能測試大資料量的情況,再者萬一出現問題,要細心分析解決。有個通用的原則就是資料量較大時儘量少用框架,可改為jdbc,儲存過程或者其它方式來解決