百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

性能测评:Mybatis和Hibernate(mybatis和hibernate性能对比)

bigegpt 2024-08-03 11:48 2 浏览

前言

由于编程思想与数据库的设计模式不同,生出了一些ORM框架。

核心都是将关系型数据库和数据转成对象型。当前流行的方案有Hibernate与myBatis。

两者各有优劣。竞争激烈,其中一个比较重要的考虑的地方就是性能。

接下将展示相同情景下测出的两组数据数据供大家参考。

测试目标

测试需要确定几点内容:

性能差异的场景;

性能不在同场景下差异比;

找出各架框优劣,各种情况下的表现,适用场景。

测试思路

测试总体分成:单表插入,关联插入,单表查询,多表查询。

测试分两轮,同场景下默认参数做一轮,调优做强一轮,横纵对比分析了。

测试中尽保证输入输出的一致性。

样本量尽可能大,达到10万级别以上,减少统计误差。

测试提纲

具体的场景情况下

插入测试1:10万条记录插入。

查询测试1:100万数据中单表通过id查询100000次,无关联字段。

查询测试2:100万数据中单表通过id查询100000次,输出关联对象字段。

查询测试3:100万*50万关联数据中查询100000次,两者输出相同字段。文章取自芋道源码.

准备

数据库:mysql 5.6

表格设计:

twitter:推特

CREATETABLE`twitter` (
`id`bigint(20) NOTNULL AUTO_INCREMENT,
`add_date` datetime DEFAULTNULL,
`modify_date` datetime DEFAULTNULL,
`ctx`varchar(255) NOTNULL,
`add_user_id`bigint(20) DEFAULTNULL,
`modify_user_id`bigint(20) DEFAULTNULL,
 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`) ONDELETESETNULL,
CONSTRAINT`UPDATE_USER_FORI` FOREIGN KEY (`modify_user_id`) REFERENCES`user` (`id`) ONDELETESETNULL
) ENGINE=InnoDB AUTO_INCREMENT=1048561DEFAULTCHARSET=utf8

user: 用户

CREATETABLE`user` (
`id`bigint(20) NOTNULL AUTO_INCREMENT,
`name`varchar(255) DEFAULTNULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=524281DEFAULTCHARSET=utf8

测试数据准备:

表一:twitter

无数据。

表二:user

50万个随机的用户名。

随机内容推特表(material_twitter)

无id,仅有随机字符串内容,共10万条。

用于插入控推特表。

生成数据代码,关联100个用户:

insertinto twitter(ctx,add_user_id,modify_user_id,add_date,modify_date)
SELECTname,ROUND(RAND()*100)+1,ROUND(RAND()*100)+1,'2016-12-31','2016-12-31'
from MATERIAL

生成数据代码,关联500000个用户:

insertinto twitter(ctx,add_user_id,modify_user_id,add_date,modify_date)
SELECTname,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,插入代码片段:

<insertid="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.id
 </select>

测试结果

测试分析

测试分成了插入,单表查询,关联查询。关联查询中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性能会更好。


  • 私信作者“java”或者”资料“无偿获取更多技术资料以及bat厂面试题集。

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...