Mybatis 批量插入

2013三月
28

Mybatis 批量插入、存儲過程

本文地址,轉載註明出處:http://www.cnblogs.com/sunwufan/archive/2012/04/27/2473308.html

本文將詳細講解Mybatis訪問MySQL數據庫的三種方式及測試效果。

三種方式:simple方式(基本數據庫逐條訪問)、batch方式(批量操作)、procedure方式(存儲過程)。

文章主要內容:

  • 準備工作:數據庫建立、存儲過程建立等。
  • Eclipse中的項目搭建。
  • 所得數據分析,工作原理淺析。

 

1.準備工作:

 

    命令行還是圖形界面?這個隨意,總之建完就這麼個東西。

      

建表主要有兩點注意:

  • 主鍵要自增(再插入數據是時,省去很多麻煩)
  • 表要建成UTF8格式(否則插入中文可能報錯,或亂碼。當然在安裝MySQL進行配置時也要選GBK之類的)

爲MySQL建存儲過程

 

MySQL存儲過程代碼
複製代碼
 1 CREATE DEFINER = `root`@`localhost` PROCEDURE `NewProc`(IN `name_in` varchar(255),IN `age_in` int,IN `sex_in` varchar(255),IN `password_in` varchar(255),IN `num_in` int)
 2 BEGIN
 3     SET @a=0;
 4 Label:LOOP
 5     SET @a=@a+1;
 6     INSERT INTO person ( name, age, sex, password) VALUES (name_in,age_in,sex_in,password_in);
 7      IF @a=num_in THEN
 8             LEAVE Label;
 9         END IF;
10 END LOOP Label;
11 END;
複製代碼

      num_in爲執行插入操作的次數,在java中設好,傳入。

     不管什麼方式,最終效果一樣就可進入下一步。

     本文地址,轉載註明出處:Mybatis 批量插入、存儲過程

 

2.Eclipse中的項目搭建。

  建立javaProject,最終目錄結構。

簡單說下建立時的邏輯順序:1導入三個jar包–>2建立log4j.properties –>3建立Configuration.xml–>4建立Person.java–>5 建立InsertSimple.java–>6建立person.xml–>7在Configuration.xml中註冊person.xml–>此時InsertSimple.java就可以運行了。

剩下的兩個Insert**.java建立好後在person.xml中注入即可。

ok講完流程了,下面開始狂貼代碼:

  配置文件

Configuration.xml
複製代碼
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 3 <configuration>
 4     <environments default="myexample">
 5         <environment id="myexample">
 6             <transactionManager type="JDBC" />
 7             <dataSource type="POOLED">
 8                 <property name="driver" value="com.mysql.jdbc.Driver" />
 9                 <property name="url" value="jdbc:mysql://localhost:3306/javaweb" />
10                 <property name="username" value="root" />
11                 <property name="password" value="admin"/>
12             </dataSource>
13         </environment>
14     </environments>
15     <mappers>
16         <mapper resource="person.xml"/>
17     </mappers>
18 </configuration>
複製代碼

log4j.properties
複製代碼
 1 log4j.rootLogger=DEBUG, Console
 2 
 3 #Console
 4 log4j.appender.Console=org.apache.log4j.ConsoleAppender
 5 log4j.appender.Console.layout=org.apache.log4j.PatternLayout
 6 log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
 7 
 8 log4j.logger.java.sql.ResultSet=INFO
 9 log4j.logger.org.apache=INFO
10 log4j.logger.java.sql.Connection=DEBUG
11 log4j.logger.java.sql.Statement=DEBUG
12 log4j.logger.java.sql.PreparedStatement=DEBUG  
複製代碼

