开发者

What's the lazy strategy and how does it work?

开发者 https://www.devze.com 2023-03-23 13:59 出处:网络
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 t

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.


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.


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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号