在向數據庫中添加數據時,不免會遇到批量添加數據的問題。下面就是使用JDBC來實現批量插入的幾種方法。java
準備工做:mysql
names
表id
:主鍵,自增name
:varchar(25),保證長度夠用就行CREATE TABLE names( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(25) );
方法一:sql
最直接的頻繁執行SQL語句來插入數據庫
long start = System.currentTimeMillis(); // 獲取數據庫鏈接 Connection conn= DriverManager.getConnection(url, user, password); // SQL語句 String sql = "insert into names(name) values(?);"; // 預編譯SQL語句 PreparedStatement ps = conn.prepareStatement(sql); // 批量插入 2萬 條數據 for (int i = 0; i < 20000; i++) { ps.setObject(1, "name_"+i); // 填充佔位符? ps.execute(); // 每一條數據都執行一次 } long end = System.currentTimeMillis(); System.out.println("花費的時間爲:" + (end - start)); // 花費的時間爲:794551 // 關閉資源 ps.close(); conn.close();
方式二:優化
使用executeBatch()
來批量插入數據url
須要在數據庫鏈接的url中添加rewriteBatchedStatements=true
字段,讓數據庫開啓批處理默認code
long start = System.currentTimeMillis(); // 獲取數據庫鏈接 Connection conn= DriverManager.getConnection(url, user, password); // SQL語句 String sql = "insert into names(name) values(?)"; // 注意這裏! 必定不要加結尾的分號; // 預編譯SQL語句 PreparedStatement ps = conn.prepareStatement(sql); // 批量插入 100萬 條數據 for (int i = 1; i <= 1000000; i++) { ps.setObject(1, "name_"+i); // 添加到同一batch中 ps.addBatch(); if (i % 500 == 0) { // 每批次夠500條才執行 控制這個數也能夠提升點速度 // 執行該batch的插入操做 ps.executeBatch(); // 清空已執行的batch ps.clearBatch(); } } long end = System.currentTimeMillis(); System.out.println("花費的時間爲:" + (end - start)); // 花費的時間爲:5177 // 關閉資源 ps.close(); conn.close();
注意:必定不要給SQL語句添加結尾的
;
。不然會拋異常。server
java.sql.BatchUpdateException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '('name_2'),('name_3'),('name_4'),('name_5'),('name_6'),('name_7'),('name_8'),('n' at line 1...
至於緣由,JDBC的源碼實在太晦澀了,看一會就先後鏈接不上了,因此筆者認爲應該是因爲在MySQL中批量插入的SQL語句的問題。資源
就是對於names
表,直接在MySQL中用SQL語句來批量添加數據時,能夠這樣get
insert into names(`name`) values("name_1"), ("name_2"), ("name_3");
這行去掉;
也能正常運行
可是若是這樣,注意分號
insert into names(`name`) values("name_1");, ("name_2"), ("name_3")
那麼"name_1"插入表中,後面2和3沒有,而且MySQL拋異常。
那麼或許在JDBC中,每次addBatch()
,都是將要放在佔位符?
的數據先存在ArrayList
中,當執行executeBatch()
時,遍歷ArrayList
將第一個數據"name_1"
放在SQL語句的?
處,後續的所有構形成,("name_2")
、,("name_3")
的形式鏈接在這條SQL語句後面,最終構形成一個長的插入SQL語句,再執行,完成批量插入。
即:
insert into names(`name`) values("name_1") insert into names(`name`) values("name_1"), ("name_2") insert into names(`name`) values("name_1"), ("name_2"), ("name_3") insert into names(`name`) values("name_1"), ("name_2"), ("name_3")..., ("name_batchSize")
這樣因爲執行拼在一塊兒的SQL就能夠完成批量插入。
可是若是insert into names(name) values(?);
結尾有個;
,就變成這樣:
insert into names(`name`) values("name_1"); insert into names(`name`) values("name_1");, ("name_2") insert into names(`name`) values("name_1");, ("name_2"), ("name_3") insert into names(`name`) values("name_1");, ("name_2"), ("name_3")..., ("name_batchSize")
那麼JDBC的對SQL語句的語法檢查或語義檢查沒法經過,就會拋異常。
數據庫中也不會有"name_1"這條數據。
以上是筆者的推測,並無經過JDBC源碼驗證。
方式三:
在方式二的基礎上再進一步優化,除了改變一批次的容量(上面是500)外,還能夠設置不容許自動提交數據,改成手動提交數據。
// 設置不容許自動提交數據 conn.setAutoCommit(false); // 該行代碼放在獲取數據庫鏈接後 // ... 批量插入操做同上 // 提交數據 conn.commit(); // 在批量插入的for循環後 // 花費時間爲:3954
另外,還有個executeLargeBatch()
方法
當要總共要插入1億條數據,而且一個batch爲100萬
executeBatch()
花費了413635毫秒
executeLargeBatch()
花費了386389毫秒
emmm...可能不是單純替換着用的,哈哈哈!