person.xml
複製代碼
 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 3 <mapper namespace="org.wufan.dao.PersonDao">
 4 
 5 
 6     <insert id="insert" parameterType="org.wufan.vo.Person"
 7         keyProperty="id" keyColumn="GENERATED_KEY" useGeneratedKeys="true">
 8         INSERT INTO person ( name, age, sex, password)
 9         VALUES (
10         #{name},#{age},#{sex},#{password} )
11     </insert>
12 
13 
14     <insert id="insertBatch" parameterType="org.wufan.vo.Person"
15         keyProperty="id" keyColumn="GENERATED_KEY" useGeneratedKeys="true">
16         INSERT INTO person ( name, age, sex, password)
17         VALUES
18         <foreach collection="persons" item="item" index="index"
19             separator=",">
20             (#{item.name},#{item.age},#{item.sex},#{item.password})
21         </foreach>
22     </insert>
23 
24 
25     <insert id="insertByProc" statementType="CALLABLE">
26         {call insertPro(#{name},#{age},#{sex},#{password},#{num})}
27     </insert>
28 
29 </mapper>
複製代碼

  Java代碼

   pojo(vo)類

Person.java
複製代碼
 1 package org.wufan.vo;
 2 
 3 
 4 public class Person {
 5     private int id;
 6     private String name;
 7     private int age;
 8     private String sex;
 9     private String password;
10     public int getId() {
11         return id;
12     }
13     public void setId(int id) {
14         this.id = id;
15     }
16     public String getName() {
17         return name;
18     }
19     public void setName(String name) {
20         this.name = name;
21     }
22     public int getAge() {
23         return age;
24     }
25     public void setAge(int age) {
26         this.age = age;
27     }
28     public String getSex() {
29         return sex;
30     }
31     public void setSex(String sex) {
32         this.sex = sex;
33     }
34     public String getPassword() {
35         return password;
36     }
37     public void setPassword(String password) {
38         this.password = password;
39     }
40 }
複製代碼

  

  普通方式(for循環逐條插入)的主函數

InsertSimple.java
複製代碼
 1 package org.wufan.test;
 2 
 3 import java.io.IOException;
 4 import org.apache.ibatis.io.Resources;
 5 import org.apache.ibatis.session.SqlSession;
 6 import org.apache.ibatis.session.SqlSessionFactory;
 7 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 8 import org.wufan.vo.Person;
 9 
10 public class InsertSimple {
11 
12     /**
13      * @param args
14      */
15     public static void main(String[] args) {
16 
17         int num = 10000;// 插入數據量
18         SqlSessionFactory factory = null;
19         try {
20             factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("Configuration.xml"));
21         } catch (IOException e) {
22             e.printStackTrace();
23         }
24         System.out.println("進入MySQL");
25         double begin = System.currentTimeMillis();
26         SqlSession sqlSession = factory.openSession();
27         Person per = new Person();
28         for (int i = 1; i <= num; i++) {
29 
30             per.setName("InsertSimple" + i);
31             per.setAge(i);
32             per.setSex("男");
33             per.setPassword("密碼" + i);
34 
35             sqlSession.insert("org.wufan.dao.PersonDao.insert", per);
36             sqlSession.commit();
37             // sqlSession.clearCache();
38         }
39         double time = (System.currentTimeMillis() - begin) / 1000;
40         System.out.println("插入共花費時間" + time + "s");
41     }
42 
43 }
複製代碼

 

  batch方式(批量操作)的主函數

InsertBatch。java
複製代碼
 1 package org.wufan.test;
 2 
 3 import java.io.IOException;
 4 import java.util.ArrayList;
 5 import java.util.HashMap;
 6 import java.util.List;
 7 import java.util.Map;
 8 
 9 import org.apache.ibatis.io.Resources;
10 import org.apache.ibatis.session.ExecutorType;
11 import org.apache.ibatis.session.SqlSession;
12 import org.apache.ibatis.session.SqlSessionFactory;
13 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
14 import org.wufan.vo.Person;
15 
16 public class InsertBatch {
17 
18     /**
19      * @param args
20      */
21     public static void main(String[] args) {
22 
23         int num = 10000;// 插入數據量
24         SqlSessionFactory factory = null;
25         List<Person> list = new ArrayList<Person>();
26         try {
27             factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("Configuration.xml"));
28         } catch (IOException e) {
29             e.printStackTrace();
30         }
31         Person per = new Person();
32         for (int i = 1; i <= num; i++) {
33             // per.setId(null);
34             per.setName("InsertBatchName" + i);
35             per.setAge(i);
36             per.setSex("男");
37             per.setPassword("密碼" + i);
38             list.add(per);
39         }
40         double begin = System.currentTimeMillis();
41         SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false);
42 
43         Map<String, List<Person>> tmp = new HashMap<String, List<Person>>();
44         tmp.put("persons", list);
45 
46         sqlSession.insert("org.wufan.dao.PersonDao.insertBatch", tmp);
47         sqlSession.commit();
48 
49         sqlSession.close();
50         double time = (System.currentTimeMillis() - begin) / 1000;
51         System.out.println("插入共花費時間" + time + "s");
52 
53     }
54 }
複製代碼

 

    procedure方式(存儲過程)的主函數

InsertProcedure。java
複製代碼
 1 package org.wufan.test;
 2 
 3 import java.io.IOException;
 4 import java.util.HashMap;
 5 import java.util.Map;
 6 
 7 import org.apache.ibatis.io.Resources;
 8 import org.apache.ibatis.session.SqlSession;
 9 import org.apache.ibatis.session.SqlSessionFactory;
10 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
11 
12 public class InsertProcedure {
13 
14     /**
15      * @param args
16      */
17     public static void main(String[] args) {
18         int num=10000;//插入數據量
19         SqlSessionFactory factory = null;
20         ;
21         try {
22             factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("Configuration.xml"));
23         } catch (IOException e) {
24             e.printStackTrace();
25         }
26         SqlSession sqlSession = factory.openSession();
27         Map map = new HashMap();
28         map.put("name", "InsertProcName");
29         map.put("age", 11);
30         map.put("sex", "男");
31         map.put("password", "1234");
32         map.put("num", num);
33         double begin = System.currentTimeMillis();
34         sqlSession.insert("org.wufan.dao.PersonDao.insertByProc", map);
35         sqlSession.commit();
36         sqlSession.close();
37         double time = (System.currentTimeMillis() - begin) / 1000;
38         System.out.println("插入共花費時間" + time + "s");
39     }
40 }
複製代碼

 

      三種方式的控制檯log

 

      普通方式InsertSimple.java

     

