Database Inconsistency or corruption

#1

Hi,

since a week I have been trying to find a solution to a strange problem.

We are using ObjectDB 2.6.8_02 im embedded mode.

The problem is that data gets lost when updating an entity.

1-) Background

Consider Three entities: ObjectValue, Attribute and Value.

ObjectValue has a one to many relationship to Attribute and Attribute has a one to one relationship to Value.

All relationships are annoted with Cascade.ALL. That is, when merging an ObjectValue, the merge operation is propagated to the referenced Attributes and from the Attributes to the Values.

Updating an ObjectValue means updating the referenced Attributes.

Updating an Attribute means replacing a referenced Value with a new Value entity.

2-) Problem Description

I initialized the database with some ObjectValues. Persisting new ObjectValues (with new Attributes and new Values)

works fine. But, update does not work. The data gets lost. I attached a formatted log file.

In the following, I consider the most important part of the log file. Before the update operation,

I dump the properties of the object I want to update.

ObjectValue ID:=100, TYPE:=Person, REVISION:=0, isPersisted:=true, LockMode:=PESSIMISTIC_WRITE]]

...

Attribute  ID:=1400, Name:=profileName, Value:=ID:=1400, PrimitiveValue:=ProfileName 99, REVISION:=0, isPersisted:=true

...

After the update Operation I also dump the properties of the object.

ObjectValue ID:=100, TYPE:=Person, REVISION:=2, isPersisted:=true, LockMode:=PESSIMISTIC_WRITE]]

...

Attribute  ID:=1400, Name:=profileName, Value:=ID:=null, PrimitiveValue:=ProfileName 99 (Just a test update...), REVISION:=1, isPersisted:=true

...

As you can see, I changed from "ProfileName 99" to "ProfileName 99 (Just a test update...)"

The id of the new Value object is null "Value:=ID:=null". This is the first strange thing. Because, after persisting

an object, it should have an id!!!

When retrieving the ObjectValue from the database, all values are lost.

ObjectValue ID:=100, TYPE:=Person, REVISION:=2, isPersisted:=true, LockMode:=null

...

Attribute  ID:=1400, Name:=null, Value:=null, REVISION:=null, isPersisted:=false

...

To confirm that the values are lost, I started the explorer and searched for the ObjectValue with revision 2

( See attached picture). Indeed, all values are lost, not just the values of the updated attribute.

3-) What I did

I thought first, that ObjectDB has a problem with cascade, so I persisted manually all referenced Attributes and the

corresponding values. But I still got the same problem.

I tried several other alternatives. But it was useless.

Here is the code:

@Override
  public ObjectValue updateObjectValue(ObjectValue ov, ObjectValue... ovList) {
    // getting transaction
    System.out.println("Trying to get transaction " + ov.getId());
   
    // edit all locked objects
    System.out.println("Trying to merge first object " + em.getLockMode(ov));
    /*
     * Trying to persist manually does not work.
    for(Attribute attr: ov.getAttributes()){
      if(em.contains(attr.getValue())){
        em.merge(attr.getValue());
      }else{
        em.persist(attr.getValue());
      }
      em.flush();
      if(em.contains(attr)){
        em.merge(attr);
      }else{
        em.persist(attr);
      }
      em.flush();
    }
    */
    ObjectValue persistedObjectValue = em.merge(ov);
    persistedObjectValue.setRevision(persistedObjectValue.getRevision() + 1);
    System.out.println("Trying to commit");
    em.flush();
   
    System.out.println("After flush " + em.getLockMode(ov));
    persistedObjectValue.setRevision(persistedObjectValue.getRevision() + 1);
    return persistedObjectValue;
  }

Did someone already faced a similar problem ?

Any hint is welcome ?

 

 

 

 

 

#2

Your report does not include the definition of the relevant entity classes, but please note that when using a bidirectional relationship it is the application responsibility (according to the JPA specification) to update both sides of the relationship, and according to your description you may have updated only one direction.

In practice, if you update the owner side of the relationship it should be sufficient, since you will get the update applied on the database, and after loading the up to date data from the database (e.g. by using refresh) both sides will reflect the new relationship. However, if you only update the mapped by side (usually the collection side) - updates are not applied.

ObjectDB Support
#3

Hi,

thank you for your quick reply.

We don't have bidirectional relationships at all, only uni-directional.

Here is a simplified definition of the relevant entity classes:

 

@javax.persistence.Entity
@javax.persistence.Table(name = "ObjectValues", schema = "dex_01")
@javax.persistence.NamedQueries({ /* queries */ })
public class ObjectValue implements metamodel.IObjectValue {

