Schema Update: class hierarchy change

#1

Hello,

we try to change the class hierarchy of an Entity.

You wrote www.objectdb.com/database/forum/899 that ObjectDB support automatic schema evolution for adding and removing fields and for class hierarchy change.

 

But our small test failed:

At first create a DataBase with one Object of MyEntity

package profileMigration.classhierachychange;

import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class Test {

public static void main(String[] args) {
  System.out.println("start");
  EntityManagerFactory emf = Persistence
    .createEntityManagerFactory("objectdb:$objectdb/db/test.tmp;drop");
  EntityManager em = emf.createEntityManager();

  // Persist an entity
  em.getTransaction().begin();
  MyEntity entity = new MyEntity();
  entity.id = "123";
  entity.name = "name123";
  entity.value = "value123";
  em.persist(entity);
  em.getTransaction().commit();
 
  em.getTransaction().begin();
  MyEntity entityWithID1 = em.find(MyEntity.class, 1);
  System.out.println(entityWithID1.id);
  System.out.println(entityWithID1.name);
  System.out.println(entityWithID1.value);
  em.getTransaction().commit();

  em.close();
  emf.close();
}

@Entity
public static class BaseEntity {
  @Basic
  String id;
}

@Entity
public static class NamedEntity extends BaseEntity {
  @Basic
  String name;
}

@Entity
public static class MyEntity extends NamedEntity {

//  @Basic
//  String name;
 
  @Basic
  String value;
}
}

Then we change the class hierarchy of MyEntity from NamedEntity to BaseEntity:

[Please note the changes in the Main method]

package profileMigration.classhierachychange;

import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public final class Test {

public static void main(String[] args) {
  System.out.println("start");
  EntityManagerFactory emf = Persistence
    .createEntityManagerFactory("objectdb:$objectdb/db/test.tmp");   //no drop - reuse the existing file
  EntityManager em = emf.createEntityManager();

  //don't create a new object!
 
//  // Persist an entity
//  em.getTransaction().begin();
//  MyEntity entity = new MyEntity();
//  entity.id = "123";
//  entity.name = "name123";
//  entity.value = "value123";
//  em.persist(entity);
//  em.getTransaction().commit();
 
  em.getTransaction().begin();
  MyEntity entityWithID1 = em.find(MyEntity.class, 1);
  System.out.println(entityWithID1.id);
  //System.out.println(entityWithID1.name);
  System.out.println(entityWithID1.value);
  em.getTransaction().commit();

  em.close();
  emf.close();
}

@Entity
public static class BaseEntity {
  @Basic
  String id;
}

@Entity
public static class NamedEntity extends BaseEntity {
  @Basic
  String name;
}

@Entity
public static class MyEntity extends BaseEntity {

//  @Basic
//  String name;
 
  @Basic
  String value;
}
}

 

I get the following exception:

Exception in thread "main" [ObjectDB 2.6.2_06] javax.persistence.PersistenceException
Failed to read the value of field field profileMigration.classhierachychange.Test$NamedEntity.name using reflection (error 363)
at com.objectdb.jpa.EMImpl.find(EMImpl.java:630)
at com.objectdb.jpa.EMImpl.find(EMImpl.java:545)
at profileMigration.classhierachychange.Test.main(Test.java:29)
Caused by: com.objectdb.o.UserException: Failed to read the value of field field profileMigration.classhierachychange.Test$NamedEntity.name using reflection
at com.objectdb.o.MSG.d(MSG.java:75)
at com.objectdb.o.UMR.P(UMR.java:934)
at com.objectdb.o.UMR.B(UMR.java:609)
at com.objectdb.o.UML.v(UML.java:549)
at com.objectdb.o.MMM.ah(MMM.java:1103)
at com.objectdb.o.UTY.aG(UTY.java:1331)
at com.objectdb.o.UTY.aF(UTY.java:1303)
at com.objectdb.o.ENH.b(ENH.java:102)
at com.objectdb.o.LDR.J(LDR.java:800)
at com.objectdb.o.LDR.U1(LDR.java:1031)
at com.objectdb.o.MST.aX(MST.java:544)
at com.objectdb.o.MST.aW(MST.java:471)
at com.objectdb.o.MST.U8(MST.java:442)
at com.objectdb.o.WRA.U8(WRA.java:266)
at com.objectdb.o.LDR.F(LDR.java:536)
at com.objectdb.o.LDR.E(LDR.java:470)
at com.objectdb.o.LDR.z(LDR.java:269)
at com.objectdb.o.OBC.aP(OBC.java:1058)
at com.objectdb.o.OBC.aN(OBC.java:976)
at com.objectdb.jpa.EMImpl.find(EMImpl.java:625)
... 2 more
Caused by: java.lang.IllegalArgumentException: Can not set java.lang.String field profileMigration.classhierachychange.Test$NamedEntity.name to profileMigration.classhierachychange.Test$MyEntity
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(Unknown Source)
at java.lang.reflect.Field.set(Unknown Source)
at com.objectdb.o.UMR$T.E(UMR.java:1072)
at com.objectdb.o.UMR$S.C(UMR.java:1015)
at com.objectdb.o.UMR.B(UMR.java:603)
... 19 more

 

If you can see in the modified Code, i had tried to move the field name from NameEntity to MyEntity. (This didn't works too)

 

But very strange:  It works after i changed the EntityModel again:

1) create the Database file

2) change the class hierarchy (now MyEnity extends BaseEntiy)  -> thows the exeption.

3) change the class hierarchy (add field name to MyEnity)  -> it works

or

1) create the Database file

2)  change the class hierarchy (now MyEnity extends BaseEntiy and add field name to MyEnity)-> thows the exeption.

3) change the class hierarchy (remove field name from MyEnity) -> it works (but we lost the content of field 'name')

But this can not be the right way.

#2

Thank you for this report. Please try build 2.6.3_02 that should fix this issue.

ObjectDB Support
#3

Thanks for the Update. Now it works.

How can data be saved for modified class hierarchy?

Actual it woks to move the attribute. The content of field name (NamedEntity) was moved to the new field name (MyEntity).

Is that a by ObjectDB supported way?

#4

Not sure what is exactly the question, could you please explain it further?

 

ObjectDB Support
#5

Suppose we have a change, in which a class is removed from the hierarchy.

How can we 'save' the contents of the fields from the class which was removed from the hierarchy.

As Example:

Given are the three classes BaseEntity, NamedEntity and MyEntity.

@Entity
public static class BaseEntity {
  @Basic
  String id;
}
@Entity
public static class NamedEntity extends BaseEntity {
  @Basic
  String name;
}
@Entity
public static class MyEntity extends NamedEntity {
  @Basic
  String value;
}

And now we change the class hierachy so that MyEntity extends BaseEntity and no more NamedEntity.

 

The question is:

How can we now access the contents of the field "name" of the entities which are stored in the database.

We have a solution. But we would like to know whether we can rely on this approach.

 

Our Solution is, that we add the field name to our BaseEntity:

@Entity
public static class MyEntity extends BaseEntity {
  @Basic
  String name;
  @Basic
  String value;
}
#6

Yes, your solution is correct since ObjectDB supports moving fields up and down in the class hierarchy as part of the automatic schema evolution. You can also move the name field to MyEntity if you wish, but it would be probably better to move it to BaseEntity if there are many other subclasses of NamedEntity. Note that in subclasses of BaseEntity that are not subclasses of NamedEntity the field will be initialized as null.

ObjectDB Support
#7

Perfect.

Thanks for clearing up.

Reply