Search code examples
jpaeclipselinkopenjpa

query with OneToMany - openJPA vs EclipseLink


openjpa is complaining about an incorrect argument for a JPA query that EclipseLink properly handles. EclipseLink returns the set of validation messages for the motor.

Two questions:
1) Is my query wrong and EclipseLink is kindly handling it anyway?
2) Any suggestions on how to restructure the query for openjpa?

Thanks for thinking about my question!

Query

SELECT m.valMessages FROM ThreePhaseMotorInput m WHERE m.id = :id

Actual openjpa exception

Caused by: <openjpa-2.3.0-r422266:1540826 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: 
Query projections cannot include array, collection, or map fields.  
Invalid query: "SELECT m.valMessages FROM ThreePhaseMotorInput m WHERE m.id = :id"
    at org.apache.openjpa.kernel.ExpressionStoreQuery$AbstractExpressionExecutor.assertNotContainer(ExpressionStoreQuery.java:328)
    at org.apache.openjpa.kernel.ExpressionStoreQuery$DataStoreExecutor.<init>(ExpressionStoreQuery.java:770)
    at org.apache.openjpa.kernel.ExpressionStoreQuery.newDataStoreExecutor(ExpressionStoreQuery.java:179)
    at org.apache.openjpa.kernel.QueryImpl.createExecutor(QueryImpl.java:749)
    at org.apache.openjpa.kernel.QueryImpl.compileForDataStore(QueryImpl.java:707)
    at org.apache.openjpa.kernel.QueryImpl.compileForExecutor(QueryImpl.java:689)
    at org.apache.openjpa.kernel.QueryImpl.compile(QueryImpl.java:589)
    at org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:1038)
    at org.apache.openjpa.persistence.EntityManagerImpl.createNamedQuery(EntityManagerImpl.java:1017)

ThreePhaseMotorInput mapping

public class ThreePhaseMotorInput implements IThreePhaseMotorInput, Serializable {
    private static final long serialVersionUID = 8084370807289186987L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Version
    private Integer version;
    private Integer status;

    @OneToOne(cascade = CascadeType.ALL, optional = true, targetEntity = UnapprovedThreePhaseMotor.class)
    @JoinColumn(name = "unapproved_id")
    private IThreePhaseMotor unapprovedMotor;

    @OneToOne(cascade = CascadeType.ALL, optional = true, targetEntity = ApprovedThreePhaseMotor.class)
    @JoinColumn(name = "approved_id")
    private IThreePhaseMotor approvedMotor;

    @OneToMany(orphanRemoval = true, cascade = CascadeType.ALL, fetch = FetchType.LAZY, targetEntity = ValidationMessage.class)
    @JoinColumn(name = "input_id", referencedColumnName = "id", nullable = false)
    @OrderColumn(name = "idx")
    private List<IValidationMessage> valMessages;

ValidationMessage mapping

public class ValidationMessage implements Serializable, IValidationMessage {
    private static final long serialVersionUID = 8765213112015434057L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "record_id")
    private Long recordId;
    @Column(name = "field_name")
    private String fieldName;
    @Column(name = "validation_msg")
    private String validationMsg;
    private Integer status;
    @Column(name = "fail_field")
    private String failField;
    @Column(name = "error_source")
    private Integer errorSource;

Solution

  • Check http://docs.oracle.com/javaee/6/tutorial/doc/bnbuf.html#bnbvx - SELECT clause: A SELECT clause cannot specify a collection-valued expression. For example, the SELECT clause p.teams is invalid because teams is a collection. But you can use valMessages for INNER/OUTER join and select IValidationMessage entities trough it, e.g.:

    SELECT ivm
      FROM ThreePhaseMotorInput tpmi
     INNER JOIN tpmi.valMessages ivm
     WHERE tpmi.id = :id