我有问题。我正在学习JPA。我在单元测试中使用嵌入式OpenEJB容器,但只能使用@OneToMany(fetch=EAGER)
。否则集合总是为null。我还没有找到,懒惰策略如何工作,容器如何填充数据以及在哪种情况下触发容器加载操作?
我已经读过,在调用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 Concepts,部分缓存和分离。)但是在编写手册时,集合有时(更像很多次)是空的,但不是空的。
答案 0 :(得分:2)
问题出在一个实体的生命周期中。 (Geronimo使用OpenJPA,因此请参阅OpenJPA教程,部分Entity Lifecycle Management。)应用程序使用容器管理的事务。对bean Persiser
的每个方法调用都在自己的转换中运行。持久化上下文取决于事务。实体在事务结束时与其上下文断开连接,因此在方法结束时。我尝试获取实体,并在同一方法的第二行获取昵称的集合,它工作。因此,问题被识别出来:我无法从数据存储中获得任何实体数据,而无需将实体重新附加到某个持久性上下文。该实体通过EntityManager.merge()
方法重新附加。
代码需要更多修正。由于实体无法获取EntityManager
引用并重新附加自身,因此返回昵称的方法必须移至Persister
类。 (评论 Heureka 标志着重新附加实体的关键线。)
public Collection<NickNames> getNickNamesFor(PhoneBook pb) {
//emf is an EntityManagerFactory reference
EntityManager em = emf.createEntityManager();
PhoneBook pb = em.merge(pb); //Heureka!
Collection<NickNames> nicks = pb.getNickNames();
em.close();
return nicks;
}
然后以这种方式获得该集合:
//I have a PhoneBook instance pb
//pb.getNickNames() returns null only
//I have a Persister instance pe
nicks = pe.getNickNames(pb);
就是这样。
你可以看看我在这个论坛上提到的关于这个话题的第二个问题。这是qustion OpenJPA - lazy fetching does not work。
答案 1 :(得分:0)
我将如何编写代码
@Entity
public class MyEntity {
@OneToMany(fetch = LAZY, mappedBy="someField")
private Set<AnotherEntities> entities;
// Constructor for JPA
// Fields aren't initalized here so that each em.load
// won't create unnecessary objects
private MyEntity() {}
// Factory method for the rest
// Have field initialization with default values here
public static MyEntity create() {
MyEntity e = new MyEntity();
e.entities = new Set<AnotherEntities>();
return e;
}
public Set<AnotherEntities> getEntities() {
return entities;
}
}
想法2:
我只是认为EAGER和LAZY获取中的操作顺序可能不同,即EAGER获取可能
entities
entities
的值(我假设null
)entities
设置为new Set<T>()
而LAZY可能
entities
设置为new Set<T>()
entities
的值(我假设null
)'也必须为此找到引用。
想法1:(不是正确答案)
如果你注释getter而不是字段怎么办?这应该指示JPA使用getter和setter而不是字段访问。
在Java Persistence API中,实体可以具有基于字段的或 基于属性的访问权限。在基于字段的访问中,持久性提供程序 直接通过其实例访问实体的状态 变量即可。在基于属性的访问中,持久性提供程序使用 用于访问实体的JavaBeans样式的get / set访问器方法 持久性。
来自The Java Persistence API - A Simpler Programming Model for Entity Persistence