ObjectDB ObjectDB

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

edit
delete
#2

Maybe trans is not a transaction of em.

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

ObjectDB Support
edit
delete
#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.

edit
delete
#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
edit
delete
#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()?

edit
delete
#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
edit
delete
#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?

edit
delete
#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
edit
delete
#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?

edit
delete
#10

Try: em.refresh(employee);

ObjectDB Support
edit
delete
#11

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

Thanks very much!

edit
delete

Reply

To post on this website please sign in.