javajpaeclipselinkentitygraph

JPA: EntityGraph and map


I use EclipseLink 2.6.3 as JPA provider. I have two entities:

@Entity
public class ClassA{

    @Id
    private String uuid;

    private String comment;

    @OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL,mappedBy = "classA",orphanRemoval = false)
    @MapKey(name="code")
    private Map<String,ClassB> texts;
    //+ getters and setters
}

@Entity
public class ClassB {

    @Id
    private String uuid;

    private String code;

    private String name;

     @ManyToOne
    @JoinColumn(name = "comeColname")
    private ClassA classA;

   //+getters and setters
}

When I load ClassA entity without EntityGraph everything works well. However, if I do the following code:

String queryString="SELECT a FROM ClassA a WHERE a.uuid='dj000000001111111111a1'";
EntityGraph<ClassA> eg = em.createEntityGraph(ClassA.class);
eg.addAttributeNodes(new String[]{"uuid","comment"});
eg.addSubgraph("texts").addAttributeNodes(new String[]{"uuid","code","name"});
Query query = em.createQuery(queryString);
query.setHint("javax.persistence.fetchgraph", eg);
List<ClassA> items=query.getResultList();
em.close();

I get the following exception:

[EL Warning]: 2016-06-10 13:40:20.093--ServerSession(1266534280)--java.lang.ClassCastException: 
    java.util.Hashtable cannot be cast to org.eclipse.persistence.queries.FetchGroupTracker
    at org.eclipse.persistence.descriptors.FetchGroupManager.getObjectFetchGroup(FetchGroupManager.java:695)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.load(ObjectBuilder.java:778)
    at org.eclipse.persistence.internal.sessions.AbstractSession.load(AbstractSession.java:5189)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1192)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
    at org.eclipse.persistence.queries.ReadAllQuery.execute(ReadAllQuery.java:460)
    at org.eclipse.persistence.internal.sessions.AbstractSession.internalExecuteQuery(AbstractSession.java:3271)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
    at org.eclipse.persistence.internal.indirection.NoIndirectionPolicy.valueFromQuery(NoIndirectionPolicy.java:326)
    at org.eclipse.persistence.mappings.ForeignReferenceMapping.valueFromRowInternal(ForeignReferenceMapping.java:2334)
    at org.eclipse.persistence.mappings.ForeignReferenceMapping.valueFromRow(ForeignReferenceMapping.java:2178)
    at org.eclipse.persistence.mappings.ForeignReferenceMapping.readFromRowIntoObject(ForeignReferenceMapping.java:1505)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildAttributesIntoObject(ObjectBuilder.java:462)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:1005)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildWorkingCopyCloneNormally(ObjectBuilder.java:899)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObjectInUnitOfWork(ObjectBuilder.java:852)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:735)
    at org.eclipse.persistence.internal.descriptors.ObjectBuilder.buildObject(ObjectBuilder.java:689)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.buildObject(ObjectLevelReadQuery.java:805)
    at org.eclipse.persistence.queries.ReadObjectQuery.registerResultInUnitOfWork(ReadObjectQuery.java:895)
    at org.eclipse.persistence.queries.ReadObjectQuery.executeObjectLevelReadQuery(ReadObjectQuery.java:562)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeDatabaseQuery(ObjectLevelReadQuery.java:1175)
    at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:904)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.execute(ObjectLevelReadQuery.java:1134)
    at org.eclipse.persistence.queries.ReadObjectQuery.execute(ReadObjectQuery.java:441)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.executeInUnitOfWork(ObjectLevelReadQuery.java:1222)
    at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1857)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1839)
    at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
    at org.eclipse.persistence.internal.jpa.QueryImpl.executeReadQuery(QueryImpl.java:258)
    at org.eclipse.persistence.internal.jpa.QueryImpl.getResultList(QueryImpl.java:465)

How to fix it? Is this a bug or I do something wrong?


Solution

  • Disclaimer - I am not EclipseLink developer and I may be wrong.

    As I found out the problem is that Map is not considered as collection in EntityGraph. So I added some code to org.eclipse.persistence.internal.sessions.AbstractSession as a temp workaround for my problem:

    public void load(Object objectOrCollection, AttributeGroup group, ClassDescriptor referenceDescriptor, boolean fromFetchGroup) {
      if (objectOrCollection == null || group == null) {
           return;
       }       
       if (objectOrCollection instanceof Collection) {
           Iterator iterator = ((Collection)objectOrCollection).iterator();
           while (iterator.hasNext()) {
               load(iterator.next(), group, referenceDescriptor, fromFetchGroup);
           }
       }//MY CODE STARTS 
        else if(objectOrCollection instanceof Map){
            Map<Object, Object> map = (Map)objectOrCollection;
            Iterator<Map.Entry<Object, Object>> it = map.entrySet().iterator();
            while (it.hasNext()) {
              Map.Entry<Object, Object> entry = it.next();
              load(entry.getValue(), group, referenceDescriptor, fromFetchGroup);
            }
       }//MY CODE ENDS
       else {
           ClassDescriptor concreteDescriptor = referenceDescriptor;
           if (concreteDescriptor.hasInheritance() && !objectOrCollection.getClass().equals(concreteDescriptor.getJavaClass())){
               concreteDescriptor = concreteDescriptor.getInheritancePolicy().getDescriptor(objectOrCollection.getClass());
           }
           AttributeGroup concreteGroup = group.findGroup(concreteDescriptor);
           concreteDescriptor.getObjectBuilder().load(objectOrCollection, concreteGroup, this, fromFetchGroup);
       }
    

    }