简体   繁体   中英

JPA LOJ (left outer join) : Cannot join to attribute of basic type

I have two entities I want to do an LOJ on using JPA and a criteria query. Sounds easy enough but I am running into a problem. My entities:

@Data
@ToString
@Entity
@Table(name = "TABLE1")
public class Table1 {
       @Id
       @SuppressWarnings("all")
       @Column(name = "ROWID")
        private String rowId;

       @Column(name = "SOME_ID")
       private String someId;
       ....
       @ManyToMany
       @JoinColumn(name = "SOME_ID", referencedColumnName = "SOME_ID")
       private List<Table2> table2s;
}

and

@Data
@ToString
@Entity
@Table(name = "TABLE2")
public class Table2 implements Serializable {

    @Id
    @SuppressWarnings("all")
    @Column(name = "ROWID")
    private String rowId;

   @Column(name = "SOME_ID")
    private String someIdAsId;
   ....

   @ManyToMany(fetch = FetchType.LAZY, mappedBy = "table2s")
   private List<Table1> table1s;
}

My criteria query/join:

        EntityManager em = getEntityManager();
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Object> criteriaQuery = cb.createQuery();
        criteriaQuery.distinct(true);
        Root<Table1> previewSearch = criteriaQuery.from(Table1.class);

        if(searchFields.contains(A_FILE)) {
            Join<Table1, Table2> join = previewSearch.join("someId", JoinType.LEFT);
        }

Later I add a filter criteria for reg file, as in:

predicates.add(cb.like(cb.trim(cb.lower((previewSearch.get("aFileNo")))), "%" + legacySearchCriteria.getAFileNo() + "%"));

then add the preds to the query:

criteriaQuery.select(previewSearch).where(predicates.toArray(new Predicate[]{}));

then:

Query query = em.createQuery(criteriaQuery);
List<Table1> results = getResults(query);

The code around the getting of results and all is proven. That isn't the issue. It has something to do with the way I have my entities annotated or similar. When I run the query when I have the "AFile" in the query (triggering the creation of a Join object in my code), I get a stacktrace whose base error is:

Caused by: org.hibernate.jpa.criteria.BasicPathUsageException: Cannot join to attribute of basic type
at org.hibernate.jpa.criteria.path.AbstractFromImpl.constructJoin(AbstractFromImpl.java:254)
at org.hibernate.jpa.criteria.path.AbstractFromImpl.join(AbstractFromImpl.java:247)
at org.hibernate.jpa.criteria.path.AbstractFromImpl.join(AbstractFromImpl.java:422)
at com.xxx.ent.dataobjects.common.dao.SearchDao.executeQuery(PreviewConditionalSearchDao.java:77)
at com.xxx.ent.dataobjects.common.dao.SearchDao.getPreview(PreviewConditionalSearchDao.java:46)
at sun.reflect.GeneratedMethodAccessor20.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)

I have hit this site et al. and nothing there looks like it has a fix for me, though I tried several things. Anyone have ideas?

---- UPDATE ----

In essence I was looking to do a:

select fields from Table1 t1 left join Table2 t2 on t1.f1=t2.f1 where t2.f2 = 'somevalue'

Lessons learned: f1 must be the Id column of the JPA entities being joined or else you don't get it. The final answer looks like this:

Entities:

 @Data
 @ToString
 @Entity
 @Table(name = "TABLE_1")
 public class Table1 {
   @Id
   @Column(name = "ID")
   private String id;
 ....
   @ManyToOne(fetch=FetchType.LAZY)
   @JoinColumn(name = "ID", referencedColumnName = "ID", insertable=false, updatable=false)
   private Table2 table2;
 }

and

@Data
@ToString
@Entity
@Table(name = "TABLE_2")
public class Table2 {

 @Id
 @Column(name = "ID")
 private String id;

 @Column(name = "FIELD_2")
 private String f2;

 @OneToMany(fetch = FetchType.LAZY, mappedBy = "table2")
 private List<Table1> table1s;
}

Code:

EntityManager entityManager = getEntityManager();
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = cb.createQuery();
criteriaQuery.distinct(true);
Root<Table1> table1Root = criteriaQuery.from(Table1.class);
Path field2Path = null;

if(searchFields.contains(FIELD2)) {
   Join<Table1, Table2> field2Join = table1Root.join("table2", JoinType.LEFT);
    field2Path = field2Join.get("f2");
}

List<Predicate> predicates = new ArrayList<>();

// add preds ...
case SOME_PRED:              
  predicates.add(cb.like(cb.trim(cb.lower((table1Root.get("somePred")))), "%" + criteria.getSomePred() + "%"));
  break;
case FIELD2:
  predicates.add(cb.like(cb.trim(cb.lower((field2Path))), "%" + criteria.getField2() + "%"));
  break;

....
criteriaQuery.select(table1Root).where(predicates.toArray(new Predicate[]{}));
Query query = entityManager.createQuery(criteriaQuery);

Then run the query. That actuallly works. Finally.

A join forms an aggregate of two entity types, yielding entity tuples. You are expected to designate an attribute identifying the relationship characterizing the join, not the join column. Something like this:

        Join<Table1, Table2> join = previewSearch.join("table2s", JoinType.LEFT);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM