网站开发服务费分录,电子商务有限公司官网,wordpress精品插件,濮阳网站建设熊掌号首先#xff0c;这篇文章的灵感来自于Burt Beckwith在2011年1月27日于SpringOne 2GX上发表的有关高级GORM –性能#xff0c;定制和监控的演讲 。 简而言之#xff0c; Burt Beckwith讨论了使用映射集合和GORM中的Hibernate 2级缓存的潜在性能问题#xff0c;以及避免此类性… 首先这篇文章的灵感来自于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.idid; }private Set getVisits() { return visits; }private void setVisits(Set visits) { this.visitsvisits; }........
} 以下是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.idid; }private String getPersonName() { return personName; }private void setPersonName(String personName) { this.personNamepersonName; }........
} 假设一个库有多个唯一的访问并且每个访问都与一个不同的库相关联则可以使用单向 的一对多关联如下所示 hibernate-mappingclass nameLibraryid nameidgenerator classsequence//idset namevisitskey columnlibrary_id not-nulltrue/one-to-many classVisit//set/classclass nameVisitid nameidgenerator classsequence//idproperty namepersonName//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项目的插入和删除。 提议的解决方案通过以下方式影响使用 要将访问添加到库中我们必须创建一个新的“ 访问”项将其与“ 库”项相关联并将其显式保存在数据库中。 要从图书馆中删除访问 我们必须搜索“访问”表找到我们需要的确切记录并将其删除。 使用建议的解决方案不支持级联。 要删除资料库您需要先删除取消关联其所有访问记录。 为了保持环境整洁有序您可以通过实现一个助手方法将“访问”伪集合恢复到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.idid; }public Set getVisits() { // TODO : return select * from visit where visit.library_idthis.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.idid; }private String getPersonName() { return personName; }private void setPersonName(String personName) { this.personNamepersonName; }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-mappingclass nameLibraryid nameidgenerator classsequence//id/classclass nameVisitid nameidgenerator classsequence//idproperty namepersonName/property namelibrary_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