什么是惰性策略以及它是如何工作的?

发布于 2024-11-27 01:43:09 字数 2674 浏览 0 评论 0原文

我有一个问题。我正在学习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 技术交流群。

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

发布评论

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

评论(2

东京女 2024-12-04 01:43:10

问题出在实体的生命周期中。 (Geronimo 使用 OpenJPA,所以不要查看 OpenJPA 教程的 实体生命周期管理。)该应用程序使用容器管理的事务。 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);

仅此而已。

您可以查看我在本论坛上提出的关于该主题的第二个问题。这是问题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 the EntityManager.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 the Persister class. (The comment Heureka marks the critical line re-attaching the entity.)

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;
}

The collection is then obtained in this way:

//I have a PhoneBook instance pb
//pb.getNickNames() returns null only
//I have a Persister instance pe
nicks = pe.getNickNames(pb);

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.

海风掠过北极光 2024-12-04 01:43:10

我将如何编写代码

@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< /code>
  2. 获取 entities 的值(我假设 null
  3. entities 的值设置为 new Set()

而 LAZY 可以

  1. 声明字段 `entities
  2. entities 的值设置为 new Set()
  3. 获取 entities 的值(我假设null)'

也必须找到对此的引用。

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

如果您注释 getter 而不是字段会怎样?这应该指示 JPA 使用 getter 和 setter,而不是字段访问。

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


来自Java 持久性 API - 更简单的实体持久性编程模型

How I would write the code

@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;
  }

}

Idea no 2:

I just thought that the order of operations in EAGER and LAZY fetching may differ i.e. EAGER fetching may

  1. Declare field entities
  2. Fetch value for entities (I'd assume null)
  3. Set value of entities to new Set<T>()

while LAZY may

  1. Declare field `entities
  2. set value of entities to new Set<T>()
  3. Fetch value for entities (I'd assume null)'

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.

In the Java Persistence API, an entity can have field-based or
property-based access. In field-based access, the persistence provider
accesses the state of the entity directly through its instance
variables
. In property-based access, the persistence provider uses
JavaBeans-style get/set accessor methods
to access the entity's
persistent properties.

From The Java Persistence API - A Simpler Programming Model for Entity Persistence

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