date比較大小 mybatis_Hibernate 和 MyBatis 哪個更好用?

87d6d5d27e3b98119386ae9e11ced0e3.png??Java大聯盟

? 幫助萬千Java學習者持續成長

關注

da047d81878c2c78f8af80f65d20f43c.gif

作者|SylvanasSun鄭沐興

https://zhuanlan.zhihu.com/p/21966051

B 站搜索:楠哥教你學Java

獲取更多優質視頻教程

前言

由于編程思想與數據庫的設計模式不同,生出了一些ORM框架。核心都是將關系型數據庫和數據轉成對象型。當前流行的方案有Hibernate與myBatis。兩者各有優劣。競爭激烈,其中一個比較重要的考慮的地方就是性能。因此筆者通過各種實驗,測出兩個在相同情景下的性能相關的指數,供大家參考。

測試目標

以下測試需要確定幾點內容:
性能差異的場景;
性能不在同場景下差異比;
找出各架框優劣,各種情況下的表現,適用場景。

測試思路

測試總體分成:單表插入,關聯插入,單表查詢,多表查詢。
測試分兩輪,同場景下默認參數做一輪,調優做強一輪,橫縱對比分析了。
測試中盡保證輸入輸出的一致性。
樣本量盡可能大,達到10萬級別以上,減少統計誤差。

測試提綱

具體的場景情況下
插入測試1:10萬條記錄插入。
查詢測試1:100萬數據中單表通過id查詢100000次,無關聯字段。
查詢測試2:100萬數據中單表通過id查詢100000次,輸出關聯對象字段。
查詢測試3:100萬*50萬關聯數據中查詢100000次,兩者輸出相同字段。

準備

數據庫:mysql 5.6
表格設計:
twitter:推特

CREATE TABLE `twitter` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `add_date` datetime DEFAULT NULL,  `modify_date` datetime DEFAULT NULL,  `ctx` varchar(255) NOT NULL,  `add_user_id` bigint(20) DEFAULT NULL,  `modify_user_id` bigint(20) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `UPDATE_USER_FORI` (`modify_user_id`),  KEY `ADD_USER_FORI` (`add_user_id`),  CONSTRAINT `ADD_USER_FORI` FOREIGN KEY (`add_user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL,  CONSTRAINT `UPDATE_USER_FORI` FOREIGN KEY (`modify_user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL) ENGINE=InnoDB AUTO_INCREMENT=1048561 DEFAULT CHARSET=utf8

user: 用戶

