默认情况下都是懒惰jar

Hibernate(实际上是JPA)具有集合映射:@OneToMany、@ManyToMany、@ElementCollection。所有这些在默认情况下都是懒惰的。这意味着集合是列表或SET接口的特定实现,它们保存对持久会话的引用,并且只有在访问集合时才从数据库加载值。如果只是偶尔使用集合,则可以节省不必要的数据库查询。

然而,这有一个问题。在我的观察中,这个问题是第二常见的异常(仅次于NullPointerException)--LazyInitializationException。问题是会话通常是为您的服务层打开的,并且在您将实体返回到视图层时立即关闭。当您试图在视图中迭代未初始化的集合(例如jsp)时,集合会抛出LazyInitializationException,因为它们持有引用的会话已经关闭,无法获取项。

这是怎么解决的?所谓的OpenSessionInView/OpenEntityManagerInView“模式”。简而言之:创建一个筛选器,在请求启动时打开会话,并在呈现视图后(而不是在服务层完成后)关闭会话。有些人称之为反模式,因为它会将持久化处理泄漏到视图层,并使设置复杂化。我不会说它有那么糟糕:一般来说,它解决了问题而没有引入其他问题。但是在我最近参与的所有项目中,我们并没有使用OpenSessionInView,而且它运行得很好。

它工作得很好,因为我们没有使用惰性集合。但是,您将正确地指出,当您加载一个实体时,您将获取“整个世界”。好吧,不。有两种类型的*Tomany映射:

  • 集合逻辑上不包含十几个元素的值类型映射。在大多数情况下@ElementCollection,而且@*ToMany对于“类别”或“价格”这样的项目,它们只是更复杂的值对象,但它们本身不包含任何其他映射。这些类型集合的另一个共同特性是,它们通常与其所属实体一起显示在UI中。例如,您很可能希望显示文章的类别。对于这种类型的集合,急切是更好的选择。无论如何,您将不得不获取它们,为什么不让Hibernate(或任何JPA实现)考虑一些聪明的连接呢?就像我说过的--从逻辑上说,收藏品的数量不超过一打或二十件,因此获取它们不会是一种性能上的冲击。而且,从逻辑上讲,他们不会用它获取一个大的对象图。
  • 跨大型核心实体的映射。这可以是“用户发出的所有订单”或“组织的所有用户”、“供应商的所有项目”等等。因为如果您为一个组织获取了2000名用户,而该组织的每个订单都有1000份订单,而一份订单平均有3项,而这些项目又收集了所有购买该订单的人。你最终会把你的整个数据库放在内存里。很明显你需要懒散的收藏,对吧?好吧,不。在这种情况下,您根本不应该使用集合映射。在99%的情况下,这些类型的关系都显示在UI中的分页列表中。或者搜索结果。它们永远不会(也不应该)全部显示在一个屏幕上(或者,如果应用程序提供了类似RESTAPI的东西,那么很少应该在一个API调用中返回)。您必须对它们进行查询,并使用query.setMaxResultsquery.setFirstResult()(或用一些限制性标准对其加以限制)。此外,映射集合意味着有人会在某个时候尝试使用它们,这可能会失败。如果对象是序列化的(XML、json等)将获取集合内容。你几乎肯定不想发生的事。(这里有一个草案:JPA可以有一个PagedList集合,该集合允许分页的延迟抓取,从而消除了查询的需要)

所以我刚才说了什么-你不应该使用懒惰的集合。对于非常简单的浅层映射使用热切的集合,对于较大的映射使用分页查询。

https://movie.douban.com/review/13925103/

不完全是。惰性集合是存在的,它们有应用程序,尽管它是相当有限的。或者,至少它们远不如它们所使用的那样适用。下面是一个我认为适用的示例场景。在我的我有一个消息实体,它包含一个图片实体集合。当用户上传图片时,图片存储在该集合中。一条消息不可能有超过10张图片,所以收集很可能是非常迫切的。但是,消息是最常用的实体--它实际上是在每个请求中获取的。但是只有一些消息有图片(您的流中有多少条推特有图片上传?)因此,我不希望Hibernate进行查询,只是为了找出给定消息没有图片。因此,我将图片的数量存储在单独的字段中,使图片集合变懒,Hibernate.initialization(..)只有当图片数>0时,才手动进行。

