java實(shí)現(xiàn)jdbc批量插入數(shù)據(jù)
文章首先介紹三種JDBC批量插入編程方法,進(jìn)行比較,具體內(nèi)容如下
JDBC批量插入主要用于數(shù)據(jù)導(dǎo)入和日志記錄因?yàn)槿罩疽话愣际窍葘?xiě)在文件下的等。
我用Mysql 5.1.5的JDBC driver 分別對(duì)三種比較常用的方法做了測(cè)試
方法一:使用PreparedStatement加批量的方法
try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(o_url, userName, password); conn.setAutoCommit(false); String sql = "INSERT adlogs(ip,website,yyyymmdd,hour,object_id) VALUES(?,?,?,?,?)"; PreparedStatement prest = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY); for(int x = 0; x < size; x++){ prest.setString(1, "192.168.1.1"); prest.setString(2, "localhost"); prest.setString(3, "20081009"); prest.setInt(4, 8); prest.setString(5, "11111111"); prest.addBatch(); } prest.executeBatch(); conn.commit(); conn.close(); } catch (SQLException ex) { Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex); } catch (ClassNotFoundException ex) { Logger.getLogger(MyLogger.class.getName()).log(Level.SEVERE, null, ex); }
說(shuō)明下在建Statement的時(shí)候,后面兩個(gè)參數(shù)的意義:
第一個(gè)參數(shù)指定 ResultSet 的類(lèi)型。其選項(xiàng)有:
TYPE_FORWARD_ONLY:缺省類(lèi)型。只允許向前訪(fǎng)問(wèn)一次,并且不會(huì)受到其他用戶(hù)對(duì)該數(shù)據(jù)庫(kù)所作更改的影響。
TYPE_SCROLL_INSENSITIVE:允許在列表中向前或向后移動(dòng),甚至可以進(jìn)行特定定位,例如移至列表中的第四個(gè)記錄或者從當(dāng)前位置向后移動(dòng)兩個(gè)記錄。不會(huì)受到其他用戶(hù)對(duì)該數(shù)據(jù)庫(kù)所作更改的影響。
TYPE_SCROLL_SENSITIVE:象 TYPE_SCROLL_INSENSITIVE 一樣,允許在記錄中定位。這種類(lèi)型受到其他用戶(hù)所作更改的影響。如果用戶(hù)在執(zhí)行完查詢(xún)之后刪除一個(gè)記錄,那個(gè)記錄將從 ResultSet 中消失。類(lèi)似的,對(duì)數(shù)據(jù)值的更改也將反映在 ResultSet 中。
第二個(gè)參數(shù)設(shè)置 ResultSet 的并發(fā)性,該參數(shù)確定是否可以更新 ResultSet。其選項(xiàng)有:
CONCUR_READ_ONLY:這是缺省值,指定不可以更新
ResultSet CONCUR_UPDATABLE:指定可以更新 ResultSet
方法二:使用Statement加批量的方法
conn.setAutoCommit(false); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); for(int x = 0; x < size; x++){ stmt.addBatch("INSERT INTO adlogs(ip,website,yyyymmdd,hour,object_id) VALUES('192.168.1.3', 'localhost','20081009',8,'23123')"); } stmt.executeBatch(); conn.commit();
方法三:直接使用Statement
conn.setAutoCommit(false); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); for(int x = 0; x < size; x++){ stmt.execute("INSERT INTO adlogs(ip,website,yyyymmdd,hour,object_id) VALUES('192.168.1.3', 'localhost','20081009',8,'23123')"); } conn.commit();
使用上述方法分別插入10萬(wàn)條數(shù)據(jù)的平均測(cè)試時(shí)間為:
方法一:17.844s
方法二:18.421s
方法三:16.359s
可以看出JDBC的batch語(yǔ)句插入不但沒(méi)有性能提升,反而比沒(méi)有用batch的時(shí)候要慢,當(dāng)然這可能跟JDBC具體驅(qū)動(dòng)的實(shí)現(xiàn)方法有關(guān)。 附件中是我測(cè)試代碼,可以用來(lái)在自己電腦上跑一下。
在執(zhí)行批量插入的時(shí)候最主要的是將自動(dòng)提交取消,這樣不管是否用JDBC的batch語(yǔ)法應(yīng)該都沒(méi)有關(guān)系。
conn.setAutoCommit(false)
個(gè)人覺(jué)得第一種方法是最方便最實(shí)用的。
jdbc批量插入數(shù)據(jù) 例子講解:
最近做一個(gè)將excel數(shù)據(jù)導(dǎo)入數(shù)據(jù)庫(kù)的程序時(shí),由于數(shù)據(jù)量大,準(zhǔn)備采用jdbc的批量插入。于是用了preparedStatement.addBatch();當(dāng)加入1w條數(shù)據(jù)時(shí),再執(zhí)行插入操作,preparedStatement.executeBatch()。我原以為這樣會(huì)很快,結(jié)果插入65536條數(shù)據(jù)一共花30多分鐘,完全出乎我的意料。于是問(wèn)了一下同事,他們?cè)谔幚磉@種大批量數(shù)據(jù)導(dǎo)入的時(shí)候是如何處理的,發(fā)現(xiàn)他們也是用的jdbc批量插入處理,但與我不同是:他們使用了con.setAutoCommit(false);然后再preparedStatement.executeBatch()之后,再執(zhí)行con.commit();于是再試,什么叫奇跡?就是剛剛導(dǎo)入這些數(shù)據(jù)花了半小時(shí),而加了這兩句話(huà)之后,現(xiàn)在只用了15秒鐘就完成了。于是去查查了原因,在網(wǎng)上發(fā)現(xiàn)了如下一段說(shuō)明:
* When importing data into InnoDB, make sure that MySQL does not have autocommit mode enabled because that
requires a log flush to disk for every insert. To disable autocommit during your import operation, surround it with
SET autocommit and COMMIT statements:
SET autocommit=0;
... SQL import statements ...
COMMIT;
第一次,正是因?yàn)闆](méi)有setAutoCommit(false);那么對(duì)于每一條insert語(yǔ)句,都會(huì)產(chǎn)生一條log寫(xiě)入磁盤(pán),所以雖然設(shè)置了批量插入,但其效果就像單條插入一樣,導(dǎo)致插入速度十分緩慢。
部分代碼如下:
String sql = "insert into table *****"; con.setAutoCommit(false); ps = con.prepareStatement(sql); for(int i=1; i<65536; i++){ ps.addBatch(); // 1w條記錄插入一次 if (i % 10000 == 0){ ps.executeBatch(); con.commit(); } } // 最后插入不足1w條的數(shù)據(jù) ps.executeBatch(); con.commit();
以上只是小菜,下面接著“上菜”:
1、測(cè)試批量寫(xiě)入數(shù)據(jù)
long start = System.currentTimeMillis(); DaoRecord daoRecord = new DaoRecord(); List<T> list = new ArrayList<T>(); for(int i = 1; i <= 1000; i++){ for(int j = 1; j <= 1000; j++){ T t = new T(); t.setI(i); t.setJ(j); list.add(t); } } daoRecord.InsertBatch(list); System.out.println("耗時(shí):" + (System.currentTimeMillis()-start)+"毫秒");
2、批量寫(xiě)入數(shù)據(jù)測(cè)試
public void InsertBatch(List<T> list){ String sql = "insert into t(go,back) values(?,?)"; DBHelper dbh = new DBHelper(sql); Connection conn = dbh.returnConn(); try { conn.setAutoCommit(false);//注意此句一定要為false,原因見(jiàn)第一篇參考文獻(xiàn) PreparedStatement ps = conn.prepareStatement(sql); for(int i = 0; i < list.size(); i++){ ps.setInt(1, list.get(i).getI()); ps.setInt(2, list.get(i).getJ()); ps.addBatch(); if (i % 10000 == 0){ ps.executeBatch(); conn.commit(); } } ps.executeBatch(); conn.commit(); conn.close(); } catch (SQLException e) { // TODO 自動(dòng)生成的 catch 塊 e.printStackTrace(); } }
數(shù)據(jù)表:
實(shí)驗(yàn)結(jié)果:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。
相關(guān)文章
利用Spring MVC+Mybatis實(shí)現(xiàn)Mysql分頁(yè)數(shù)據(jù)查詢(xún)的過(guò)程詳解
這篇文章主要給大家介紹了關(guān)于利用Spring MVC+Mybatis實(shí)現(xiàn)Mysql分頁(yè)數(shù)據(jù)查詢(xún)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08idea啟動(dòng)與jar包啟動(dòng)中使用resource資源文件路徑的問(wèn)題
這篇文章主要介紹了idea啟動(dòng)與jar包啟動(dòng)中使用resource資源文件路徑的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07maven打包成第三方j(luò)ar包且把pom依賴(lài)包打入進(jìn)來(lái)的方法
這篇文章主要介紹了maven打包成第三方j(luò)ar包且把pom依賴(lài)包打入進(jìn)來(lái)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11Spring Boot整合MybatisPlus逆向工程(MySQL/PostgreSQL)
MyBatis-Plus是MyBatis的增強(qiáng)工具,本文主要介紹了Spring Boot整合MybatisPlus逆向工程(MySQL/PostgreSQL),具有一定的參考價(jià)值,感興趣的可以了解一下2021-07-07Java對(duì)敏感數(shù)據(jù)進(jìn)行加密的方法詳解
敏感數(shù)據(jù)的加密是數(shù)據(jù)安全的重要方面,尤其是對(duì)于手機(jī)號(hào)和身份證號(hào)這類(lèi)個(gè)人信息,本文主要為大家介紹了Java對(duì)敏感數(shù)據(jù)進(jìn)行加密的相關(guān)方法,希望對(duì)大家有所幫助2024-03-03springboot整合httpClient代碼實(shí)例
這篇文章主要介紹了springboot整合httpClient代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12