Hibernate映射集合性能问题

首先,这篇文章的灵感来自于Burt Beckwith在2011年1月27日于SpringOne 2GX上发表的有关高级GORM –性能,定制和监控的演讲 。 简而言之, Burt Beckwith讨论了使用映射集合和GORM中的Hibernate 2级缓存的潜在性能问题,以及避免此类性能下降的策略。

尽管如此, Burt Beckwith在演讲中指出的有关映射集合的性能问题通常适用于每个启用Hibernate的应用程序。 这就是为什么在观看他的演示文稿后,我才意识到他的提议正是我自己一直在做的事情,并指示我的同事在使用Hibernate中的 映射集合进行开发时应该做。

以下是使用Hibernate 映射集合时要考虑的5件事:

让我们考虑以下经典的“图书馆–访问”示例:

以下Library类具有Visit实例的集合:

package eg;
import java.util.Set;public class Library {private long id;private Set visits;public long getId() { return id; }private void setId(long id) { this.id=id; }private Set getVisits() { return visits; }private void setVisits(Set visits) { this.visits=visits; }........
}

以下是Visit类:

package eg;
import java.util.Set;public class Visit {private long id;private String personName;public long getId() { return id; }private void setId(long id) { this.id=id; }private String getPersonName() { return personName; }private void setPersonName(String personName) { this.personName=personName; }........
}

假设一个库有多个唯一的访问,并且每个访问都与一个不同的库相关联,则可以使用单向 的一对多关联,如下所示:

<hibernate-mapping><class name="Library"><id name="id"><generator class="sequence"/></id><set name="visits"><key column="library_id" not-null="true"/><one-to-many class="Visit"/></set></class><class name="Visit"><id name="id"><generator class="sequence"/></id><property name="personName"/></class></hibernate-mapping>

我还将提供上述模式的表定义示例:

create table library (id bigint not null primary key )
create table visit(id bigint not nullprimary key,personName varchar(255),library_id bigint not null)
alter table visit add constraint visitfk0 (library_id) references library

那么这张照片怎么了?

当您尝试添加到映射的集合时,可能会出现性能瓶颈。 如您所见,集合被实现为Set 。 集合保证其所包含元素之间的唯一性。 那么, Hibernate如何知道一个新项目是唯一的以便将其添加到Set中呢? 好吧,不要惊讶; 添加到Set中需要从数据库加载所有可用项。 Hibernate将每一个都与新的进行比较,以确保唯一性。 此外,以上是我们无法绕过的标准行为,即使我们由于业务规则而知道新项目是唯一的!

在映射集合中使用List实现也无法解决向其中添加项目时的性能瓶颈问题。 尽管列表不保证唯一性,但它们可以保证项目的顺序。 因此,为了在映射的List中保持正确的项目顺序,即使我们要添加到列表的末尾, Hibernate也必须提取整个集合。

我认为,添加一个新的访问 图书馆是很长的路要走,您不同意吗?

此外,上面的示例在开发中非常有效,我们只有很少的访问。 在每个库可能有数百万访问量的生产环境中,请想象一下当您尝试再添加一个时会降低性能!

为了克服上述性能问题,我们可以将集合映射为Bag ,这只是一个常规集合,没有顺序或唯一性保证,但是在这样做之前,请考虑下面的最后一点。

当您从集合中删除对象或向集合中添加对象时,集合所有者的版本号会增加。 因此,当同时进行访问创建时,在Library对象上存在人为的乐观锁定异常的高风险。 我们将乐观的锁定异常描述为“人为的”,因为它们发生在集合所有者对象( Library )上,当我们从Visits集合中添加/删除项目时,我们不认为自己正在编辑(但实际上是!)。

我要指出的是,相同的规则适用于多对多关联类型。

那么解决方案是什么?

解决方案很简单,从所有者( Library )对象中删除映射的集合 ,然后“手动”执行Visit项目的插入和删除。 提议的解决方案通过以下方式影响使用:

  1. 要将访问添加到库中,我们必须创建一个新的“ 访问”项,将其与“ 库”项相关联,并将其显式保存在数据库中。
  2. 要从图书馆中删除访问 ,我们必须搜索“访问”表,找到我们需要的确切记录并将其删除。
  3. 使用建议的解决方案,不支持级联。 要删除资料库,您需要先删除(取消关联)其所有访问记录。

