如何跨多个请求(使用 Wicket 和 JPA)将实体(或其关联)附加到当前持久性上下文?

发布于 2024-11-29 20:41:59 字数 1999 浏览 0 评论 0原文

我正在 Java EE 上开发基于 Wicket 的 Web 应用程序。

我试图找到一种方法来确保在 Wicket 尝试渲染任何组件之前,用作模型对象的任何实体始终附加到当前的 EntityManager。这样,当组件从其模型中获取数据时,实体可以根据需要延迟加载数据。

那里有很多教程,这里还有一些帖子,提到 LoadableDetachableModels (LDM) 作为解决方案。当我们不需要在请求之间保留任何状态时,这对我们有用。在这些情况下,每当呈现页面时,LDM 都会从数据库加载所需实体的最新版本。

然而,有时用户需要在保存数据之前通过多个步骤以有状态形式编辑数据,因此模型需要将实体保留在“未保存”状态。 LDM 将有效地消除用户在每一步中所做的更改。

到目前为止,我们一直在使用一种在需要时将实体与持久性上下文合并的模型。这是一个简化版本:

public final class DetachableMergingModel<E extends Identifiable> implements IModel<E> {

    private E entity;
    private boolean attached = false;

    public DetachableMergingModel(E entity) {
        this.entity = entity;
    }

    @Override
    public E getObject() {
        if (!attached) {
            attached = true;
            // Non-transactional method merges entity with persistence context 
            // but does not flush any data to database
            entity = getRepository().merge(entity);
            }
        }
        return entity;
    }

    @Override
    public void setObject(E entity) {
        this.entity = entity;
    }

    @Override
    public void detach() {
        // This ensures that the next call to getObject() will merge the entity with 
        // the persistence context
        attached = false;
    }
    /* ... */
}

我们的 EntityManager 由 GlassFish 注入,它跨越整个 servlet 请求,因此当实体附加到持久性上下文时,它将保持附加状态,直到页面呈现之后。

上面的模型处理实体已经被持久化并且刚刚被编辑的情况。每当页面上的组件调用此模型上的 getObject() 时,模型都会将实体与持久化上下文合并,并且我们可以自由地导航实体的整个对象图,而不会抛出任何 LazyInitializationExceptions。

但是,在实体是新的且尚未持久化的情况下,我们无法使用此模型,因为实体尚未准备好合并。当用户创建新实体并且仍然需要通过表单填充值时,通常会出现这种情况。在这种情况下,我们希望拥有相同的自由度来导航实体的对象图(因为已经设置了一些关联,例如实体的父级),而不必担心出现 LazyInitializationException。

此处描述了类似的解决方案(选项#3),但它不涵盖上述“新实体”用例。

有人遇到过这个用例吗?你是怎么解决的?有没有比通过自定义 IModel 实现更好的方法将 Wicket 与 JPA 集成?

I am working on a Wicket-based web app on Java EE.

I am trying to find a way to ensure that any entities used as model objects are always attached to the current EntityManager before Wicket tries to render any components. This way, when the components grab data from their model, the data can be lazily-loaded by the entity as needed.

There are lots of tutorials out there, and some posts on here, referring to LoadableDetachableModels (LDM) as the solution. This has worked for us when we don't need to keep any state in-between requests. In these cases, whenever the page is rendered, the LDM will load the most recent version of the required entity from the database.

However, there are times when a user needs to edit data in a stateful form via multiple steps before she saves the data, so the model needs to retain the entity in its 'unsaved' state. An LDM would effectively wipe out the user's changes on every step.

So far, we have been using a model that merges the entity with the persistence context when needed. Here is a simplified version:

public final class DetachableMergingModel<E extends Identifiable> implements IModel<E> {

    private E entity;
    private boolean attached = false;

    public DetachableMergingModel(E entity) {
        this.entity = entity;
    }

    @Override
    public E getObject() {
        if (!attached) {
            attached = true;
            // Non-transactional method merges entity with persistence context 
            // but does not flush any data to database
            entity = getRepository().merge(entity);
            }
        }
        return entity;
    }

    @Override
    public void setObject(E entity) {
        this.entity = entity;
    }

    @Override
    public void detach() {
        // This ensures that the next call to getObject() will merge the entity with 
        // the persistence context
        attached = false;
    }
    /* ... */
}