  @javax.persistence.Id
  @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.TABLE, generator = "objectValueGen")
  @javax.persistence.TableGenerator(name = "objectValueGen", table = "IdValues", schema = "dex_01", pkColumnName = "idName", valueColumnName = "idValue", pkColumnValue = "objectValueId", allocationSize = 1)
  protected Long id;

  private String type;
  private Long revision;

  protected boolean markedDeleted = false;
  @ManyToOne
  private Project project;

  @javax.persistence.OneToMany(cascade=javax.persistence.CascadeType.ALL, fetch =   javax.persistence.FetchType.EAGER, orphanRemoval = true)
  @javax.persistence.JoinTable(name = "ObjectValueAttributes", schema = "dex_01", joinColumns = @javax.persistence.JoinColumn(name = "objectValue", referencedColumnName = "id"), inverseJoinColumns = @javax.persistence.JoinColumn(name = "attributes", referencedColumnName = "id"))
  private java.util.Set<metamodel.Attribute> attributes = new java.util.HashSet<metamodel.Attribute>();


  public ObjectValue() {
    super();
  }
  public ObjectValue(
      String type,
      Long revision,
      Project project,
      Set<metamodel.Attribute> attributes,
      Long id,
      boolean markedDeleted) {
    this.type = type;
    this.revision = revision;
    this.project = project;
    this.attributes = attributes;
    this.id = id;
    this.markedDeleted = markedDeleted;
  }
 

  /* Getters and Setters */
}


@javax.persistence.Entity
@javax.persistence.Table(name="Attributes", schema="dex_01")
@javax.persistence.NamedQueries({  /*  queries */})
public class Attribute implements metamodel.IAttribute {

  @javax.persistence.Id
  @javax.persistence.GeneratedValue(strategy=javax.persistence.GenerationType.TABLE, generator="attributeGen")
  @javax.persistence.TableGenerator(name="attributeGen", table="IdValues", schema="dex_01", pkColumnName="idName", valueColumnName="idValue", pkColumnValue="attributeId", allocationSize=1)
  protected Long id;

  private String name;
  private Long revision;

  @javax.persistence.OneToOne(
     cascade=javax.persistence.CascadeType.ALL,
     fetch=javax.persistence.FetchType.EAGER)
   @javax.persistence.JoinColumn(name="value")
  private metamodel.Value value;

 public Attribute() {
    super();
  }
   /* Getter and Setters */
}


@javax.persistence.Entity
@javax.persistence.Table(name = "Value", schema = "dex_01")
@javax.persistence.Inheritance(strategy = javax.persistence.InheritanceType.JOINED)
@javax.persistence.NamedQueries({  /* queries */ })
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING, length = 20)
@DiscriminatorValue("Value")
@XmlSeeAlso({ CollectionValue.class, PrimitiveValue.class, ReferenceValue.class })
public abstract class Value implements Serializable, metamodel.IValue {
  @javax.persistence.Id
  @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.TABLE, generator = "valueGen")
  @javax.persistence.TableGenerator(name = "valueGen", table = "IdValues", schema = "dex_01", pkColumnName = "idName", valueColumnName = "idValue", pkColumnValue = "valueId", allocationSize = 1)
  protected Long id;

  private static final long serialVersionUID = 1L;

  public Value() {
  }

  /* Getters and Setters */
}

#4

For further investigation of this issue please post a minimal console test case that demonstrates it in the format specified on the posting instructions.

ObjectDB Support
#5

Hi,

providing a minimal console test is not easy because we have a quite complex environment.

It is not easy to reproduce the problem.

I started yet ObjectDB in Client-Server mode, so that I have the possibility to query the database using the Explorer while the application is running.

But I am missing a feature. It would be really great to see all queries which are currently processed by ObjectDB.

Like PostgresQL which has a view that shows all queries that the DB is currently executing.

As an alternative, it would be nice to see the traces of all EntityManagers.

(Considering my first post) It seems that all Attributes of the updated ObjectValue are just deleted from the database.

If I had detailled traces of the EntityManager or of ObjectDB, I could identified when the delete operation is executed!

There is still something I don't understand. After persisting the changes, the Id of the new Value entity was still nulll

(See #1). How can this be ? It is true that the new value entity is sent by the client via a Webservice interface.

So the object is actually created on a different JVM. May be it is the source of the problem. What do you think ?

 

 

#6

> But I am missing a feature. It would be really great to see all queries which are currently processed by ObjectDB.

You can enable logging.

> There is still something I don't understand. After persisting the changes, the Id of the new Value entity was still nulll

This is normal. You can choose an ID allocation strategy that will make IDs available on persist, or flush after persist to get IDs allocated.

It is unclear why your updates are not committed. Here is a list of possible reasons:

  1. Changing objects with no active transaction.
  2. Updating objects in a transaction but with no commit.
  3. Partial enhancement.
  4. Using enhanced classes but updating objects using reflection.
  5. Updating only the "mapped by" side of a bidirectional relationship.

If an update is not detected (e.g. due to 3 or 4) - you may try changing another (even dummy) field in the object and see if it makes a change.

There may be of course other reasons. There is no open issue in ObjectDB (and there was none in the last years) that may cause this issue. Although not easy, isolating the issue in your big project may be needed.

ObjectDB Support

Reply