一尘不染

用更简单的解决方案代替完整的ORM(JPA /休眠):推荐的加载/保存模式?

java

我正在开发一个新的Java
Web应用程序,并且正在探索保存数据的新方法(对我来说是新的!)。我大多数都具有JPA和Hibernate的经验,但是,除了简单的情况之外,我认为这种完整的ORM可能会变得非常复杂。另外,我不太喜欢和他们一起工作。我正在寻找一个新的解决方案,可能更接近SQL。

我目前正在研究的解决方案:

但是,与Hibernate相比,我担心的是这些解决方案有两个用例。我想知道这些用例的推荐模式是什么。


用例1-获取实体并访问其某些关联的子代和孙代实体。

  • 假设我有一个Person实体。
    • Person具有关联的Address实体。
    • Address具有关联的City实体。
      • City实体具有name属性。

从人员实体开始,访问城市名称的完整路径为:

person.address.city.name

现在,假设我PersonService使用以下方法从加载了Person实体:

public Person findPersonById(long id)
{
    // ...
}

使用Hibernate,Person可以根据需要延迟加载与关联的实体,因此可以访问person.address.city.name并确保我可以访问此属性(只要该链中的所有实体都不为空)。

但是使用我正在研究的3个解决方案中的任何一个,它都更加复杂。使用这些解决方案,建议使用哪种模式来处理该用例?首先,我看到3种可能的模式:

  1. 所需要的所有关联的子代和孙代实体都可以通过所使用的SQL查询来快速加载。

但是我看到的这个解决方案的问题是,可能还有一些其他代码需要访问来自该实体的 其他
实体/属性路径Person。例如,也许某些代码需要访问person.job.salary.currency。如果要重用findPersonById()已有的方法,则SQL查询将需要加载更多信息!不仅关联address->city实体,而且关联job->salary实体。

现在,如果还有其他 10 个地方需要从人员实体开始访问其他信息,该怎么办?我是否应该始终热切地加载 所有
可能需要的信息?或者也许有12种不同的服务方法来加载人实体?:

    findPersonById_simple(long id)

findPersonById_withAdressCity(long id)

findPersonById_withJob(long id)

findPersonById_withAdressCityAndJob(long id)

...

但是,每当我使用一个Person实体时,我就必须知道它已经加载了什么,没有加载了什么……可能会很麻烦,对吧?

  1. 在实体的getAddress()getter方法中Person,是否可以进行检查以查看地址是否已经加载,如果没有,则延迟加载它?这是现实生活中经常使用的模式吗?

  2. 还有其他模式可用于确保我可以从加载的模型访问所需的实体/属性吗?


用例2-保存实体,并确保还保存了其关联的和修改的实体。

我希望能够Person使用此PersonService方法保存实体:

public void savePerson(Person person)
{
    // ...
}

如果我有一个Person实体并且要更改person.address.city.name为其他实体,那么City当我保存该实体时,如何确保该实体的修改将得以保留Person?使用Hibernate,可以很容易地将保存操作
级联 到关联的实体。我正在研究的解决方案如何?

  1. 我应该使用某种 肮脏的 标记来知道在保存人时还必须保存哪些关联的实体吗?

  2. 还有其他已知模式可用于处理此用例吗?


更新 :有一个讨论有关在JOOQ论坛这个问题。


阅读 187

收藏
2020-12-03

共1个答案

一尘不染

当不使用真正的ORM时,这种问题很典型,并且没有灵丹妙药。对于使用iBatis(myBatis)的(不是很大的)Web应用程序来说,一种对我有用的简单设计方法是使用两层持久性:

  • 愚蠢的底层:每个表都有其Java类(POJO或DTO),其 字段直接映射到表的column 。假设我们有一个PERSON表,其中的一个ADDRESS_ID字段指向一个ADRESS表;然后,我们将有一个PersonDb仅包含addressId(整数)字段的类;我们没有 personDb.getAdress()办法,只有平原personDb.getAdressId()。因此,这些Java类非常笨拙(它们对持久性或相关类一无所知)。相应的PersonDao类知道如何加载/持久化此对象。使用iBatis + iBator(或MyBatis + MYBatisGenerator)之类的工具可以轻松创建和维护该层。

  • 包含 丰富域对象的 更高层:这些 对象 通常是上述POJO 的 图形 。这些类还具有通过调用相应的DAO来加载/保存图形(可能很懒惰,可能带有一些脏标志)的智能。但是,重要的是,这些丰富的域对象不是一对一地映射到POJO对象(或DB表),而是使用 域用例 。确定每个图的“大小”(它不会无限增长),并且像特定类一样从外部使用。因此,并不是说您有一个丰富的Person类(带有一些不确定的相关对象图)被用于多个用例或服务方法。相反,您有几个丰富的类PersonWithAddresesPersonWithAllData…每个人都包装了一个特定的,受限制的图,并具有自己的持久逻辑。这似乎效率低下或笨拙,在某些情况下可能是这样,但是经常发生这种情况,当您需要保存完整的对象图时,用例实际上受到限制。

  • 另外,对于表格报告之类的东西((特定的SELECTS返回一堆要显示的列)),您将不会使用上述内容,而会使用直截了当的POJO(甚至可能是Maps)

2020-12-03