用更轻的解决方案替换完整的ORM(JPA / Hibernate):推荐的加载/保存模式? [英] Replacing a full ORM (JPA/Hibernate) by a lighter solution : Recommended patterns for load/save?
问题描述
我正在开发一个新的Java Web应用程序,我正在探索新方法(对我来说是新的!)来保存数据。我大部分都有JPA& amp; Hibernate但是,除了简单的情况,我认为这种完整的ORM会变得非常复杂。另外,我不喜欢和他们一起工作。我正在寻找一种新的解决方案,可能更接近SQL。
I'm developing a new Java web application and I'm exploring new ways (new for me!) to persist the data. I mostly have experience with JPA & Hibernate but, except for simple cases, I think this kind of full ORM can become quite complex. Plus, I don't like working with them that much. I'm looking for a new solution, probably closer to SQL.
我正在调查的解决方案:
The solutions I'm currently investigating :
- MyBatis
- JOOQ
- Plain SQL/JDBC, potentially with DbUtils or some other basic utility libraries.
但与Hibernate相比,有两个用例我担心这些解决方案。我想知道这些用例的推荐模式是什么。
But there are two use cases I'm worrying about with those solutions, compared to Hibernate. I'd like to know what are the recommended patterns for those use cases.
- 假设我有
人
实体。
- 此
人
有一个关联的地址
实体。
- 此
地址
有一个关联的城市
实体。
- 此
City
实体的名称
物业。
- Let's say I have a
Person
entity.- This
Person
has an associatedAddress
entity.- This
Address
has an associatedCity
entity.- This
City
entity has aname
property.
从人物实体开始,访问城市名称的完整路径为:
The full path to access the name of the city, starting from the person entity, would be :
person.address.city.name
现在,假设我加载了Person实体来自
PersonService
,使用此方法:Now, let's say I load the Person entity from a
PersonService
, with this method :public Person findPersonById(long id) { // ... }
使用Hibernate,可以根据需要延迟加载与
Person
相关联的实体,因此可以访问person.address.city.name
并确保我可以访问此属性(只要该链中的所有实体都不可为空)。Using Hibernate, the entities associated to the
Person
could be lazily loaded, on demand, so it would be possible to accessperson.address.city.name
and be sure I have access to this property (as long as all the entities in that chain are not nullable).但是使用任何一个我正在调查的3个解决方案,它更复杂。有了这些解决方案,有哪些推荐的模式来处理这个用例?在前面,我看到了3种可能的模式:
But using anyone of the 3 solutions I'm investigating, it's more complicated. With those solutions, what are the recommended patterns to take care of this use case? Upfront, I see 3 possible patterns:
-
所有必需的关联子孙实体都可能被所使用的SQL查询急切加载。
All the required associated children and grandchildren entities could be eagerly loaded by the SQL query used.
但是我在这个解决方案中看到的问题是,可能还有一些其他代码需要从<其他实体/属性路径访问
人
实体。例如,某些代码可能需要访问person.job.salary.currency
。如果我想重用我已经拥有的findPersonById()
方法,那么SQL查询将需要加载更多信息!不仅是关联的地址 - >城市
实体,还有关联的job-> salary
实体。But the issue I see with this solution is that there may be some other code that needs to access other entities/properties paths from the
Person
entity. For example, maybe some code will need access toperson.job.salary.currency
. If I want to reuse thefindPersonById()
method I already have, the SQL query will then need to load more information! Not only the associatedaddress->city
entity but also the associatedjob->salary
entity.现在如果 10 其他地方需要从人员实体开始访问其他信息怎么办?我是否应该始终急切地加载所有可能需要的信息?或者可能有12种不同的服务方法来加载一个人实体? :
Now what if there are 10 other places that need to access other information starting from the person entity? Should I always eagerly load all the potentially required information? Or maybe have 12 different service methods to load a person entity? :
findPersonById_simple(long id) findPersonById_withAdressCity(long id) findPersonById_withJob(long id) findPersonById_withAdressCityAndJob(long id) ...
但是每次我使用
Person
实体时,我都必须知道它已经加载了什么,什么没有...这可能非常麻烦,对吧?But then everytime I would use a
Person
entity, I would have to know what has been loaded with it and what hasn't... It could be quite cumbersome, right?getAddress()
的getter方法Person
实体,是否可以检查地址是否已经加载,如果没有,懒得加载它?这是现实应用中经常使用的模式吗?In the
getAddress()
getter method of thePerson
entity, could there be a check to see if the address has already been loaded and, if not, lazily load it? It this a frequently used pattern in real life applications?是否有其他模式可用于确保我可以访问我需要的实体/属性已加载的模型?
Are there other patterns that can be used to make sure I can access the entities/properties I need from a loaded Model?
用例2 - 保存实体和确保其关联和修改的实体也被保存。
我希望能够使用
Person
实体保存这个PersonService
的方法:
Use Case 2 - Saving an entity and making sure its associated and modified entities are also saved.
I want to be able to save a
Person
entity using thisPersonService
's method :public void savePerson(Person person) { // ... }
如果我有一个
Person
实体,我将person.address.city.name
更改为其他内容,我该如何确保City
保存Person
时,实体修改会持续存在?使用Hibernate,可以轻松地级联对关联实体的保存操作。我正在研究的解决方案怎么样?If I have a
Person
entity and I changeperson.address.city.name
to something else, how can I make sure theCity
entity modifications will be persisted when I save thePerson
? Using Hibernate, it can be easy to cascade the save operation to the associated entities. What about the solutions I'm investigating?-
我应该使用某种脏标志来知道在保存此人时还必须保存哪些关联实体?
Should I use some kind of dirty flag to know what associated entities also have to be saved when I save the person?
是否还有其他已知模式可用于处理此用例?
Are there any other known patterns useful to deal with this use case?
更新 :有关于此问题的讨论 JOOQ论坛。
推荐答案
这种问题在不使用真正的ORM时是典型的,并且没有银弹。一个简单的设计方法适用于iBatis(myBatis)的(不是很大的)webapp,是使用两个层进行持久化:
This kind of problem is typical when not using a real ORM, and there is no silver bullet. A simple design approach that worked for me for a (not very big ) webapp with iBatis (myBatis), is to use two layers for persistence:
-
一个哑的低层:每个表都有它的Java类(POJO或DTO),字段直接映射到表列。假设我们有一个
PERSON
表,其中ADDRESS_ID
字段指向ADRESS
table;
然后,我们有一个PersonDb
类,只有addressId
(整数)字段;我们没有personDb.getAdress()
方法,只有普通的personDb.getAdressId()
。这些Java类非常愚蠢(他们不了解持久性或相关类)。相应的PersonDao
类知道如何加载/持久化此对象。使用iBatis + iBator(或MyBatis + MYBatisGenerator )。
A dumb low-level layer: each table has its Java class (POJO or DTO), with fields that maps directly to the table columns. Say we have a
PERSON
table with aADDRESS_ID
field that points to anADRESS
table; then, we'd have aPersonDb
class, with just aaddressId
(integer) field; we have nopersonDb.getAdress()
method, just the plainpersonDb.getAdressId()
. These Java classes are, then, quite dumb (they don't know about persistence or about related classes). A correspondingPersonDao
class knows how to load/persist this object. This layer is easy to create and maintain with tools like iBatis + iBator (or MyBatis + MYBatisGenerator).
包含富域对象的更高级别图层:每个图层通常都是图表强烈的>上述POJO。这些类还具有通过调用相应的DAO来加载/保存图形(可能是懒惰,可能带有一些脏标志)的智能。但重要的是,这些富域对象不会一对一地映射到POJO对象(或DB表),而是映射域用例。确定每个图形的大小(它不会无限增长),并且从外部像特定类一样使用。所以,并不是你有一个富的
Person
类(带有一些相关对象的不确定图),它们是几个用例或服务方法;相反,你有几个丰富的类,PersonWithAddreses
,PersonWithAllData
...每个包装一个特定的限定图,有自己的持久性逻辑。这可能看起来效率低或笨拙,并且在某些情况下可能会这样,但是当您需要保存完整的对象图时,用例实际上是有限的。A higher level layer that contains rich domain objects: each of these is typically a graph of the above POJOs. These classes have also the intelligence for loading/saving the graph (perhaps lazily, perhaps with some dirty flags), by calling the respective DAOs. The important thing, however, is that these rich domain objects do not map one-to-one to the POJO objects (or DB tables), but rather with domain use cases. The "size" of each graph is determined (it doesn't grow indefinitely), and is used from the outside like a particular class. So, it's not that you have one rich
Person
class (with some indeterminate graph of related objects) that is used is several use cases or service methods; instead, you have several rich classes,PersonWithAddreses
,PersonWithAllData
... each one wraps a particular well-limited graph, with its own persistence logic. This might seem inefficient or clumsy, and in some context it might be, but it happens often that the use cases when you need to save a full graph of objects are actually limited.此外,对于像表格报告这样的东西(特定的SELECTS返回要显示的一堆列)你不会使用上面的内容,而是直接和愚蠢的POJO(甚至可能是地图)
Additionally, for things like tabular reports, (specific SELECTS that return a bunch of columns to be displayed) you'd not use the above, but straight and dumb POJO's (perhaps even Maps)
参见我的相关答案这里
这篇关于用更轻的解决方案替换完整的ORM(JPA / Hibernate):推荐的加载/保存模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- This
- This
- This
- 此
- 此
- 此