所以有一些场景,当实体有任选属于上述第一类的集合(“小而浅的集合”)。因此,如果它是小的、浅的和可选的(例如,在不到20%的情况下使用),那么您应该使用Lazy来保存不必要的查询。

对于其他一切--懒惰的收藏会让你的生活更加艰难。

​​​​​​https://m.douban.com/movie/review/13925103/

别担心因为那些人错了。当然,并不是完全错误,因为getter和setter可以破坏封装,但是在常规业务项目的通常情况下,它们不会。封装的目的是什么?首先,隐藏对象如何准确地执行其任务。并保护对象的内部数据,使任何外部对象都不能侵犯其状态空间。换句话说,只有对象知道字段值的哪一种组合是有效的,而哪种组合是无效的。将字段暴露给外部世界会使对象处于不一致的状态。例如,如果您可以在不设置Size字段的情况下更改ArrayList中的支持数组,怎么办?ArrayList实例将不一致,并将违反其合同。因此,数组列表内部数组没有getter和setter。

但是,大多数为其生成getter和setter的对象都是简单的数据持有者。他们对自己的状态没有任何可强制执行的规则,状态空间由所有可能的值组合组成,而且--他们对这些数据无能为力。在你叫我“贫血”之前,不管你是在用领域驱动的设计做“真正的OOP”,在同一个对象中有业务逻辑和状态,还是在做胖的服务层+贫血对象。为什么不重要?因为即使在域驱动的项目中,也有DTO。DTO只是数据持有者,需要getter和setter。

https://www.imdb.com/list/ls508350877/

另一件事是,在许多情况下,您的对象状态无论如何都是公共的。工具使用反射来使用对象--视图技术使用EL访问对象,Orms使用反射来持久化您的实体,Jackson使用反射将对象序列化为JSON,Jasper报告使用反射从其模型获取详细信息,等等。实际上,在常规项目中所做的任何事情都需要将数据传递到应用程序之外:用户、数据库、打印机,这是API调用的结果。你必须知道这些数据是什么。在EL中,您有${foo.bar}-有或没有getter,您使用该字段。在ORM中,您需要知道要使用哪些数据库类型。在JSONAPI的文档中,您应该指定结构(这里的另一个主题是类似REST的服务是否需要文档)。这里的要点是,没有在数据持有者对象上设置getter和setter,就不会赢得任何胜利。他们的内部状态无论如何都是公开的,而且必须是公开的。这些领域的任何变化都意味着必须在其他地方做出改变。改变是人们所害怕的--“你必须在你的项目中的任何地方改变它”。嗯,是的,你有,因为它变了。如果将地址的结构从String更改为Address类,则很可能应该重新访问它所使用的所有位置,并将其拆分到那里。如果你把双倍改为BigDecimal,你最好去修正你所有的计算。

另外一点--上面的例子强调了读取数据。但是,您必须以某种方式设置该数据。你有大约3种选择--构造函数,构建者,设置者。带有15个参数的构造函数显然不是一个选项。每个对象的建设者都太冗长了。所以我们使用setters,因为它更实用,更易读。

这就是这里的要点--在数据持有者对象上使用setter和getter是实用的。我曾经支持过相当大的项目,其中有很多设置者和获取者,而我对此绝对没有问题。事实上,跟踪“谁设置了该数据”与“此对象(封装其数据的对象)来自何处”是相同的。是的,在理想的OO世界中,您不需要数据持有者/DTO,系统中也不会有数据流。但在现实世界里。

最后,对非数据对象的setter和getter要小心。封装是一件真实的事情。在设计库、组件或项目中的一些基本框架时,不要简单地生成getter和setter。但是对于常规的数据对象--别担心,这样做没有坏处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值