文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

说实话,Hibernate和MyBatis哪个更好用?

2024-12-03 16:10

关注

前言

由于编程思想与数据库的设计模式不同,生出了一些ORM框架。核心都是将关系型数据库和数据转成对象型。当前流行的方案有Hibernate与myBatis。两者各有优劣。竞争激烈,其中一个比较重要的考虑的地方就是性能。因此笔者通过各种实验,测出两个在相同情景下的性能相关的指数,供大家参考。

测试目标

以下测试需要确定几点内容:

性能差异的场景;

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

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

测试思路

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

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

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

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

测试提纲

具体的场景情况下

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

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

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

查询测试3:100万*50万关联数据中查询100000次,两者输出相同字段。

准备

数据库:mysql 5.6

表格设计:

twitter:推特 

  1. CREATE TABLE `twitter` (  
  2.   `id` bigint(20) NOT NULL AUTO_INCREMENT, 
  3.   `add_date` datetime DEFAULT NULL,  
  4.   `modify_date` datetime DEFAULT NULL,  
  5.   `ctx` varchar(255) NOT NULL,  
  6.   `add_user_id` bigint(20) DEFAULT NULL,  
  7.   `modify_user_id` bigint(20) DEFAULT NULL,  
  8.   PRIMARY KEY (`id`),  
  9.   KEY `UPDATE_USER_FORI` (`modify_user_id`), 
  10.   KEY `ADD_USER_FORI` (`add_user_id`), 
  11.   CONSTRAINT `ADD_USER_FORI` FOREIGN KEY (`add_user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL,  
  12.   CONSTRAINT `UPDATE_USER_FORI` FOREIGN KEY (`modify_user_id`) REFERENCES `user` (`id`) ON DELETE SET NULL  
  13. ENGINE=InnoDB AUTO_INCREMENT=1048561 DEFAULT CHARSET=utf8 

user: 用户 

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

测试数据准备:

表一:twitter

无数据。

表二:user

50万个随机的用户名。

随机内容推特表(material_twitter)

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

用于插入控推特表。

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

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

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

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

实体代码 

  1. @Entity  
  2. @Table(name = "twitter" 
  3. public class Twitter implements java.io.Serializable{  
  4.   private Long id;  
  5.   private Date add_date;  
  6.   private Date modify_date;  
  7.   private String ctx;  
  8.   private User add_user;  
  9.   private User modify_user; 
  10.   private String createUserName; 
  11.   @Id  
  12.   @GeneratedValue(strategy = IDENTITY 
  13.   @Column(name = "id"unique = truenullable = false 
  14.   public Long getId() {  
  15.     return id;  
  16.   }  
  17.   public void setId(Long id) {  
  18.     this.id = id;  
  19.   }  
  20.   @Temporal(TemporalType.DATE)  
  21.   @Column(name = "add_date" 
  22.   public Date getAddDate() {  
  23.     return add_date;  
  24.   }  
  25.   public void setAddDate(Date add_date) {  
  26.     this.add_date = add_date;  
  27.   }  
  28.   @Temporal(TemporalType.DATE)  
  29.   @Column(name = "modify_date" 
  30.   public Date getModifyDate() {  
  31.     return modify_date;  
  32.   }  
  33.   public void setModifyDate(Date modify_date) {  
  34.     this.modify_date = modify_date;  
  35.   }  
  36.   @Column(name = "ctx" 
  37.   public String getCtx() {  
  38.     return ctx;  
  39.   }  
  40.   public void setCtx(String ctx) {  
  41.     this.ctx = ctx;  
  42.   }  
  43.   @ManyToOne(fetch = FetchType.LAZY)  
  44.   @JoinColumn(name = "add_user_id" 
  45.   public User getAddUser() { 
  46.      return add_user; 
  47.   }  
  48.   public void setAddUser(User add_user) {  
  49.     this.add_user = add_user;  
  50.   }  
  51.   @ManyToOne(fetch = FetchType.LAZY)  
  52.   @JoinColumn(name = "modify_user_id" 
  53.   public User getModifyUser() {  
  54.     return modify_user;  
  55.   }  
  56.   public void setModifyUser(User modify_user) {  
  57.     this.modify_user = modify_user;  
  58.   } 
  59.   @Transient  
  60.   public String getCreateUserName() {  
  61.     return createUserName;  
  62.   }  
  63.   public void setCreateUserName(String createUserName) {  
  64.     this.createUserName = createUserName;  
  65.   }   

开始

插入测试1

代码操作:

将随机内容推特表的数据加载到内存中,然后一条条加入到推特表中,共10万条。

关键代码:

hibernate: 

  1. Session session = factory.openSession();  
  2.     session.beginTransaction();  
  3.     Twitter t = null 
  4.     Date now = new Date();  
  5.     for(String materialTwitter : materialTwitters){  
  6. // System.out.println("materialTwitter="+materialTwitter);  
  7.            t = new Twitter();  
  8.         t.setCtx(materialTwitter);  
  9.            t.setAddDate(now);  
  10.            t.setModifyDate(now);  
  11.            t.setAddUser(null);  
  12.            t.setModifyUser(null);  
  13.            session.save(t);  
  14.       } 
  15.      session.getTransaction().commit(); 

mybatis: 

  1. Twitter t = null 
  2.     Date now = new Date();  
  3.     for(String materialTwitter : materialTwitters){  
  4. // System.out.println("materialTwitter="+materialTwitter);  
  5.            t = new Twitter();  
  6.            t.setCtx(materialTwitter);  
  7.            t.setAddDate(now);  
  8.            t.setModifyDate(now);  
  9.            t.setAddUser(null);  
  10.            t.setModifyUser(null);  
  11.            msession.insert("insertTwitter", t);  
  12.       }  
  13.     msession.commit(); 

TwitterMapper.xml,插入代码片段: 

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

查询测试1

通过id从1递增到10万依次进行查询推特内容,仅输出微博内容。

关键代码:

hibernate: 

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

mybatis: 

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

查询测试2

与查询测试1总体一样,增加微博的创建人名称字段,此处需要关联。

其中微博对应有10万个用户。可能一部份用户重复。这里对应的用户数可能与hibernate配懒加载的情况有影响。

此处体现了hibernate的一个方便处,可以直接通过getAddUser()可以取得user相关的字段。

然而myBatis则需要编写新的vo,因此在测试batis时则直接在Twitter实体中增加创建人员名字成员(createUserName)。

此处hibernate则会分别测试有懒加载,无懒加载。

mybatis会测有默认与有缓存两者情况。

其中mybatis的缓存机制比较难有效配置,不适用于真实业务(可能会有脏数据),在此仅供参考。

测试时,对推特关联的用户数做了两种情况,一种是推特共关联了100个用户,也就是不同的推特也就是在100个用户内,这里的关联关系随机生成。

另外一种是推特共关联了50万个用户,基本上50个用户的信息都会被查询出来。

在上文“准备”中可以看到关联数据生成方式。

关键代码:

hibernate: 

  1. long cnt = 100000 
  2. for(long i = 1; i <= cnt; ++i){  
  3.       Twitter t = (Twitter)session.get(Twitter.class, i);  
  4.       t.getAddUser().getName();//加载相应字段  
  5.       //System.out.println("t.getCtx="+ t.getCtx() + " t.getUser.getName=" + t.getAddUser().getName());  
  6.     } 

急懒加载配置更改处,Twitter.java: 

  1. @ManyToOne(fetch = FetchType.EAGER)//急加载  
  2.       //@ManyToOne(fetch = FetchType.LAZY)//懒加载  
  3.   @JoinColumn(name = "add_user_id" 
  4.   public User getAddUser() {  
  5.     return add_user;  
  6.   } 

mybatis: 

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

TwitterMapper.xml配置: 

  1. <select id="getTwitterHasUser" parameterType="long"   
  2.          resultType="org.pushio.test.show1.entity.Twitter">  
  3.          select twitter.*,user.name as creteUserName from twitter,user  
  4.          where twitter.id=#{id}  
  5.            AND twitter.add_user_id=user.id   
  6.      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编程内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