为了保持环境整洁有序,您可以通过实现一个助手方法将“访问”伪集合恢复到Library对象,该方法将查询数据库并返回与特定Library关联的所有Visit对象。 此外,您可以在Visit项目中实现几个帮助程序方法,这些方法将执行实际的访问记录插入和删除操作。

下面,我们提供Library类, Visit类和Hibernate映射的更新版本,以便符合我们提出的解决方案:

首先更新的类:

package eg;
import java.util.Set;public class Library {private long id;public long getId() { return id; }private void setId(long id) { this.id=id; }public Set getVisits() { // TODO : return select * from visit where visit.library_id=this.id}........
}

如您所见,我们删除了映射的集合,并引入了方法“ getVisits() ”,该方法应用于返回特定Library实例的所有Visit项目(TODO注释为伪代码)。

以下是更新的Visit类:

package eg;
import java.util.Set;public class Visit {private long id;private String personName;private long library_id;public long getId() { return id; }private void setId(long id) { this.id=id; }private String getPersonName() { return personName; }private void setPersonName(String personName) { this.personName=personName; }private long getLibrary_id() { return library_id; }private void setLibrary_id(long library_id) { this. library_id =library_id; }........
}

如您所见,我们已经在Visit对象中添加了“ library_id ”字段,以便能够将其与Library项目相关联。

最后是更新的Hibernate映射:

<hibernate-mapping><class name="Library"><id name="id"><generator class="sequence"/></id></class><class name="Visit"><id name="id"><generator class="sequence"/></id><property name="personName"/><property name="library_id"/></class></hibernate-mapping>

因此,永远不要在Hibernate中使用映射的集合吗?

好吧,说实话,不。您需要检查每个案例,以便决定要做什么。 如果收集的数量较小,则标准方法很好-在多对多关联方案的情况下,双方都是如此。 此外,集合将包含代理,因此在初始化之前,它们将小于实际实例。

编码愉快! 别忘了分享!

贾斯汀

聚苯乙烯

在TheServerSide上对这篇文章进行了相当长的辩论之后,一个或我们的读者Eb Bras提供了一个有用的Hibernate“技巧和窍门”列表,让他看看该说些什么:

这是我长期记录的一些Hibernate提示和技巧:

反=“真”
一对多的父子关联(与另一个实体或用作一个实体的值类型)中尽可能多地使用它。
该属性在集合标签(如“ set”)上设置,表示多对一拥有关联,并负责所有数据库的插入/更新/删除。 它使关联成为孩子的一部分。 它将保存外键的数据库更新,因为它将在插入子代时直接发生。

尤其是在使用“集合”作为映射类型时,它可以提高性能,因为不需要将子级添加到父级集合中,这样可以节省整个集合的负载。 那就是:由于集合映射的性质,添加新子元素时必须始终加载整个集合,因为这是hibernate可以确保新条目不是重复项的唯一方法,这是JRE Set的功能接口。
如果它涉及一个组件集合(=仅包含纯值类型的集合),则inverse = true会被忽略并且没有意义,因为Hibernate对对象具有完全控制权,并将选择执行其操作的最佳方法。
如果它涉及分离的DTO对象(不包含任何休眠对象),则休眠将删除所有值类型子对象,然后插入它们,因为它不知道哪个对象是新对象或存在对象,因为它已完全分离。 Hibernate将其视为新集合。

懒惰的Set.getChilds()是邪恶的
使用getChilds()会返回一个Set并会延迟加载所有子项,请小心。
当您只想添加或删除孩子时,请勿使用此功能

始终实现equals / hashcode
确保始终对Hibernate管理的每个对象实施equals / hashcode,即使它看起来并不重要。 对于值类型对象也是如此。 如果对象不包含作为equals / hashcode候选者的属性,请使用代理密钥,例如,由UUID组成。 Hibernate使用equals / hashcode找出数据库中是否已存在对象。 如果它涉及到一个现有的对象,但是Hibernate认为它是一个新对象,因为equals / hashcode没有正确实现,则Hibernate将执行插入操作,并可能删除旧值。 特别是对于Set中的值类型而言,这一点很重要,必须进行测试,因为它可以节省数据库流量。 想法:您正在向Hibernate提供更多知识,以便可以使用它来优化他的操作。

使用版本
始终将version属性与实体或用作实体的值类型一起使用。
由于Hibernate使用此信息来发现它是否涉及新对象或现有对象,因此这将减少数据库流量。 如果不存在此属性,则必须命中数据库以查找它是否涉及新对象或现有对象。

渴望获取
默认情况下,非延迟集合(子项)是通过额外选择查询加载的,该查询仅在从数据库加载父项之后才执行。
通过启用热切获取,可以通过加载集合映射标签上的属性“ fetch = join”来完成与加载父对象相同的查询。 如果启用,则通过左外部联接加载子项。 测试这是否可以提高性能。 如果发生许多联接,或者如果它涉及具有许多列的表,则性能将变差而不是变好。

在值类型子对象中使用代理键
Hibernate将在由所有非空列组成的父子关系的值类型子项中构造主键。 这可能会导致奇怪的主键组合,尤其是在涉及日期列时。 日期列不应该是主键的一部分,因为它的毫秒部分将导致几乎绝不相同的主键。 这将导致奇怪的数据库性能,并且可能导致性能下降。 为了改善这一点,我们在所有子值类型对象中使用代理键,这是唯一的非null属性。 然后,Hibernate将构造一个由外键和代理键组成的主键,该主键是逻辑上的且性能良好。 请注意,代理键仅用于数据库优化,不需要在可能包含业务逻辑的equals / hashcode中使用。

相关文章 :
  • Java最佳实践–高性能序列化
  • Java最佳实践– Vector vs ArrayList vs HashSet
  • Java最佳实践–字符串性能和精确字符串匹配
  • Java最佳实践–队列之战和链接的ConcurrentHashMap
  • Java最佳实践– Char到Byte和Byte到Char的转换
  • 如何在不到1ms的延迟内完成100K TPS
  • 提升您的休眠引擎
  • Cajo,用Java完成分布式计算的最简单方法

翻译自: https://www.javacodegeeks.com/2011/02/hibernate-mapped-collections.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法:1!+(1!+3!)+(1!+3!+5!) + ( 1! + 3! + 5! + 7! + 9!)+....+(1!+3!+5!+ ... + m!)...

-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{ //算法入口 [self func2:9]; } //计算阶乘 factor&#xff08;m&#xff09;&#xff1d; m&#xff01; -(int)factor:(int)m{ int factorNum0; if(m0|m1) return 1; e…

知道这 20 个正则表达式,能让你少写 1,000 行代码

CocoaChina05-13正则表达式&#xff0c;一个十分古老而又强大的文本处理工具&#xff0c;仅仅用一段非常简短的表达式语句&#xff0c;便能够快速实现一个非常复杂的业务逻辑。熟练地掌握正则表达式的话&#xff0c;能够使你的开发效率得到极大的提升。下面是技匠整理的&#x…

用Jackson进行Java JSON处理

JSON &#xff08;Javascript对象符号&#xff09;正成为一种非常流行的数据交换格式。 在使用诸如YUI&#xff0c;ExtJS&#xff0c;DOJO等Javascript框架开发Web应用程序时&#xff0c;我们可以使用XML或JSON在客户端和服务器之间交换数据。 通常&#xff0c;我们从服务器获得…

Android Togglebutton 默认背景被放大

1 . 最近在项目中自定义toggleButton 发现背景图放入后 &#xff0c;比美工给我的原图要大很大 2. 为什么&#xff1f; 3.比较ToggleButton 和TextView源码 发现, toggleButton 设置了minWidth 和minHeigh &#xff0c;导致我的背景小图被拉伸。 4.解决这种问题&#xff1a; 重…

MapReduce:简单介绍

MapReduce是Google流行的一种并行编程技术。 它用于处理大量数据。 仅通过将工作并行分配给多台机器&#xff0c;就可以在合理的时间内完成这种处理。 每台机器都处理一小部分数据。 MapReduce是一种编程模型&#xff0c;使开发人员可以专注于编写处理数据的代码&#xff0c;而…

python翻译词典实例

#!/usr/bin/python # -*- coding:utf-8 -*- #通过有道翻译来进行内容翻译 import urllib2 import urllib import json #---------翻译方法定义 start---------# def transfer( transferStr , lanSource auto ): data {type:lanSource,i:transferStr,doctype:json,xmlVersion:1…

元素分类--块级元素(特点:独占一行, 宽高边距可改)

什么是块级元素&#xff1f;在html中<div>、 <p>、<h1>、<form>、<ul> 和 <li>就是块级元素。设置display:block就是将元素显示为块级元素。如下代码就是将内联元素a转换为块状元素&#xff0c;从而使a元素具有块状元素特点。 a{display:b…

Java应用程序中的消息传递主体

消息传递是每个Java应用程序的关键方面&#xff0c;尤其是对于涉及企业应用程序集成&#xff08;EAI&#xff09;或关注点分离的应用程序&#xff0c;例如多层WEB应用程序。 消息传递可以分为两个主要类别&#xff0c;即同步和异步。 另一方面&#xff0c;使用同步消息传递时&…

站立会议05(第二次冲刺)

一、站立会议信息&#xff08;配站立会议照片&#xff09; 第五天我们继续开发&#xff0c;把注册验证信息完善一下&#xff0c;将开始网站公共主页的开发。 二、任务进度 第五天我们注册验证完成。 三、任务看板&#xff08;图&#xff09; 四、燃尽图&#xff08;图&#xff…

[SoapUI] DataSource, DataSourceLoop, DataSink

Script assertion in login: 转载于:https://www.cnblogs.com/MasterMonkInTemple/p/4748189.html

1154. 一年中的第几天

给你一个字符串 date &#xff0c;按 YYYY-MM-DD 格式表示一个 现行公元纪年法 日期。请你计算并返回该日期是当年的第几天。 通常情况下&#xff0c;我们认为 1 月 1 日是每年的第 1 天&#xff0c;1 月 2 日是每年的第 2 天&#xff0c;依此类推。每个月的天数与现行公元纪年…

将CAPTCHA添加到您的GWT应用程序

什么是验证码&#xff1f; 在一个充满恶意机器人的世界中&#xff0c;您该怎么做才能保护您宝贵的Web应用程序&#xff1f; 您真正应该做的基本事情之一就是向其中添加CAPTCHA功能。 如果您不熟悉&#xff08;听起来有些奇怪&#xff09;&#xff0c;则CAPTCHA是确保用户实际上…

SQL基础语句

数据库面试常见题 一、SQL语言包括数据定义语言、数据操作语言、数据控制语言和事务控制语言1&#xff1a;DDL(Data Definition Language)&#xff0c;是用于描述数据库中要存储的现实世界实体的语言。 CREATE TABLE - 创建新表 ALTER TABLE - 变更&#xff08;改变&#xff0…

YYModel Summary

YYModel Effect-> YYModel的作用Provide some data-model method—>提供一些数据模型的方法Convert json to any object, or convert any object to json.->对任何对象转换成JSON&#xff0c;和对任何JSON转换为对象Set object properties with a key-value dictionar…

iOS学习——ScrollView图片轮播和同类控件优先级问题

iOS学习——ScrollView的使用和同类控件优先级问题 1. 布置界面 ScrollView的使用非常简单&#xff0c;只有三步 1.1 添加一个scrollview 1.2 向scrollview添加内容 1.3 告诉scrollview中内容的实际大小 首先做第一步&#xff0c;布置界面。 拖拽一个scrollview就可以了 就…

Exchanger和无GC的Java

总览 Exchanger类在线程之间传递工作和回收使用的对象方面非常有效。 AFAIK&#xff0c;它也是最少使用的并发类之一。 但是&#xff0c;如果您不需要GC&#xff0c;则使用ArrayBlockingQueue进行日志记录会更简单。 交换器类 Exchanger类对于在两个线程之间来回传递数据很有…

构造函数的反射

1 import java.lang.reflect.Constructor;2 3 public class zzbds {4 public static void main(String[] args) {5 6 try{ 7 Class cStudent.class; //获得无参构造函数8 Constructor constructorc.getConstructor(new Class[]{…

字符串连接“+”int、char、string

String s1 "21" "8" "54";System.out.println(s1);String s2 "21" 8 "54";System.out.println(s2);String s3 "21" 8 "54";System.out.println(s3);21854 21854 21854

使用Spring使用Java发送电子邮件– GMail SMTP服务器示例

对于使用Java发送电子邮件&#xff0c; JavaMail API是标准解决方案。 如官方网页所述&#xff0c;“ JavaMail API提供了独立于平台和协议的框架来构建邮件和消息传递应用程序”。 必需的类包含在JavaEE平台中&#xff0c;但是要在独立的JavaSE应用程序中使用它&#xff0c;您…

Java字符与数字的计算

先看例子&#xff1a; char ch;int x;int y 7;System.out.print("7的ASCII码值是&#xff1a;");System.out.println(y);ch 7 2;System.out.print("7 2的char型&#xff1a;");System.out.println(ch);x 7 2;System.out.print("7 2的int型&…