Our EntityManager is injected by GlassFish and it spans a whole servlet request, so when an entity is attached to the persistence context, it will stay attached until after the page has been rendered.

This Model above takes care of situations where the entity is already persisted and is just being edited. Whenever a component on the page calls getObject() on this model, the Model will merge the entity with the persistence context, and we are free to navigate the whole object graph of the entity without throwing any LazyInitializationExceptions.

However, in a situation where the entity is new and has not been persisted, we cannot use this Model because the entity is not ready to be merged yet. This is often the case when the user is creating a new entity, and still needs to populate it with values via the form. In this case, we want to have the same freedom navigating the object graph of the entity (as some associations have already been set, such as the entity's parent), without fear of a LazyInitializationException.

A similar solution is described here (option #3), but it does not cover the 'new entity' use-case described above.

Has anyone come across this use-case? How did you solve it? Is there a better way to integrate Wicket with JPA than through custom IModel implementations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

孤独患者 2024-12-06 20:41:59

在这种情况下,我通常会创建一个 DTO 来保存创建实体所需的所有数据。
如果您需要引用现有实体 - 将它们作为单独的模型传递到您的表单/面板。提交表单后,您可以:

  • 执行验证
  • 从已编辑的 DTO 创建一个新实体
  • 注入对您存储在这些单独的 LDM 模型中的其他实体的引用。
  • 持久化实体。

虽然有点麻烦,但确实有效。

跨越多个请求的对话在纯 wicket 中不可用,并且可能永远不会 - 这不是 wicket 的区域。

Seam 支持长时间对话,并且它确实支持 wicket(我从未使用过 Seam - 我无法在这方面向您提供建议)。

I cases like this I usually create a DTO keeping all the data I need to create an entity.
If you need to reference existing entities - pass them to your forms/panels as separate models. When the form is submitted you can then:

  • perform the validation
  • create a new entity from DTO that was edited
  • inject references to other entities that you have stored in those separate LDM models.
  • persist the entity.

It's a bit of hassle but does actually work.

Conversations spanning multiple requests are not available in pure wicket and probably will never be - this is not wicket's area.

Seam supports long conversations and it does support wicket (I have never used seam - I cannot advice you on this one).

时光与爱终年不遇 2024-12-06 20:41:59

似乎是一篇旧帖子..

我希望它能对其他人有所帮助:

首先:
你的LDM有点没用,因为实体属性不是瞬态的。因此,您的实体被序列化到会话存储中,这不是 LDM 的含义。它应该最小化任何模型数据的序列化大小。

第二:
你的问题不是真正的问题,因为你需要的是你已经拥有的:
您想要准备一个跨多个页面的实体,最终存储在数据库中(某些向导等......)。现在,您的 LDM 已在客户端请求之间完全序列化到会话存储,您的实体及其编辑的数据可以承受多个请求,无需进行任何合并。一旦您的向导完成,您只需保留该孔实体即可。在实体处于最终状态之前,保留任何内容都是没有意义的(尽管它在会话存储中的请求中仍然存在)。

您甚至不需要 LDM 来实现这种功能。

只需将实体作为参数提供给下一页,用户可以在其中完成其数据。

希望你已经解决了这个问题..

Seems to be kindof an old post..

I hope it will help some others anyway:

First:
Your LDM is a bit useless, because the entity property is not transient. Therefore your entity gets serialised to your session store and thats not the meaning of an LDM. It's supposed to minimize the serialization size of any modeldata.

Second:
Your problem isn't a real problem, cause what you need, is what you already have:
You want to prepare an Entity over several pages to be finally stored in your database (some wizard or so..). Now, that your LDM is fully serialized to your session store between the requests of the client, your entity and its edited data survives multiple requests, no need for any merging. As soon, as your wizard is finished, you simply persist the hole entity. Before the entity is in its final state, it doesn't make sense to persist anything (though it survives over requests in your session store).

You don't even need the LDM for this kind of funcionality..

Simply give the entity as parameter to the next page, where the user may complete its data.

Hope you solved this problem already..

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文