find() delay

#1

Hi, I tried objectdb in following steps:

1, for 1 to 30 {trans.begin(); create typeC o; em.persist(o); trans.commit();}

2, for 1 to 30 {p = em.find(typeC.class, i); print(p)}

So simple a test, there is no result in one go; I have to disable step 1 and run again, then Step 2 output the expected results.

I tried em.flush(), em.clear(), etc.. that do not work.

Any idea? TIA

#2

Maybe trans is not a transaction of em.

Maybe different EntityManager instances are used and the L2 cache is enabled.

ObjectDB Support
#3

L2 cache? I'm not sure. But why the second find() will output?

By the way, a query "select e from nodes e" will be ok regardless of the first time or the second time.

My codes, again, are similar to these:

1. trans = em.getTransaction(); trans.begin(); create typeC o; em.persist(o); trans.commit();

2. Query q =  em.createQuery("select e from typeC e"); List l = q.getResultList(); // this works

3. typeC p = em.find(typeC.class, i);  // this p will be null

I'm doubting the example in jpab.jar runs twice in each test and choose only the second result, maybe is related to this.

#4

The following program demonstrates how it works:

import javax.persistence.*;

public final class Test {
    public static void main(String[] args){

        EntityManagerFactory emf = Persistence.createEntityManagerFactory(
            "$objectdb/db/test.odb");
        EntityManager em = emf.createEntityManager();
      
        for (int i = 0; i < 30; i++) {
            em.getTransaction().begin();
            em.persist(new MyEntity());
            em.getTransaction().commit();
        }

        for (int i = 0; i < 30; i++) {
            System.out.println(em.find(MyEntity.class, i));
        }
        em.close();
        emf.close();
    }
  
    @Entity
    public static final class MyEntity {
        @Id @GeneratedValue long id;
    }
}

Try to change this example to demonstrate the issue that you have.

Please try to generate a simple test like the one above for any other problem that you may have. Otherwise it is very difficult to understand the problem and provide help.

ObjectDB Support
#5

Ok, the minimal codes are here:

public final class Test {
 public static void main(String[] args){

  EntityManagerFactory emf = Persistence.createEntityManagerFactory("$objectdb/db/test.odb");
  EntityManager em = emf.createEntityManager();
  EntityTransaction trans = em.getTransaction();
  int iBlock = 3;
  //* codes-1
  for (int i = 1; i <= 3*iBlock; i++) {
   trans.begin();
   Node o = new Node();
   Node p = em.find(Node.class, i/3);
   if (null==p) System.out.printf("Node %d has no parent.%n", i);
   o.setParent( p );
   em.persist(o);
   trans.commit();
  }//*/
 
  /* codes-2.
  em.close();
  em = emf.createEntityManager();
  //*/
  em.clear(); // codes-3
  trans = em.getTransaction();
  int iSum = 0;
  for (int i=1;i<=3*iBlock;i++){
   Node o = em.find(Node.class, i);
   if (null== o){
    System.out.printf("%s is not Node %n", o);
    continue;
   }
   List<Node> sons = o.getSons();
   if (null==sons){
    System.out.printf("%s has no sons %n", sons);
    continue;
   }
   int iCnt = sons.size();
   System.out.printf("%d : type:Node, sons:%d %n",i, iCnt);
   iSum += iCnt;
   Iterator<Node> it = sons.iterator();
   while (it.hasNext()){
    Node en = it.next();
    System.out.printf("child : %d %n", en.getId());
   }
  }
  System.out.printf("The sum : %d %n", iSum);
  em.close();
  emf.close();
 }
 @Entity
 static class Node {
  @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
  private int id;
  @ManyToOne @JoinColumn(name="pid")
  protected Node parent;

  @OneToMany(mappedBy="parent",fetch=FetchType.EAGER)
  protected List<Node> Sons;

  public int getId() {
   return id;
  }
  public Node getParent() {
   return parent;
  }

  public List<Node> getSons() {
   return Sons;
  }

  public void setParent(Node parent) {
   this.parent = parent;
  }
 }
}

The ideal result is here:

Node 1 has no parent.
Node 2 has no parent.
1 : type:Node, sons:3
child : 3
child : 4
child : 5
2 : type:Node, sons:3
child : 6
child : 7
child : 8
3 : type:Node, sons:1
child : 9
4 : type:Node, sons:0
5 : type:Node, sons:0
6 : type:Node, sons:0
7 : type:Node, sons:0
8 : type:Node, sons:0
9 : type:Node, sons:0
The sum : 7

The bad result is here:

Node 1 has no parent.
Node 2 has no parent.
null has no sons
null has no sons
null has no sons
null has no sons
null has no sons
null has no sons
null has no sons
null has no sons
null has no sons
The sum : 0

I tried the "codes-2" or "codes-3", they work with "codes-1" to output the ideal result.

Only "codes-1" will output the bad result.

When the "codes-1" is comment in the second run, the ideal result appears.

So, is there any reasonable method than .clear()?

#6

OK. Now I see the problem.

You have a bidirectional relationship but you update only one side.

It is the application responsibility to update both sides. Otherwise the "mapped by" side will only be available when the object that contains that "mapped by" (inverse) field is retrieved from the database again (as done by "codes-2" and "codes-3").

Please see the documentation on inverse fields and also this forum thread.

ObjectDB Support
#7

Thanks for you hints.

I read this forum thread and find the active method you mentioned. If I have to maintain the inverse fields, then is there a conflict between manual and auto relations? Suppose the Employee and the address are all regular classes, I changed the codes you given in Eml's post:

  em.getTransaction().begin();

  Employee employee = new Employee();
  employee.name = "Employee1";
  employee.addresses = new ArrayList<Address>();

  Address address1 = new Address();
  address1.line = "address1";
  address1.employee = employee;
  em.persist(address1);                    // changed
  employee.addresses.add(address1);

  Address address2 = new Address();
  address2.line = "address2";
  address2.employee = employee;
  em.persist(address2);                    // changed
  // employee.addresses.add(address2);     // changed

  em.persist(employee);
  em.getTransaction().commit();

My address List only add once, but the class Address exactly have two many2one relations to the same employee. When the third application visit the data, how many addresses for the employee?

#8

employee.addresses will have exactly the Address instances that you added to it manually - until it is retrieved from the database (e.g. using another EntityManager, after clear, etc.) and then the automatic inverse query is run and the list is built according to the owner side (address.employee).

ObjectDB Support
#9

Thanks.

If there is a large number of objects in cache, the List.add() make sons for a node, how to make it appear in database instancely without clear() or destroying the cache?

#10

Try: em.refresh(employee);

ObjectDB Support
#11

Ok, finally, the refresh() is the bingo.

Thanks very much!

Reply