EntityManager 的 find() 方法是否创建 JPA 类的新实例?
我有点困惑。问题就在标题中,这就是我问的原因。 我在单个虚拟机上运行 JSF + JPA Web 应用程序。 JPA 类具有 @Transient
字段。现在想象一下,某个 Web 用户打开某个页面并执行下面的代码
import javax.persistence.EntityManager;
// method 1 in backing bean
Agent a = entityManager.find(Agent.class, "007");
a.setTransientValue("Aston Martin");
当另一个 Web 用户/线程尝试读取该瞬态值时,我应该期望什么输出:
// method 2 in backing bean
Agent a = entityManager.find(Agent.class, "007");
String val = a.getTransientValue();
换句话说,就 JVM 而言,find()
方法总是返回新的类实例或相同的或“这取决于”?我已经通过 JSR-220 寻找答案,但没有成功,任何帮助或文档参考将不胜感激。
I'm a bit confused. The question is in title, and here's why I'm asking.
I have JSF + JPA web-application running on a single VM.
And an JPA class has @Transient
field. Now imagine some web user opens some page and executes code below
import javax.persistence.EntityManager;
// method 1 in backing bean
Agent a = entityManager.find(Agent.class, "007");
a.setTransientValue("Aston Martin");
What output should I expect when another web user/thread tries to read that transient value:
// method 2 in backing bean
Agent a = entityManager.find(Agent.class, "007");
String val = a.getTransientValue();
In other words, and in terms of JVM, does find()
method return always new class instance or the same or 'it depends'? I've looked through JSR-220 for an answer, with no success, any help or doc reference would be appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您在同一会话中(即在同一实体管理器生命周期内)调用find(..),则将返回相同的对象引用。
find()
的文档指定了这一点:换句话说,
EntityManager
保存实体的集合(最有可能是映射)。当您调用find
时,它会检查该集合。如果在那里找不到该实体,则会对数据库进行查询。返回的实体被放入映射中,因此后续调用将在那里找到它。但请再次注意,这仅适用于一个会话的范围。这通常与一个 http 请求相同(在 Web 应用程序上下文中)
If you invoke
find(..)
within the same session (that is, within the same entitymanager lifetime), then the same object reference will be returned. The documentation offind()
specifies this:In other words, the
EntityManager
holds a collection (map most likely) of entities. When you callfind
it checks that collection. If the entity is not found there, a query to the database is made. The returned entity is put into the map, so subsequent calls will find it there.But note again that this is only for the span of one session. This is usually the same as one http request (in the web app context)
要真正理解其工作原理,理解实体管理器和上下文之间的关系至关重要。
实体管理器是您访问实体的公共接口,但是,您的实体驻留在附加到实体管理器的上下文中。了解不同类型上下文的生命周期将回答您的问题。
持久化上下文可以有不同的类型。在 Java EE 应用程序中,您可以拥有事务范围的持久性上下文或扩展持久性上下文。在 JSE 应用程序中,上下文的性质由开发人员控制。
当您向实体管理器请求实体时,它会在其附加上下文中查找该实体,如果在那里找到该实体,则返回该实体,否则,它会从数据库中检索该实体。在上下文中对该实体的后续调用将返回相同的实体。
事务范围
在使用事务范围持久化上下文的 Java EE 应用程序中,当您第一次访问实体管理器时,它会检查当前 JTA 事务是否附加了上下文,如果尚不存在上下文,创建一个新的上下文,并将实体管理器链接到该上下文。然后从数据库中读取实体(如果存在,则从缓存中读取)并将其放入上下文中。当事务结束(提交或回滚)时,上下文将变得无效,并且其中的任何实体都会被分离。这是无状态会话 Bean 的经典场景。
这也意味着,根据您设计事务的方式,您最终可能会得到多个上下文。
扩展持久性上下文
在具有有状态会话 bean 的 Java EE 应用程序中,您可能希望上下文能够在多个 bean 调用中幸存下来,因为您不希望在 bean 被标记为删除之前提交,对吧?在这些情况下,您需要使用扩展的持久性上下文。在这种情况下,持久性上下文是在第一次需要时创建的,但在您将有状态 bean 标记为要删除之前,它不会变得无效。
这意味着,无论在有状态会话 Bean 方法的后续调用中注入到此 Bean 中的实体管理器的实例如何,您都可以确保始终访问相同的上下文,因此,即使后续调用也将返回相同的内容。例如,因为它是相同的上下文。
此外,在 Bean 被标记为要删除或您手动刷新它们之前,您的更改不会被刷新。
应用程序管理
您始终可以手动实例化您的实体管理器工厂和实体管理器。这是您通常在 JSE 应用程序中执行的操作,对吧?
对于这种类型的应用程序,您通常没有容器来处理 JTA 事务,对吧?因此,您使用资源本地事务,并负责手动提交或回滚更改。
对于这种类型的应用程序,当您实例化实体管理器时,会自动附加一个上下文。
根据您的应用程序,您可以决定创建一个全局实体管理器,其生命周期附加到应用程序本身的生命周期。也就是说,应用程序的整个生命周期都有一个实体管理器。在这种情况下,您的上下文将由您的实体管理器创建和销毁。
或者,您可以为与应用程序用户的每个对话(即事务)创建一个实体管理器。在这种情况下,范围由您决定,但您的上下文仍然将由您的实体管理器创建和销毁。
To actually understand how this works it is vital to understand the relationship between the entity manager and the context.
The entity manager is the public interface through which you access your entities, however, your entities reside in a context, attached to your entity manager. Understanding the life cycle of the different types of contexts will answer your question.
Persistence contexts can be of different types. In Java EE applications, you can either have a transaction-scoped persistence context or a extended-persistence context. In the JSE application, the nature of the context is controlled by the developer.
When you ask for an entity to your entity manager, it looks for the entity in its attached context, if it finds the entity there, then it returns it, otherwise, it retrieves the entity from the database. Subsequent calls for this entity in context will return the same entity.
Transaction-scoped
In a Java EE application using the transaction-scoped persistence context, when you first access your entity manager, it checks if the current JTA transaction has a context attached, if no context is yet present, a new context is created and the entity manager is linked to this context. Then the entity is read from the database (o from the cache if present) and it is placed into the context. When your transaction ends (commit or rollback), the context becomes invalid and whatever entities in it become detached. This is the classical scenario for stateless sessions beans.
This also means that depending on how you design your transactions, you may end up with more than one context.
Extended-Persistence Context
In a Java EE application with statefull session beans you might like the context to survive multiple bean invocations, since you won't like to commit until the bean has been marked for removal, right? In those cases you need to use a extended persistence context. In this case, the persistence context is created when it is first needed, but it won't become invalid until your mark the statefull bean for removal.
This means that, regardless of the instance of the entity manager that gets injected into this bean in subsequent calls of the statefull session beans methods, you can be sure you will always access the same context, and therefore, even subsequent calls will return the same instance, because it is the same context.
Also, your changes will not be flushed until the bean is marked for removal or you manually flush them.
Application-Managed
You can always instantiate manually your entity manager factory, and your entity manager. This is what you would typically do in a JSE application, right?
For this kind of applications you typically do not have a container to deal with the JTA transactions, right? So you use resource local transactions and you are responsible for manually committing or rolling back changes.
For this kind of application, when you instantiate your entity manager, a context is automatically attached to it.
Depending of your application, you can decide to create a global entity manager whose life cycle is attached to the life of the application itself. That is, a single entity manager for the entire life of the application. In this cases, you context will be created and destroyed with your entity manager.
Or, you could create a entity manager per conversation (i.e. transaction) with your application user. The scope in this case is determined by you, but still, your context will be created and destroyed with your entity manager.