什么是惰性策略以及它是如何工作的?
我有一个问题。我正在学习JPA。我在单元测试中使用嵌入式 OpenEJB 容器,但只能使用 @OneToMany(fetch=EAGER)
。否则集合始终为空。我还没有发现,惰性策略是如何工作的,容器如何填充数据以及在什么情况下触发容器的加载动作?
我读过,当调用 getter 时,该操作就会触发。但是当我有代码时:
@OneToMany(fetch = LAZY, mappedBy="someField")
private Set<AnotherEntities> entities = new Set<AnotherEntities>();
...
public Set<AnotherEntities> getEntities() {
return entities;
}
我总是得到空值。我认为,LAZY 策略无法使用嵌入式容器进行测试。问题也可能存在于双向关系中。
还有其他人有类似的 JPA 测试经验吗?
附件
设置的真实测试用例:
@RunWith(UnitilsJUnit4TestClassRunner.class)
@DataSet("dataSource.xml")
public class UnitilsCheck extends UnitilsJUnit4 {
private Persister prs;
public UnitilsCheck() {
Throwable err = null;
try {
Class.forName("org.hsqldb.jdbcDriver").newInstance();
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
props.put("ds", "new://Resource?type=DataSource");
props.put("ds.JdbcDriver", "org.hsqldb.jdbcDriver");
props.put("ds.JdbcUrl", "jdbc:hsqldb:mem:PhoneBookDB");
props.put("ds.UserName", "sa");
props.put("ds.Password", "");
props.put("ds.JtaManaged", "true");
Context context = new InitialContext(props);
prs = (Persister) context.lookup("PersisterImplRemote");
}
catch (Throwable e) {
e.printStackTrace();
err = e;
}
TestCase.assertNull(err);
}
@Test
public void obtainNickNamesLazily() {
TestCase.assertNotNull(prs);
PersistableObject po = prs.findByPrimaryKey("Ferenc");
TestCase.assertNotNull(po);
Collection<NickNames> nicks = po.getNickNames();
TestCase.assertNotNull(nicks);
TestCase.assertEquals("[Nick name: Kutyafája, belongs to Ferenc]", nicks.toString());
}
}
bean Presister
是中介对实体 bean 的访问的 bean。该类的关键代码如下:
@PersistenceUnit(unitName="PhonePU")
protected EntityManagerFactory emf;
public PhoneBook findByPrimaryKey(String name) {
EntityManager em = emf.createEntityManager();
PhoneBook phonebook = (PhoneBook)em.find(PhoneBook.class, name);
em.close();
return phonebook;
}
实体PhoneBook
是一行电话簿(也是人)。一个人可以有零个或多个昵称。通过 EAGER 策略,它是有效的。使用 LAZY 时,集合始终为空。问题可能出在物体的分离上。 (请参阅 OpenEJB - JPA 概念,部分缓存和分离。)< /em> 但手册中写道,集合有时(更像是很多时候)可以为空,但不能为空。
I have a problem. I'm learning JPA. I'm using embedded OpenEJB container in unit tests, but only working is @OneToMany(fetch=EAGER)
. Otherwise is the collection allways null. I haven't found, how the lazy strategy works, how the container fills the data and in which circumstances triggers the container the loading action?
I have read, that the action triggers when the getter is being called. But when I have the code:
@OneToMany(fetch = LAZY, mappedBy="someField")
private Set<AnotherEntities> entities = new Set<AnotherEntities>();
...
public Set<AnotherEntities> getEntities() {
return entities;
}
I'm always getting null. I thing, the LAZY strategy cannot be tested with embedded container. The problem might be also in the bidirectional relation.
Does have anybody else similar expiriences with the JPA testing?
Attachments
The real test case with setup:
@RunWith(UnitilsJUnit4TestClassRunner.class)
@DataSet("dataSource.xml")
public class UnitilsCheck extends UnitilsJUnit4 {
private Persister prs;
public UnitilsCheck() {
Throwable err = null;
try {
Class.forName("org.hsqldb.jdbcDriver").newInstance();
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
props.put("ds", "new://Resource?type=DataSource");
props.put("ds.JdbcDriver", "org.hsqldb.jdbcDriver");
props.put("ds.JdbcUrl", "jdbc:hsqldb:mem:PhoneBookDB");
props.put("ds.UserName", "sa");
props.put("ds.Password", "");
props.put("ds.JtaManaged", "true");
Context context = new InitialContext(props);
prs = (Persister) context.lookup("PersisterImplRemote");
}
catch (Throwable e) {
e.printStackTrace();
err = e;
}
TestCase.assertNull(err);
}
@Test
public void obtainNickNamesLazily() {
TestCase.assertNotNull(prs);
PersistableObject po = prs.findByPrimaryKey("Ferenc");
TestCase.assertNotNull(po);
Collection<NickNames> nicks = po.getNickNames();
TestCase.assertNotNull(nicks);
TestCase.assertEquals("[Nick name: Kutyafája, belongs to Ferenc]", nicks.toString());
}
}
The bean Presister
is the bean mediating access to the entity beans. The crucial code of class follows:
@PersistenceUnit(unitName="PhonePU")
protected EntityManagerFactory emf;
public PhoneBook findByPrimaryKey(String name) {
EntityManager em = emf.createEntityManager();
PhoneBook phonebook = (PhoneBook)em.find(PhoneBook.class, name);
em.close();
return phonebook;
}
Entity PhoneBook
is one line of phone book (also person). One person can have zero or more nick names. With EAGER strategy it works. With LAZY the collection is allways null. May be the problem is in the detaching of objects. (See OpenEJB - JPA Concepts, part Caches and detaching.) But in the manual is written, that the collection can be sometimes (more like manytimes) empty, but not null.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题出在实体的生命周期中。 (Geronimo 使用 OpenJPA,所以不要查看 OpenJPA 教程的 实体生命周期管理。)该应用程序使用容器管理的事务。 bean
Persiser
上的每个方法调用都在自己的事务中运行。并且持久化上下文取决于事务。在事务结束时,即在方法结束时,实体与其上下文断开连接。我尝试以相同的方法获取实体并在第二行获取昵称集合,并且它有效。所以问题被确定了:如果不将实体重新附加到某些持久性上下文,我就无法从数据存储中额外获取任何实体数据。该实体通过 EntityManager.merge() 方法重新附加。该代码需要更多更正。由于实体无法获取 EntityManager 引用并重新附加自身,因此必须将返回昵称的方法移至 Persister 类。 (注释Heureka标记了重新附加实体的关键线。)
然后以这种方式获得集合:
仅此而已。
您可以查看我在本论坛上提出的关于该主题的第二个问题。这是问题OpenJPA - 延迟获取不起作用。
The problem is in the life cycle of an entity. (Geronimo uses OpenJPA, so le't see OpenJPA tutorial, part Entity Lifecycle Management.) The application uses container managed transactions. Each method call on the bean
Persiser
runs in an own transation. And the persistency context depends on the transaction. The entity is disconnected from its context at the end of the transaction, thus at the end of the method. I tried to get the entity and on second line in the same method to get the collection of nick names and it worked. So the problem was identifyed: I cannot get additionally any entity data from the data store without re-attaching the entity to some persistency context. The entity is re-attached by theEntityManager.merge()
method.The code needs more correctures. Because the entity cannot obtain the
EntityManager
reference and re-attach itself, the method returning nick names must be moved to thePersister
class. (The comment Heureka marks the critical line re-attaching the entity.)The collection is then obtained in this way:
That's all.
You can have a look at my second question concerning this topic I'have asked on this forum. It is the qustion OpenJPA - lazy fetching does not work.
我将如何编写代码
想法2:
我只是认为EAGER和LAZY抓取中的操作顺序可能不同,即EAGER抓取可能会
entities< /code>
entities
的值(我假设null
)entities
的值设置为new Set()
而 LAZY 可以
entities
的值设置为new Set()
entities 的值
(我假设null
)'也必须找到对此的引用。
想法 1:(不是正确的答案)
如果您注释 getter 而不是字段会怎样?这应该指示 JPA 使用 getter 和 setter,而不是字段访问。
来自Java 持久性 API - 更简单的实体持久性编程模型
How I would write the code
Idea no 2:
I just thought that the order of operations in EAGER and LAZY fetching may differ i.e. EAGER fetching may
entities
entities
(I'd assumenull
)entities
tonew Set<T>()
while LAZY may
entities
tonew Set<T>()
entities
(I'd assumenull
)'Have to find a citation for this as well.
Idea no 1: (Not the right answer)
What if you'd annotate the getter instead of the field? This should instruct JPA to use getters and setters instead of field access.
From The Java Persistence API - A Simpler Programming Model for Entity Persistence