my2iu / Jinq

LINQ-style queries for Java 8
Other
659 stars 71 forks source link

Mapping collections to CustomTuples #83

Closed mkp05 closed 4 years ago

mkp05 commented 4 years ago

Hi, having the possibility to return Custom Tuples directly from the queries I've encountered some problems with nested collections and other entities.

  1. Nested collection Having (simplified) entity as follows:

    @Entity
    @Table(name = "entity")
    public class Entity {
    @Id
    private Long id;
    private String lead;
    
    // some more heavy rarely needed fields
    
    @ElementCollection
    @CollectionTable(name = "followers", joinColumns = @JoinColumn(name = "entity_id"))
    @Column(name = "follower", nullable = false)
    private Collection<String> followers = new LinkedList<>();
    }

    and custom tuple as follows:

    public class EntityTuple {
    private String lead;
    private Collection<String> followers;
    
    public EntityTuple (String lead, Collection<String> followers) {
    this.lead = lead;
    this.followers = followers;
    }

    registered by registerCustomTupleConstructor, I'm trying to retrieve data in a following way:

    <stream>
      .where(e -> e.getId().equals(id))
      .joinFetchList(e-> e.getFollowers())
      .select(e-> new EntityTuple(
        e.getLead(),
        e.getFollowers()
      ))
      .getOnlyValue();

    I receive error like this:

    java.lang.IllegalArgumentException: Could not translate code to a query
    at org.jinq.jpa.JPAQueryComposer.translationFail(JPAQueryComposer.java:118) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:294) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.JPAQueryComposer.select(JPAQueryComposer.java:491) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.JPAQueryComposer.select(JPAQueryComposer.java:63) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.orm.stream.QueryJinqStream.select(QueryJinqStream.java:62) ~[api-1.8.29.jar:?]
    at org.jinq.jpa.QueryJPAJinqStream.select(QueryJPAJinqStream.java:127) ~[jinq-jpa-1.8.29.jar:?]
    [... a lot of stuff...]
    Caused by: org.jinq.jpa.transform.QueryTransformException: ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitorException: Unhandled symbolic execution operation: @arg0.getFollowers()
    at org.jinq.jpa.transform.SelectTransform.apply(SelectTransform.java:51) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:290) ~[jinq-jpa-1.8.29.jar:?]
    ... 123 more
    Caused by: ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitorException: Unhandled symbolic execution operation: @arg0.getFollowers()
    at org.jinq.jpa.transform.SymbExToColumns.defaultValue(SymbExToColumns.java:54) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.SymbExToColumns.defaultValue(SymbExToColumns.java:41) ~[jinq-jpa-1.8.29.jar:?]
    at ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitor.methodCallValue(TypedValueVisitor.java:116) ~[analysis-1.8.29.jar:?]
    at ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitor.virtualMethodCallValue(TypedValueVisitor.java:124) ~[analysis-1.8.29.jar:?]
    at org.jinq.jpa.transform.SymbExToColumns.virtualMethodCallValue(SymbExToColumns.java:677) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.SymbExToColumns.virtualMethodCallValue(SymbExToColumns.java:41) ~[jinq-jpa-1.8.29.jar:?]
    at ch.epfl.labos.iu.orm.queryll2.symbolic.MethodCallValue$VirtualMethodCallValue.visit(MethodCallValue.java:143) ~[analysis-1.8.29.jar:?]
    at org.jinq.jpa.transform.SymbExToColumns.handleMakeTupleMethodCall(SymbExToColumns.java:786) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.SymbExToColumns.virtualMethodCallValue(SymbExToColumns.java:369) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.SymbExToColumns.virtualMethodCallValue(SymbExToColumns.java:41) ~[jinq-jpa-1.8.29.jar:?]
    at ch.epfl.labos.iu.orm.queryll2.symbolic.MethodCallValue$VirtualMethodCallValue.visit(MethodCallValue.java:143) ~[analysis-1.8.29.jar:?]
    at org.jinq.jpa.transform.JPQLQueryTransform.simplifyAndTranslatePathToColumns(JPQLQueryTransform.java:110) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.JPQLQueryTransform.simplifyAndTranslateMainPathToColumns(JPQLQueryTransform.java:102) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.JPQLQueryTransform.makeSelectExpression(JPQLQueryTransform.java:36) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.transform.SelectTransform.apply(SelectTransform.java:27) ~[jinq-jpa-1.8.29.jar:?]
    at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:290) ~[jinq-jpa-1.8.29.jar:?]
    ... 123 more

Is there anything more needed to be able to map collections directly to custom tuples?

my2iu commented 4 years ago

That's a limitation of SQL. A query can't return an item that contains multiple elements inside of it. The joinFetchList() thing is the most you can do. Just return the entity from the query, and read out the lead and followers from the entity afterwards.

mkp05 commented 4 years ago

That's a limitation of SQL. I'm aware of that. I just hoped that there is some library-side solution similar to QueryDSL's Projections.list or Projections.set.

Just return the entity from the query, and read out the lead and followers from the entity afterwards. it's too much data to download this way (and this approach could be achieved using simple JpaRepository), but I managed to come up with ugly solution with leftJoin and then grouping and fixing the data on the application server side.