什么是懒惰的策略,它是如何工作的?

时间:2011-07-29 06:24:25

标签: unit-testing jpa one-to-many fetch openjpa

我有问题。我正在学习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,部分缓存和分离。)但是在编写手册时,集合有时(更像很多次)是空的,但不是空的。

2 个答案:

答案 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获取可能

  1. 声明字段entities
  2. 获取entities的值(我假设null
  3. entities设置为new Set<T>()
  4. 而LAZY可能

    1. 声明字段`entities
    2. entities设置为new Set<T>()
    3. 获取entities的值(我假设null)'
    4. 也必须为此找到引用。

      想法1:(不是正确答案)

      如果你注释getter而不是字段怎么办?这应该指示JPA使用getter和setter而不是字段访问。

        

      在Java Persistence API中,实体可以具有基于字段的或   基于属性的访问权限。在基于字段的访问中,持久性提供程序   直接通过其实例访问实体的状态   变量即可。在基于属性的访问中,持久性提供程序使用   用于访问实体的JavaBeans样式的get / set访问器方法   持久性。

      来自The Java Persistence API - A Simpler Programming Model for Entity Persistence