【问题标题】:Good usage of managed entities / proxies with ORM使用 ORM 很好地使用托管实体/代理
【发布时间】:2016-06-14 11:47:26
【问题描述】:

我目前正在考虑如何处理我的域对象以及 hibernate 考虑以下几点:

  • 我的模型对象直接用JPA注解,没有实体层。
  • 在某些数据库繁重的操作中,我不介意调整我的代码,因此我可以充分利用代理,即使我们可以将其视为抽象/实现掩码的泄漏。当然我更喜欢当我可以做其他事情的时候。
  • 因为我没有实体层,我没有 DAO 层,实体管理器被认为是 DAO 层(相关:I found JPA, or alike, don't encourage DAO pattern

但是,我正在考虑改进我正在做的事情,以降低一点复杂性,或者至少将复杂性重新定位到更适合的地方,例如实体的相关服务。也许更抽象的事实是我正在使用 ORM。

这是一个通用的 CRUD 服务,我的所有业务服务都从该服务继承。这段代码是为了向你展示当前的事情是如何完成的(为了清楚起见,注释、日志删除):

public void create(T entity) {
    this.entityManager.persist(entity);
}
@Transactional(value = TxType.REQUIRED, rollbackOn=NumeroVersionException.class)
public void update(T entity) throws NumeroVersionException{
    try{
        this.entityManager.merge(entity);
    }catch(OptimisticLockException ole){
        throw new NumeroVersionException("for entity "+entity, ole);
    }
}

public T read(int id) {
    return this.entityManager.find(entityClass, id);
}
public void delete(int id) {
    T entity = this.entityManager.getReference(entityClass, id);
    this.entityManager.remove(entity);
    // edit : removed null test thanks to @JBNizet
}

这种实现的问题在于,如果我想创建一个对象,然后利用代理的优势,我基本上必须创建它然后重新获取它。当然,查询可能不会访问数据库,而只会访问 hibernat 的缓存(虽然不确定)。但这意味着我仍然必须不要忘记重新获取代理。

这意味着我泄露了我在幕后使用 ORM 和代理的事实。

所以我正在考虑将我的界面更改为:

public T read(int id);
public T update(T t)throws NumeroVersionException;
public T create(T object);
public void delete(int id);
List<T> list();

意味着一旦我将一个对象传递给这一层,我将不得不使用返回的值。 并具体实现更新,例如:

public T update(T t){
    if(!(t instanceof [Proxy class goes there])){
        //+ check if it is a detached proxy
        entityManager.merge(t);
    }  
}

由于每次调用合并都会命中数据库,对于一些只涉及大约 10 个实体的操作,这可能很烦人,我不会在带有代理的更新方法中调用它。

当然,我希望有一些边缘情况,我需要 entityManager 来刷新等等。但我认为这将显着降低我当前代码的复杂性并更好地隔离问题。

简而言之,我试图在服务中重新定位 ORM 代码,这样我就可以隐藏我正在使用 ORM 和代理的事实,并像使用任何其他实现一样使用接口,而不会失去以下好处使用 ORM。

问题是这样的:

  1. 这个新设计对这个想法来说是个好主意吗?
  2. 我是否错过了有关如何正确处理此问题的任何信息?

注意:尽管我在谈论性能,但我也关心关注点的隔离、可维护性以及对于不熟悉我所使用的 ORM 和 Java 的开发人员来说更容易使用。

【问题讨论】:

  • 我很难理解你在问什么。您说您没有 DAO,但您展示的服务 DAO:根本没有业务逻辑,纯粹是与持久性相关的操作。 merge() 有充分的理由返回一个实体,并且由于您的 update() 方法直接调用了 merge(),因此它当然应该返回该实体。关于代理,我不知道你关心的是什么。您不必关心是否从 Hibernate 获得代理,以及 update() 是否收到代理。
  • 顺便说一句,update()/merge() 几乎从不需要。通常,您从 find() 或查询中获取托管实体,对其进行修改,仅此而已。 JPA 使更改自动持久化。
  • @JBNizet 我有比这更多的方法,这个类是一个泛型,可以处理我真正的服务层继承的 crud 操作。一些覆盖它以添加业务逻辑,然后依赖 super.create/update 来处理实体部分
  • @JBNizet 实际上我的问题是:在比服务更高的级别上,我应该像使用 ORM 代理一样使用我的对象(将自动更新并启动)还是应该将它们用作我的服务实现可能不是 ORM 吗?
  • @JBNizet :我编辑了我的帖子,试图让我的问题更容易理解。我也考虑了 getReference() 点

标签: spring hibernate jpa orm proxy-classes


【解决方案1】:

感谢@JBNizet,我更清楚地看到了一些东西:

  • 我应该使用merge()方法返回的值。
  • 托管实体并不总是代理。
  • 我不必抽象出我使用托管实体这一事实,这会导致代码复杂且效率低下
  • 我选择了 JPA,除非重写完整模型以代表基于非关系数据库的东西,否则我不会切换它。

所以我将只更改原始代码的更新方法,其余的我会保留。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-13
    • 2014-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 2019-01-27
    相关资源
    最近更新 更多