0
votes

I'm facing a problem with JOIN FETCH and EAGER relations.

I have the following entity relations:

Entity A extends abstract entity E.

Abstract entity E has a oneToOne bidirectional relation with Entity C with EAGER fetch type.

Entity A has oneToMany relation with Entity B.

Entity B extends abstract entity E (so have oneToOne relation too with C)

Entity C has inverse relation to abstract entity E

While executing a simple namedQuery like

SELECT a FROM A a WHERE a.key = :key

Where parameter 'key' is String type, then i have no problem. Accessing to sub entities B from retrieved entity A execute sub requests as its supposed to be.

But if i add a JOIN FETCH to my namedQuery:

SELECT a FROM A a JOIN FETCH a.entitiesB WHERE a.key = :key

i obtain the following error stack trace:

javax.ejb.EJBTransactionRolledbackException: The transaction has been marked rollback only because the bean encountered a non-application exception :javax.ejb.EJBTransactionRolledbackException : The transaction has been marked rollback only because the bean encountered a non-application exception :org.apache.openjpa.persistence.ArgumentException : The specified parameter of type "class org.apache.openjpa.util.LongId" is not a valid query parameter.
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.convertException(BaseEjbProxyHandler.java:345)
at org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke(BaseEjbProxyHandler.java:283)
... 
Caused by: <openjpa-2.2.0-r422266:1244990 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: The specified parameter of type "class org.apache.openjpa.util.LongId" is not a valid query parameter.
FailedObject: SELECT relation FROM Relation relation JOIN FETCH relation.accounts WHERE relation.key = :key [java.lang.String]
at org.apache.openjpa.jdbc.sql.DBDictionary.setUnknown(DBDictionary.java:1458)
at org.apache.openjpa.jdbc.sql.SQLBuffer.setParameters(SQLBuffer.java:544)
at org.apache.openjpa.jdbc.sql.SQLBuffer.prepareStatement(SQLBuffer.java:453)
at org.apache.openjpa.jdbc.sql.SQLBuffer.prepareStatement(SQLBuffer.java:429)
at org.apache.openjpa.jdbc.sql.SelectImpl.prepareStatement(SelectImpl.java:479)
... 

If i change the EAGER relations to LAZY, i do not have this error anymore. So, what's the problem ?

Edit 1: I get the same error if i keep oneToOne relations with LAZY fetch type and add JOIN FETCH a.entityC directly on the namedQuery

Edit 2: Removing relation oneToOne with entity C from abstractEntity class and adding this relation directly into EntityA and EntityB (plus modifying entity C to have reverse relation with A & B), still keeping this oneToOne relation as EAGER => No problem. So it looks like the problem come from having this shared relation into the abstract entity class, but why?

This is code example illustrated the problem.

Entity A

@Entity
@Table(name = "TABLE_A")
@Access(AccessType.FIELD)
@NamedQueries({
   @NamedQuery(name = "findADetails", query = "SELECT a FROM A a WHERE a.customKey.key     = :customKey")})
public class A extends abstractEntity {

   @Embedded
   private CustomEmbeddedKey customKey;

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "entityA")
   private List<B> bEntities;

   ...
}

Entity B

@Entity
@Table(name = "TABLE_B")
@Access(AccessType.FIELD)
public class B extends abstractEntity {

   @ManyToOne(fetch = FetchType.LAZY, targetEntity = A.class)
   @JoinColumn(name = "FK_A_ID")
   private A entityA;

   ...
}

Abstract entity class

@Entity
@Access(AccessType.FIELD)
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@SequenceGenerator(name = "TOTO_ID_SEQ", sequenceName = "TOTO_ID_SEQ", initialValue = 1, allocationSize = 1)
public abstract class abstractEntity {

   @Id
   @Column(name = "ID")
   @GeneratedValue(generator = "TOTO_ID_SEQ", strategy = GenerationType.SEQUENCE)
   private Long id;

   @OneToOne(mappedBy = "...", fetch = FetchType.EAGER)
   private C anotherEntity;

   @OneToMany(mappedBy = "...", fetch = FetchType.LAZY)
   private List<D> anotherEntities;

   ...

}

Embedded key

@Embeddable
@Access(AccessType.FIELD)
public class CustomEmbeddedKey {

   @Column(name = "...", length = ...)
   private String key;

   ...
}

Dao sample

TypedQuery<A> query = createNamedQuery("findADetails", A.class);
  query.setParameter("customKey", queryParam);
  List<A> aEntitiesFound = query.getResultList();

Thanks in advance for your help.

1

1 Answers

1
votes

Think i found the problem. It's due to the mapping which not really supported by openJPA if we refer to the documentation on Eager Fetching Considerations and Limitations

So finally there are two solutions for me:


Solution 1 (easy one):

  • Change fetch type EAGER to LAZY on the abstract entity class
  • Add fetch groups and update each related namedQueries or code

Advantages:

  • Quick solution and do not have lot of impacts on the existing code.

Disadvantages:

  • Must investigate on usage of new LAZY fields + updates of related queries as it will be now lazy loaded per getters (if not from updated namedQueries)
  • Running tests with Junit and dynamic enhancement will still throw NPE while doing JOIN FETCH on namedQueries with such mapping (Abstraction & TABLE_PER_CLASS inheritance type)

Solution 2: Modify the mapping

  • From abstract entity, remove usage of inheritance (no business usage in fact) and replace by @MappedSuperClass (and so remove @Entity and @Inheritance)
  • Add an interface and define getters / setters of EAGER properties
  • Implement this interface into the abstract entity class so that child classes has to implements getters / setters (and so the related properties and mapping)
  • Change the type of reversed relation onto the target entity of these EAGER objects from abstract class type to new interface type
  • Finally move implementation of mapping, getters and setters of the EAGER properties from abstract entity class to all child entity classes.

Advantages:

  • No more exceptions (nor the NPE due to old mapping and dynamic class enhancement)
  • Stronger solution because more chance to avoid further exception due to openJPA limitations on old mapping style

Disadvantages:

  • More modifications
  • Maybe less readable for devs due to interface instead of abstract class type into targetted entity classes