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.mzph.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;物体中不与观察平面平行任一簇平行线投影成收…