CREATE TABLE `user` (  `id` bigint(20) NOT NULL AUTO_INCREMENT,  `name` varchar(255) DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=524281 DEFAULT CHARSET=utf8

測試數據準備:
表一:twitter
無數據。

表二:user
50萬個隨機的用戶名。

隨機內容推特表(material_twitter)
無id,僅有隨機字符串內容,共10萬條。
用于插入控推特表。

生成數據代碼,關聯100個用戶:

insert into twitter(ctx,add_user_id,modify_user_id,add_date,modify_date)SELECT name,ROUND(RAND()*100)+1,ROUND(RAND()*100)+1,'2016-12-31','2016-12-31'from MATERIAL

生成數據代碼,關聯500000個用戶:

insert into twitter(ctx,add_user_id,modify_user_id,add_date,modify_date)SELECT name,ROUND(RAND()*500000)+1,ROUND(RAND()*500000)+1,'2016-12-31','2016-12-31'from MATERIAL

實體代碼

@Entity@Table(name = "twitter")public class Twitter implements java.io.Serializable{  private Long id;  private Date add_date;  private Date modify_date;  private String ctx;  private User add_user;  private User modify_user;    private String createUserName;    @Id  @GeneratedValue(strategy = IDENTITY)  @Column(name = "id", unique = true, nullable = false)  public Long getId() {    return id;  }  public void setId(Long id) {    this.id = id;  }  @Temporal(TemporalType.DATE)  @Column(name = "add_date")  public Date getAddDate() {    return add_date;  }  public void setAddDate(Date add_date) {    this.add_date = add_date;  }  @Temporal(TemporalType.DATE)  @Column(name = "modify_date")  public Date getModifyDate() {    return modify_date;  }  public void setModifyDate(Date modify_date) {    this.modify_date = modify_date;  }  @Column(name = "ctx")  public String getCtx() {    return ctx;  }  public void setCtx(String ctx) {    this.ctx = ctx;  }  @ManyToOne(fetch = FetchType.LAZY)  @JoinColumn(name = "add_user_id")  public User getAddUser() {    return add_user;  }  public void setAddUser(User add_user) {    this.add_user = add_user;  }  @ManyToOne(fetch = FetchType.LAZY)  @JoinColumn(name = "modify_user_id")  public User getModifyUser() {    return modify_user;  }  public void setModifyUser(User modify_user) {    this.modify_user = modify_user;  }  @Transient  public String getCreateUserName() {    return createUserName;  }  public void setCreateUserName(String createUserName) {    this.createUserName = createUserName;  }  }

開始

插入測試1
代碼操作:
將隨機內容推特表的數據加載到內存中,然后一條條加入到推特表中,共10萬條。


關鍵代碼:
hibernate:

Session session = factory.openSession();    session.beginTransaction();    Twitter t = null;    Date now = new Date();    for(String materialTwitter : materialTwitters){// System.out.println("materialTwitter="+materialTwitter);           t = new Twitter();        t.setCtx(materialTwitter);           t.setAddDate(now);           t.setModifyDate(now);           t.setAddUser(null);           t.setModifyUser(null);           session.save(t);      }        session.getTransaction().commit();mybatis:      Twitter t = null;    Date now = new Date();    for(String materialTwitter : materialTwitters){// System.out.println("materialTwitter="+materialTwitter);           t = new Twitter();           t.setCtx(materialTwitter);           t.setAddDate(now);           t.setModifyDate(now);           t.setAddUser(null);           t.setModifyUser(null);           msession.insert("insertTwitter", t);      }    msession.commit();

TwitterMapper.xml,插入代碼片段:

<insert id="insertTwitter" keyProperty="id" parameterType="org.pushio.test.show1.entity.Twitter" useGeneratedKeys="true">     insert into twitter(ctx, add_date,modify_date) values (#{ctx},#{add_date},#{modify_date})insert>

查詢測試1

通過id從1遞增到10萬依次進行查詢推特內容,僅輸出微博內容。
關鍵代碼:
hibernate:

long cnt = 100000;    for(long i = 1; i <= cnt; ++i){      Twitter t = (Twitter)session.get(Twitter.class, i);      //System.out.println("t.getCtx="+ t.getCtx() + " t.getUser.getName=" + t.getAddUser().getName());    }

mybatis:

long cnt = 100000;    for(long i = 1; i <= cnt; ++i){      Twitter t = (Twitter)msession.selectOne("getTwitter", i);      //System.out.println("t.getCtx="+ t.getCtx() + " t.getUser.getName=" + t.getAddUser().getName());    }

查詢測試2

與查詢測試1總體一樣,增加微博的創建人名稱字段,此處需要關聯。
其中微博對應有10萬個用戶。可能一部份用戶重復。這里對應的用戶數可能與hibernate配懶加載的情況有影響。


此處體現了hibernate的一個方便處,可以直接通過getAddUser()可以取得user相關的字段。


然而myBatis則需要編寫新的vo,因此在測試batis時則直接在Twitter實體中增加創建人員名字成員(createUserName)。


此處hibernate則會分別測試有懶加載,無懶加載。
mybatis會測有默認與有緩存兩者情況。


其中mybatis的緩存機制比較難有效配置,不適用于真實業務(可能會有臟數據),在此僅供參考。


測試時,對推特關聯的用戶數做了兩種情況,一種是推特共關聯了100個用戶,也就是不同的推特也就是在100個用戶內,這里的關聯關系隨機生成。
另外一種是推特共關聯了50萬個用戶,基本上50個用戶的信息都會被查詢出來。


在上文“準備”中可以看到關聯數據生成方式。

關鍵代碼:
hibernate:

long cnt = 100000;for(long i = 1; i <= cnt; ++i){      Twitter t = (Twitter)session.get(Twitter.class, i);      t.getAddUser().getName();//加載相應字段      //System.out.println("t.getCtx="+ t.getCtx() + " t.getUser.getName=" + t.getAddUser().getName());    }

急懶加載配置更改處,Twitter.java:

@ManyToOne(fetch = FetchType.EAGER)//急加載      //@ManyToOne(fetch = FetchType.LAZY)//懶加載  @JoinColumn(name = "add_user_id")  public User getAddUser() {    return add_user;  }

mybatis:

for(long i = 1; i <= cnt; ++i){      Twitter t = (Twitter)msession.selectOne("getTwitterHasUser", i);      //System.out.println("t.getCtx="+ t.getCtx() + " t.getUser.getName=" + t.getCreateUserName());    }

TwitterMapper.xml配置:

<select id="getTwitterHasUser" parameterType="long"         resultType="org.pushio.test.show1.entity.Twitter">         select twitter.*,user.name as creteUserName from twitter,user         where twitter.id=#{id}           AND twitter.add_user_id=user.idselect>

測試結果

6807be9af569986e5de897c251d24b74.png

測試分析

測試分成了插入,單表查詢,關聯查詢。關聯查詢中hibernate分成三種情況進行配置。其中在關聯字段查詢中,hibernate在兩種情況下,性能差異比較大。都是在懶加載的情況下,如果推特對應的用戶比較多時,則性能會比僅映射100個用戶的情況要差很多。

換而言之,如果用戶數量少(關聯的總用戶數)時,也就是會重復查詢同一個用戶的情況下,則不需要對用戶表做太多的查詢。其中通過查詢文檔后,證明使用懶加載時,對象會以id為key做緩存,也就是查詢了100個用戶后,后續的用戶信息使用了緩存,使性能有根本性的提高。甚至要比myBatis更高。

如果是關聯50萬用戶的情況下,則hibernate需要去查詢50萬次用戶信息,并組裝這50萬個用戶,此時性能要比myBatis性能要差,不過差異不算大,小于1ms,表示可以接受。其中hibernate非懶加載情況下與myBatis性能差異也是相對其他測試較大,平均值小于1ms。

這個差異的原因主要在于,myBatis加載的字段很干凈,沒有太多多余的字段,直接映身入關聯中。反觀hibernate則將整個表的字都會加載到對象中,其中還包括關聯的user字段。

hibernate這種情況下有好有壞,要看具體的場景,對于管理平臺,需要展現的信息較多,并發要求不高時,hibernate比較有優勢。
然而在一些小活動,互聯網網站,高并發情況下,hibernate的方案太不太適合,myBatis+VO則是首選。

測試總結

總體初觀,myBatis在所有情況下,特別是插入與單表查詢,都會微微優于hibernate。不過差異情況并不明顯,可以基本忽略差異。差異比較大的是關聯查詢時,hibernate為了保證POJO的數據完整性,需要將關聯的數據加載,需要額外地查詢更多的數據。這里hibernate并沒有提供相應的靈活性。

關聯時一個差異比較大的地方則是懶加載特性。其中hibernate可以特別地利用POJO完整性來進行緩存,可以在一級與二級緩存上保存對象,如果對單一個對象查詢比較多的話,會有很明顯的性能效益。以后關于單對象關聯時,可以通過懶加載加二級緩存的方式來提升性能。

最后,數據查詢的性能與orm框架關無太大的關系,因為orm主要幫助開發人員將關系數據轉化成對象型數據模型,對代碼的深析上來看,hibernate設計得比較重量級,對開發來說可以算是重新開發了一個數據庫,不讓開發去過多關心數據庫的特性,直接在hibernate基礎上進行開發,執行上分為了sql生成,數據封裝等過程,這里花了大量的時間。

然而myBatis則比直接,主要是做關聯與輸出字段之間的一個映射。其中sql基本是已經寫好,直接做替換則可,不需要像hibernate那樣去動態生成整條sql語句。


好在hibernate在這階段已經優化得比較好,沒有比myBatis在性能上差異太多,但是在開發效率上,可擴展性上相對myBatis來說好太多。最后的最后,關于myBatis緩存,hibernate查詢緩等,后續會再專門做一篇測試。

關于緩存配置

myBatis相對Hibernate 等封裝較為嚴密的ORM 實現而言,因為hibernate對數據對象的操作實現了較為嚴密的封裝,可以保證其作用范圍內的緩存同步,而ibatis 提供的是半封閉的封裝實現,因此對緩存的操作難以做到完全的自動化同步。以上的緩存配置測試僅為性能上的分析,沒有加入可用性上的情況,因為myBatis直接配置緩存的話,可能會出現臟數據。

在關聯查詢數據的情況下,hiberntae的懶加載配二級緩存是個比較好的方案(無臟數據),也是與myBatis相比有比較明顯的優勢。此情景下,性能與myBatis持平。


在真實情況下,myBatis可能不會在這個地方上配置緩存,會出現臟數據的情況,因而很有可能在此hibernate性能會更好。

推薦閱讀

1、Spring Boot+Vue項目實戰

2、B站:4小時上手MyBatis Plus

3、一文搞懂前后端分離

4、快速上手Spring Boot+Vue前后端分離

楠哥簡介

資深 Java 工程師,微信號?southwindss

《Java零基礎實戰》一書作者,今日頭條認證大V

GitChat認證作者,B站認證UP主(楠哥教你學Java)

致力于幫助萬千 Java 學習者持續成長。

23b7a0d09cd56d650cd2b5da38596be7.png

有收獲,就在看5f5336f5b0c29a23ef57f68911865955.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443278.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443278.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443278.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

簡單的cpu飆升排查方法

1先來一段飆升代碼 public class FindJavaThreadInTaskManager {public static void main(String[] args) {Thread thread new Thread(new Worker());thread.start();}static class Worker implements Runnable {Overridepublic void run() {while (true) {System.out.printl…

tortoisesvn創建部署項目_FrameWork如何進行云托管部署

介紹CloudBase Framework 是云開發官方出品的云原生一體化部署工具&#xff0c;可以幫助開發者將靜態網站、后端服務和小程序等應用&#xff0c;一鍵部署到云開發 Serverless 架構的云平臺上&#xff0c;自動伸縮且無需關心運維&#xff0c;聚焦應用本身&#xff0c;無需關心底…

【算法系列之十四】最大子序和

1、題目描述 給定一個整數數組 nums &#xff0c;找到一個具有最大和的連續子數組&#xff08;子數組最少包含一個元素&#xff09;&#xff0c;返回其最大和。 示例: 輸入: [-2,1,-3,4,-1,2,1,-5,4], 輸出: 6 解釋: 連續子數組 [4,-1,2,1] 的和最大&#xff0c;為 6。 2、…

python的代碼復用技術_Python__函數和代碼復用

主要內容函數的定義和使用實例:七段數碼管的繪制代碼復用與函數遞歸PyInstall庫的使用實例&#xff1a;科赫雪花小包裹函數的定義與使用函數的理解與定義函數的使用及調用過程函數的參數傳遞函數的返回值局部變量和全局變量lambda函數------------------------------------函數…

Queue:poll、offer、element、peek的區別

隊列是一種特殊的線性表&#xff0c;它只允許在表的前端&#xff08;front&#xff09;進行刪除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;進行插入操作。進行插入操作的端稱為隊尾&#xff0c;進行刪除操作的端稱為隊頭。隊列中沒有元素時&#xff0c;稱為空隊…

python實現k均值算法_python實現kMeans算法

聚類是一種無監督的學習&#xff0c;將相似的對象放到同一簇中&#xff0c;有點像是全自動分類&#xff0c;簇內的對象越相似&#xff0c;簇間的對象差別越大&#xff0c;則聚類效果越好。1、k均值聚類算法k均值聚類將數據分為k個簇&#xff0c;每個簇通過其質心&#xff0c;即…

mysql給數據量大的表添加索引的辦法

有一個問題&#xff0c;一張表有3百萬條記錄&#xff0c;隨著時間的增加&#xff0c;記錄量會更多&#xff0c;此時查詢速度很慢。在創建此表前沒有未相應字段添加索引&#xff0c;所以此時需要為表添加索引。但是因為數據量大的原因&#xff0c;索引添加不成功&#xff0c;想了…

修改背景圖片_我花了5小時,為網易修改了一份內容超多的PPT,效果超級贊!!...

微信掃碼觀看全套Excel、Word、PPT視頻作者&#xff1a;宋雪賢 來源&#xff1a;PPT進化論(ID&#xff1a;PPTjinhualun)哈嘍&#xff0c;大家好&#xff0c;不知道您看過《我花了3個小時&#xff0c;為京東修改了一份PPT&#xff0c;效果好到驚人&#xff01;》這篇案例修改文…

MySQL千萬級別大表如何優化?

當MySQL單表記錄數過大時&#xff0c;增刪改查性能都會急劇下降&#xff0c;可以參考以下步驟來優化&#xff1a; 單表優化 除非單表數據未來會一直不斷上漲&#xff0c;否則不要一開始就考慮拆分&#xff0c;拆分會帶來邏輯、部署、運維的各種復雜度&#xff0c;一般以整型值…

linux c 調用python_C程序調用Python腳本

一般調用步驟Py_Initialize(); //初始化Python環境PyImport_ImportModule("test"); // 載入python模塊PyObject_GetAttrString(g_pModule,"test1"); //獲得相應Python函數的PyObjectPyObject_CallFunction(test1,"i,s",2,e); //調用Python相應的…

命令測試post_【第2088期】前端中臺化,把格局做大——NodeJS 和測試服務探索

前言今日早讀文章由《React狀態管理與同構實戰》作者LucasHC投稿分享。正文從這開始~~近些年&#xff0c;「NodeJS 應該如何在公司業務中真實落地 」這類問題屢見不鮮。自從 2009 年 NodeJS 誕生之后&#xff0c;搶盡風頭&#xff0c;圈粉無數。但一定有工程師不禁要質疑「Node…

Go類型轉換

由于Go語言不存在隱式類型轉換&#xff0c;因此所有的類型轉換都必須顯式的聲明。 string、int、float類型相互轉換 string轉其他 string轉成int&#xff1a; int, err : strconv.Atoi(string) string轉成int64&#xff1a; // 參數1&#xff1a;帶轉換字符串&#xff0c;/…

linux tee 重定向_快樂的linux命令行-重定向

整理自《快樂的linux命令行一書》。linux系統版本&#xff1a; Ubuntu 17.04本章&#xff0c;我們將介紹命令行最酷的特性&#xff0c;叫做I/O重定向&#xff0c;通過這個工具&#xff0c;可以重定向命令的輸入輸出&#xff0c;命令的輸入來自文件&#xff0c;而輸出也存到文。…

Java 診斷工具 Arthas 常見命令

基本概念 云原生這么多微服務&#xff0c;當然需要一個診斷利器來排查問題。 Arthas 是阿里開源的 Java 診斷工具&#xff0c;深受開發者喜愛。在線排查問題&#xff0c;無需重啟&#xff1b;動態跟蹤 Java 代碼&#xff1b;實時監控 JVM 狀態。Arthas 支持 JDK 6&#xff0c…

28和lba48命令格式區別_編譯Sass(命令行)

本文作者&#xff1a;開課吧無憂圖文編輯&#xff1a;開三金sass編譯有很多種方式&#xff0c;如命令行編譯模式、編輯器自動編譯、編譯軟件koala、sass-loader等。今天我們就先來看第一種&#xff1a;命令行編譯剛才我在test文件夾里面已經建立了一個style.scss文件&#xff0…

JAVA基礎編程代碼50個

【程序1】 題目&#xff1a;古典問題&#xff1a;有一對兔子&#xff0c;從出生后第3個月起每個月都生一對兔子&#xff0c;小兔子長到第三個月后每個月又生一對兔子&#xff0c;假如兔子都不死&#xff0c;問每個月的兔子對數為多少&#xff1f; 程序分析&#xff1a; 兔子…

爬蟲軟件python功能_Python 網絡爬蟲程序詳解

#!/usr/bin/python #調用pythonfrom sys import argv #導入sys是導入python解釋器和他環境相關的參數from os import makedirs,unlink,sep  #os主要提供對系統路徑&#xff0c;文件重命名和刪除文件所需的函數#makedirs是創建遞歸文件夾的函數。#比如說我們要創建一個新的目錄…

價錢轉換python_如何在python中轉換貨幣?

我正在做一個虛擬助手項目。我想讓它告訴我其他貨幣的美元匯率。我用beauthoulsoup編寫了以下代碼&#xff0c;它從給定的網站獲取數據&#xff0c;對其進行解析并在命令行中打印結果供我閱讀。但這只是美元對巴基斯坦盧比。如何修改程序&#xff0c;使其接受任何貨幣并告訴我該…

char qt 轉unicode_Qt QString 中文 char* UTF-8 QByteArray QTextCodec unicode gb2312 GBK 亂碼與轉碼問題...

2012-03-22 14:00175人閱讀評論(0)代碼如下&#xff1a;如果不不設全局的字符集是utf-8&#xff0c;那么網上一般的方法是可以轉的。如下程序中 #define DD 1的情況下&#xff1b;但是如果設置了全局的utf-8&#xff0c;再用以前的方法&#xff1a;QByteArraybaaaa.toLatin1();…

計算機圖形學考試題及答案_計算機圖形學考試題及答案

3、在圖形文件中&#xff0c;常用來描述圖形元素(點&#xff0c;線&#xff0c;圓&#xff0c;弧等)&#xff1b;而在光柵掃描圖形顯示器中&#xff0c;采用顯示所有圖形。4、當三維物體用透視變換方程投影到觀察平面上&#xff0c;物體中不與觀察平面平行任一簇平行線投影成收…