batch方式(批量操作)

 

procedure方式(存儲過程)

如果出現以上三圖,恭喜,哥們你成功了!!

 

3.所得數據分析。

複製代碼
 1     1--num = 1000 ;  
 2        普通處理:3.527s
 3        批量處理:0.628s  
 4        存儲過程處理:0.144s
 5       
 6     2--num = 1w;  
 7        普通處理:27.465s  
 8        批量處理:1.528s  
 9        存儲過程處理:0.585s  
10       
11     3--num = 10w;  
12        普通處理: 335.18s  
13        批量處理: 11.944s
14        存儲過程處理:5.146s  
15       
16     4--num = 30w;  
17        普通處理: 885.335s     
18        批量處理:34.767s
19       存儲過程處理:15.875s
複製代碼

 小筆記本扛不住了,測試就到這裏了。

 注意:在這裏由於存儲數據過大,批量處理時內存會吃不消。

  • 我的筆記本到達10W條數據時,會超過MySQL默認內存設置。Eclipse會報錯。

   解決辦法:http://www.cnblogs.com/chy710/archive/2008/03/06/1093680.html

當插入30w條數據時會報“Java heap space” java虛擬機內存錯誤

   解決辦法:http://developer.51cto.com/art/200906/128572.htm

 

   工作原理淺析(其實上面的DEBUG截圖,已經很能說明問題了):

  • 普通方式,就是一條一條往裏插。

  優點:對計算機基本沒什麼要求,插入方式靈活。

  缺點:重複插入大量數據時,很慢。

  • 批量處理:是把插入請求放入內存,一起提交給數據庫。

  優點:比普通方式快,比存儲過程靈活。

  缺點:吃內存,海量數據壓力略大。

  • 存儲過程:只需在程序中調用存儲過程,向數據庫傳參,。數據庫根據你建好的存儲過程來跑。

  優點:最快,飛起啊!!

  缺點:較爲死板,換數據庫工具,還要在新的數據庫中建立存儲過程。改需求時維護,較爲麻煩。

      從數據報告可看出,普通插入理論上就是一次函數,二階導數爲零(就尼瑪直線啊!);批量處理和存儲過程應該都是會有一個性價比最好的值。一階導數恆大於零,二階導數先正後負。

 

PS:

  •  Mybatis和Ibatis還是有些不同的(雖然沒用過Ibatis),網上的文章比較少,官網的用戶報告下不下來,靠網友經驗和自己摸索做的。本人也是被趕鴨子上架頭一次做,之前都怎麼敲過java代碼,更別說框架了,如有錯誤請指正!!
  • 請不要管我要源碼了,這些寫的比較清楚了(基本上這就算源碼了==!),用源碼一點成就感都沒得。
  •  別問我其他操作怎麼搞,我估計也不會的,如果針對此教程調試不通過可以儘管問。

      

最後是查到的一些對我有幫助的資料:

       ibatis 學習筆記(一) 批量處理 存儲過程 (行文方式直接抄他的,但具體技術就不一樣了,他的測試結果有點怪)

   Mybatis調用存儲過程  ibatis3調用存儲過程  mybatis 3.0.5 –batch insert後如何獲取返回的值

      
MyBatis的關於批量數據操作的體會
 Java:MyBatis簡單入門   MyBatis簡介與配置MyBatis+Spring+MySql 





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章