У меня есть две сущности, которые я хочу сделать LOJ на использование JPA и запрос критерия. Звучит достаточно просто, но я столкнулся с проблемой. Мои сущности:
@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;
}
и
@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;
}
Мои критерии запроса / присоединения:
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);
}
Позже я добавлю критерии фильтра для файла reg, например:
predicates.add(cb.like(cb.trim(cb.lower((previewSearch.get("aFileNo")))), "%" + legacySearchCriteria.getAFileNo() + "%"));
затем добавьте преды к запросу:
criteriaQuery.select(previewSearch).where(predicates.toArray(new Predicate[]{}));
, то:
Query query = em.createQuery(criteriaQuery);
List<Table1> results = getResults(query);
Код получения результатов и все доказано. Это не проблема. Это как-то связано с тем, как мои сущности аннотированы или похожи. Когда я запускаю запрос, когда у меня есть «AFile» в запросе (запускающий создание объекта Join в моем коде), я получаю трассировку стека, базовая ошибка которой:
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)
Я попал на этот сайт и др. и ничто там не выглядит, как будто это исправляет меня, хотя я попробовал несколько вещей. У кого-нибудь есть идеи?
---- ОБНОВЛЕНИЕ ----
По сути, я хотел сделать:
select fields from Table1 t1 left join Table2 t2 on t1.f1=t2.f1 where t2.f2 = 'somevalue'
Извлеченные уроки: f1 должен быть столбцом Id объединяемых сущностей JPA, иначе вы его не получите. Окончательный ответ выглядит так:
Объекты:
@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;
}
и
@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;
}
Код:
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);
Затем запустите запрос. Это действительно работает. Наконец-то.