JPA实体模型提案
我正在尝试通过 hibernate 使用 JPA2.0 设计一个实体模型。
我有一个这样的域:有很多餐馆,所以显然有一个餐馆实体。每家餐厅都提供多种不同的菜肴,并且 0 到多家餐厅可能提供相同的菜肴。每道菜都有多种食材,有些菜会共用食材。每种成分都含有多种营养素,并且每种营养素也可能被0到多种成分所拥有。
我很挣扎,因为我什至不确定我的问题到底是什么,但我知道我错过了一些东西。我认为问题在于如何准确地强制执行域模型以及主键和 .equals/.hashcode 方法的含义是什么。
最初我打算使用生成的代理键。每个实体都会有类似的东西
@Id
@GeneratedValue
public Long id;
,并且 .equals 将测试除 id 之外的所有实体成员的相等性。例如,无论 Long id 的值如何,餐厅都是相等的,但其 List 成员相等。 (当然,Dish 会一直调用 .equals ,一直到营养物质。)
因此,当我给 DAO bean 一个餐厅时,我只希望它持续存在,前提是数据存储中没有等效的餐厅(通过 .equals) 。但我想这会涉及一个相当费力的 jpql 调用,我觉得它不优雅或高效(如果我错了,请告诉我。我经常错......哈哈)。在此过程中,如果它遇到说一种已经存在于数据存储中的成分(但不是菜肴的一部分,因此是新保留的),它会将新菜肴与已经存在的成分相关联,而不是创建一个全新的(并且逻辑上重复)成分。
我想得越多,在这个等级制度中坚持更高的事物听起来就越痛苦。
我开始研究复合 Key(通过 @IdClass 或 @EmbeddedId)的想法,它本身可以保存子成员。这似乎将层次结构中较高的内容持久化就像通过 id 查找并在我找不到任何内容时持久化一样简单。但我对此很怀疑......这似乎不对。这意味着我正在创建实体,其中所有成员和实体本身以及组合键的一部分。
有些事情需要注意。营养素的含量相对有限。成分的空间是 {numOfNutrients}^{typesOfNutrients},可能相当大。当我们到达餐厅时,客户几乎不可能意外地建造一个重复的餐厅,因为空间确实很大。有时我实际上并不关心数据存储中是否存在重复项,因为这种情况不太可能发生。事实上,看看这种情况是否发生会很有趣。餐厅的例子也并非是人为的。不过,我的真实域名在形式上相似。
抱歉胡言乱语。我知道帖子里有很多想法。但我觉得它们都很相关,我想一次性把它们都提起来。
有人可以提供一些魔法吗?
我知道这篇文章: 行为类似于 @Entity 和 @ 的类可嵌入 通常我会看到类似的内容,“除非特殊情况”,否则不要这样做。我正在谈论的就是其中一个案例。如果不是,那么使用它的合适情况到底是什么。
另请注意,一旦将餐厅放入数据库,其表示形式就不可改变。我意识到这对于人为的例子来说是不现实的,但这就是我的领域的情况。这让我认为将所有成员作为组合键的一部分是正确的方法......
I am trying to design an entity model using JPA2.0 via hibernate.
I have a domain like this: There are a bunch of restaurants, so obviously there is a restaurant entity. Each restaurant has a number of different dishes that it serves and 0-to-many restaurants may serve the same thing. Each dish has a number of ingredients, and some dishes will share ingredients. Each ingredients has several nutrients, and again each nutrient might be possessed by 0-to-many of the ingredients.
Im struggling because Im not even sure exactly what my problem is, but I know that I am missing something. I believe the question lies in how exactly to enforce the domain model and what are the implications for primary keys and .equals/.hashcode methods.
Originally I was going to use a generated surrogate key. Each entity would have something like
@Id
@GeneratedValue
public Long id;
And the .equals would test equality for all the entities members except id. For example a restaurant would be equal no matter the value of Long id, but in the equality of its List member. (and of course Dish would keep calling .equals all teh way down the hirarchy to nutrients.)
So when I give a DAO bean a restaurant, I only want it persisted iff there are no eqivalent restaurants in the data store (via .equals). But I guess that would involve a rather laborious jpql call, which I don't find elegant or efficient (please let me know if I am wrong. I am often wrong... lol). Further along the way, if it encountered say an ingredient that was already in the data store (but part of a dish that was not and therefore newly persisted) it would associate new dish with the ingredient already there rather than creating an entirely new (and logically duplicate) ingredient.
The more I thought About it, the more painful it sounded to persist things higher up in this heirarchy.
I started to look into the idea of a composite Key (via @IdClass or @EmbeddedId) that would itself hold the child members. This seems like persisting things higher in the hierarchy would be as simple as finding by id and persisting if I dont find anything. But I'm very suspicious of this... It does not seem right. It would mean that I am creating entities where all members and entities themselves and part of the composite key.
Some things to note. There are a relatively finite amount of nutrients. The space for ingredients is {numOfNutrients}^{typesOfNutrients} which could be fairly large. by the time we get up to restaurants, it will be almost impossible for a client to construct a duplicate resaurant by acccident as the space will be truly large. There comes a point when I don't actually care if a duplicate is in the data store, because it is so unlikely to happen. In fact it would almost be interesting to see if this occurred. Also not that the restaurant example is contrived. My real domain is similar in form though.
Sorry for the rambling. I know there are a lot of ideas in post. But I feel they are all so related, I wanted to bring them up all at once.
Can someone please offer some wizardry?
I am aware of this post: A class that behaves like @Entity and @Embeddable
Usually I see something like, don't do this "except in special cases". Is what Im talking about one of these cases. If not, what exactly would be the appropriate case to use this.
Also note that once a Restaurant is put in the database, its representation is immutable. I realize this is unrealistic with the contrived example, but thats the case of my domain. This makes me think that making all the members part of the composite key is the way to go....
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Hibernate 不使用 equals 和 hashCode 来了解实体是否已经持久化。如果实体有 ID,则它是持久的。不然就是新的了如果两个实体具有相同的 ID,则它们是相同的。 equals 方法仅用于在您的代码中(而不是在 Hibernate 代码中)比较两个分离的实体,或者比较分离的实体与附加的实体。
如果实体存储在 Set 中,您还需要 equals 和 hashCode。
如果您确实需要有 equals 和 hashCode 方法,那么使用子实体的集合来实现它们是一个非常糟糕的主意。您不仅需要加载一半的数据库才能调用它们,而且一旦您将配料添加到菜肴中,其哈希码就会发生变化,这会“破坏”其存储位置的集合。
您应该使用实体的不可空、不可变的唯一功能字段来实现 equals 和 hashCode(餐厅名称、菜肴名称、营养素名称)。并且一定要使用代理标识符。如果没有这样的字段,可以使用 ID。但只需确保在将实体存储到集合中之前生成它即可。
Hibernate doesn't use equals and hashCode to know if an entity is already persistent or not. If the entity has an ID, it is persistent. Else, it's new. And two entities are the same if they have the same ID. The equals method is only used to compare two detached entities, or to compare a detached entities with an attached one, in your code (and not in the Hibernate code).
You also need equals and hashCode if the entities are stored in a Set.
If you really need to have an equals and hashCode method, using the collection of children entities to implement them is a very bad idea. Not only do you need to load half of the database to be able to call them, but as soon as you add an ingredient to a dish, its hashCode changes, which "corrupts" the set where it's stored.
You should use a non-nullable immutable unique functional field of the entity to implement equals and hashCode (the restaurant's name, the dish name, the nutrient's name). And definitely use surrogate identifiers. If you don't have such a field, you can use the ID. But just make sure to have it generated before storing the entity in a Set.