Hi Paul,

Thanks for following up on this.

On Apr 28, 2009, at 11:19 PM, Paul Copeland wrote:

Here is an OpenJPA 1.2.1 example that demonstrates the "OpenJPA replaces a collection field" problem. This may not be technically a bug but it can be a problem for the unaware user.

The JPA specification is silent on this issue.

However, the JDO specification requires that the collection field be replaced at makePersistent (not flush). Since OpenJPA is based on Kodo, which implemented JDO, this behavior is historic.

The reason for replacing the user's collection is to be able to track changes made to the collection after makePersistent and before commit. The reason to do it at makePersistent is so the user knows when it was done.

This happens with a new persisted object with a collection field that is initially null. If the null collection is initialized to an empty collection it will correctly register additions until it is flushed. Then the collection field is replaced and the old collection is stale.



In this example the flush is an explicit call. But the same problem happens when the object is implicitly flushed to get the generated identity for a foreign key.

This is more interesting. Does the implicit flush not happen at commit or user flush?

Craig


In the main program the behavior changes when "em.flush()" is commented out.

================= LazyLoadTest.java ==============
package com.jpatest;

import javax.persistence.*;
import java.util.*;

import com.jpatest.persistence.*;

public class LazyLoadTest
{
  public static void main(String args[])
      throws Throwable
  {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpatest");
      EntityManager em = emf.createEntityManager();
      em.getTransaction().begin();
      ListHolder holder = new ListHolder();
      em.persist(holder);
      List<ListMember> listReference = holder.getMembers();
      em.persist(new ListMember(holder));
System.err.println("id="+holder.id+" size[expect=1]="+listReference.size());

      em.persist(new ListMember(holder));
System.err.println("id="+holder.id+" size[expect=2]="+listReference.size());

      em.flush(); // **** this causes problem when uncommented
      em.persist(new ListMember(holder));
System.err.println("id="+holder.id+" size[expect=3]="+listReference.size());

      em.getTransaction().commit();
      em.close();
  }
}


================= ListHolder.java ==============
package com.jpatest.persistence;

import java.util.*;
import javax.persistence.*;

@Entity
@Table (name="list_holder")
public class ListHolder
  implements java.io.Serializable
{
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  @Id public long id;
  @Version private long version;

  @OneToMany(mappedBy="holder", fetch=FetchType.LAZY,
             cascade={CascadeType.PERSIST,CascadeType.REMOVE})
  private List<ListMember> members;

  public ListHolder() {}

  public List<ListMember> getMembers()
  {
      if (members == null) {
          System.err.println("---- ListHolder members == null");
          members = new ArrayList<ListMember>();
      }
      return members;
  }
}

================= ListMember.java ==============
package com.jpatest.persistence;

import javax.persistence.*;

@Entity
@Table (name="list_member")
public class ListMember
  implements java.io.Serializable
{
  @GeneratedValue(strategy=GenerationType.IDENTITY)
  @Id private long id;
  @Version private long version;

@ManyToOne (optional=false,fetch=FetchType.LAZY,cascade=CascadeType.ALL)
  private ListHolder holder;

  protected ListMember() {}
  public ListMember(ListHolder holder)
  {
      this.holder = holder;
      holder.getMembers().add(this);
  }
}



Craig L Russell
Architect, Sun Java Enterprise System http://db.apache.org/jdo
408 276-5638 mailto:[email protected]
P.S. A good JDO? O, Gasp!

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to