Attempt to store an instance of a non persistable type

#1

I can't understand why I get the following error on one machine, but NOT on another machine (same code, but somehow different environment of course):

Caused by: com.objectdb.o._PersistenceException: Attempt to store an instance of a non persistable type technology.tavla.common.device.data.log.v2.Log - field technology.tavla.server.runtime.common.data.device.Device.logs at com.objectdb.o._PersistenceException.a(_PersistenceException.java:47) at com.objectdb.o.JPE.d(JPE.java:147) at com.objectdb.o.ERR.h(ERR.java:56) at com.objectdb.o.OBC.onObjectDBError(OBC.java:1591) at com.objectdb.jpa.EMImpl.merge(EMImpl.java:505) at technology.tavla.misc.data.DefaultDataManager$DefaultEntityManager.merge(DefaultDataManager.java:214) ... 35 common frames omitted Caused by: com.objectdb.o.UserException: Attempt to store an instance of a non persistable type technology.tavla.common.device.data.log.v2.Log - field technology.tavla.server.runtime.common.data.device.Device.logs at com.objectdb.o.MSG.a(MSG.java:64) at com.objectdb.o.TYW.writeElement(TYW.java:274) at com.objectdb.o.ABT.writeArray(ABT.java:587) at com.objectdb.o.CLT.writeStrictly(CLT.java:208) at com.objectdb.o.TYW.ab(TYW.java:455) at com.objectdb.o.TYW.aa(TYW.java:506) at com.objectdb.o.TYW.writeElement(TYW.java:328) at com.objectdb.o.UMR$j.G(UMR.java:1011) at com.objectdb.o.UMR.YL(UMR.java:575) at com.objectdb.o.UML.YL(UML.java:538) at com.objectdb.o.MMM.M(MMM.java:1217) at com.objectdb.o.UTY.Y(UTY.java:1413) at com.objectdb.o.EMR.i(EMR.java:272) at com.objectdb.o.EMR.g(EMR.java:81) at com.objectdb.jpa.EMImpl.merge(EMImpl.java:502) ... 36 common frames omitted

-----

package technology.tavla.common.device.data.log.v2;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.UUID;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import com.google.common.collect.ComparisonChain;
import com.owlike.genson.annotation.JsonIgnore;
import com.owlike.genson.annotation.JsonProperty;

import technology.tavla.common.device.data.log.v2.exception.Exception;

@Entity
public class Log implements Serializable,Comparable<Log> {
    
    private static final long serialVersionUID = 7417072929572142416L;
    
    public enum Level {
        
        TRACE,
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        AUDIT;
        
        @Override
        public String toString() {
            return this.name();
        }
        
    }
    
    @Id @GeneratedValue @JsonIgnore
    private long id;
    
    @JsonProperty
    private String uuid = null;
    
    @JsonProperty
    private LocalDateTime dateTime = null;
    
    @Enumerated(EnumType.STRING) @JsonProperty
    private Level level = null;
    
    @JsonProperty
    private String message = null;
    
    @Embedded @JsonProperty
    private Exception exception = null;
    
    private Log(final Builder builder) {
        this.uuid = builder.uuid;
        this.dateTime = builder.dateTime;
        this.level = builder.level;
        this.message = builder.message;
        this.exception = builder.exception;
    }
    
    public Log(@JsonProperty("uuid") String uuid, @JsonProperty("dateTime") LocalDateTime dateTime, @JsonProperty("level") Level level, @JsonProperty("message") String message, @JsonProperty("exception") Exception exception) {
        this.uuid = uuid;
        this.dateTime = dateTime;
        this.level = level;
        this.message = message;
        this.exception = exception;
    }
    
    public long getId() {
        return this.id;
    }
    
    public String getUuid() {
        return this.uuid;
    }
    
    public LocalDateTime getDateTime() {
        return this.dateTime;
    }

    public Level getLevel() {
        return this.level;
    }

    public String getMessage() {
        return this.message;
    }
    
    public Exception getException() {
        return this.exception;
    }
    
    @Override
    public int compareTo(Log log) {
        return ComparisonChain.start().compare(this.uuid, log.getUuid()).result();
    }
    
    @Override
    public String toString() {
        return this.dateTime + " " + this.level + " " + this.message;
    }
    
    @Override
    public boolean equals(java.lang.Object object) {
        if(this == object) return true;
        
        if(object == null) return false;
        
        if(this.getClass() != object.getClass()) return false;
        
        return Objects.equals(this.getUuid(), this.getClass().cast(object).getUuid());
    }
    
    @Override
    public int hashCode() {
        return Objects.hashCode(this.getUuid());
    }
    
}
#2

Somehow this Log class is not identified as an entity class.

Maybe somewhere in the classpath there is an additional version of this class, without @Entity?

Is it an issue only with this specific class? Has it been changed recently?

It may also be related to other class loading issues, e.g. loading JPA types from 2 different class loaders.

ObjectDB Support
#3

I already tried context classpath and added some additional imports (OSGi). However, as it is working for one platform (there the launcher is slightly different), could it be that the schema of the other database has some old field for "logs"? Or something like this...

Does the message mean exactly that @Entity is missing? So it cannot read-out the annotations maybe?

#4

Yes, it is possible that there is some issue with the schema in that database. However, if the entity class is available in the classpath when you open the EntityManagerFactory the schema is expected to be updated according to the entity class, so maybe there is some issue in locating the entity class in the classpath.

ObjectDB Support
#5

Why don't you throw exact messages? If the class is not found, it is a ClassNotFoundException...this is just guessing now!

#6

Actually this error message is for situations in which a user forgot to define an entity class as such or added a reference from an entity by mistake to an object that is not intended to be an entity. These probably cover more than 99% of the occasions in which this exception is thrown, and in these cases the exception is clear.

The situation in your report is very rare and was not expected. Obviously this error message is frustrating and a better message should be produced. However, unfortunately first the cause has to be isolated and understood. Because when the exception is thrown the available information is already very limited. The class has already been identified as non-persistable when the database was opened. This is not considered as an abnormal situation as most classes are non-persistable.

ObjectDB does not require access to the entity classes. Therefore, ClassNotFoundException is not thrown. This is useful for example for the ObjectDB Server and the ObjectDB Explorer that can work without classes, and also in other cases. When classes are not found ObjectDB creates synthetic classes based on the schema in the database. This is something that you can track by enabling logging:

<logger name="type.loader" level="debug" />

Look for messages "Building a synthetic Class for" ...  to see if classes were missing in your case.

Another thing that you may want to try (if the classes are found) is adding a dummy field to the class. Maybe it will help in updating the schema, if it is somehow damaged in a specific database (the field can be deleted later).

ObjectDB Support
#7

It was about class loading.

Reply