У меня есть три сопоставленных объекта: элемент, пользователь и компания. Есть таблица, которая определяет пользователя и его отношения с компанией (для разрешений)
Я пытаюсь найти все эти элементы, которые либо принадлежат пользователю, либо принадлежат компаниям, на которые у них есть разрешениедля просмотра / редактирования.
Я могу заставить запрос работать легко в нативном SQL, я просто не могу заставить его работать с построителем критериев. У него будут динамические фильтры и порядок, поэтому использование собственных запросов мешает конкатенации строк.
@Entity
public class Item implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "item_id")
private Integer itemId;
@Basic(optional = false)
@NotNull
@Lob
@Size(min = 1, max = 65535)
@Column(name = "title")
private String title;
@JoinColumn(name = "company_id", referencedColumnName = "company_id")
@ManyToOne
private Company companyId;
@JoinColumn(name = "user_id", referencedColumnName = "user_id")
@ManyToOne(optional = false)
private Users userId;
... getters and setters...
}
@Entity
public class CompanyUsers implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "company_user_id")
private Integer companyUserId;
@Basic(optional = false)
@NotNull
@Column(name = "edit_company")
private boolean editCompany;
@JoinColumn(name = "company_id", referencedColumnName = "company_id")
@ManyToOne(optional = false)
private Company companyId;
@JoinColumn(name = "user_id", referencedColumnName = "user_id")
@ManyToOne(optional = false)
private Users userId;
... getters and setters...
}
@Entity
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "company_id")
private Integer companyId;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 256)
@Column(name = "company_name")
private String companyName;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "companyId")
private Collection<CompanyUsers> companyUsersCollection;
...getters and setters...
}
@Entity
public class Users implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "user_id")
private Integer userId;
@Basic(optional = false)
@NotNull
@Size(min = 1, max = 128)
@Column(name = "email_address")
private String emailAddress;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private Collection<Item> itemCollection;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "userId", orphanRemoval = true)
private Collection<CompanyUsers> companyUsersCollection;
...getters and setters...
/*
Then the attempt and getting this right with Criteria Builder.
*/
public List<Item> findAllItem(Users selectedUser, int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Item> cq = cb.createQuery(Item.class);
Root<Item> item = cq.from(Item.class);
CriteriaQuery<Item> select = cq.select(item);
Subquery<CompanyUsers> subquery = cq.subquery(CompanyUsers.class);
Root<CompanyUsers> companyUsers = subquery.from(CompanyUsers.class);
subquery.select(companyUsers)
.distinct(true)
.where(cb.equal(companyUsers.get("userId"), selectedUser));
if (sortField == null) {
sortField = "itemId";
}
if (sortOrder.equals(SortOrder.ASCENDING)) {
cq.orderBy(cb.asc(item.get(sortField)));
} else if (sortOrder.equals(SortOrder.DESCENDING)) {
cq.orderBy(cb.desc(item.get(sortField)));
}
List<Predicate> predicateList = new ArrayList<>();
predicateList.add(cb.equal(item.get("userId"), selectedUser));
predicateList.add(cb.in(item.get("companyId")).value(subquery));
filters.entrySet().forEach((filter) -> {
if (filter.getKey().equals("itemStatus") || filter.getKey().equals("itemId")) {
predicateList.add(cb.equal(item.get(filter.getKey()), filter.getValue().toString()));
} else {
predicateList.add(cb.like(item.get(filter.getKey()), "%" + filter.getValue().toString() + "%"));
}
});
Predicate[] predicateArray = predicateList.stream().toArray(Predicate[]::new);
cq.where(predicateArray);
TypedQuery<Item> typedQuery = em.createQuery(select);
if (first >= 0) {
typedQuery.setFirstResult(first);
}
if (pageSize >= 0) {
typedQuery.setMaxResults(pageSize);
}
return typedQuery.getResultList();
}
Я не включил здесь все имена столбцов, но есть и другие.
Этопроизводит следующий sql:
SELECT t0.title AS a2, t0.item_id AS a1 FROM item t0, company_users t1 WHERE ((t0.user_id = ?) AND (t1.user_id = ?)) ORDER BY t0.item_id ASC LIMIT ?, ?
, который близок к одному из следующих:
select title, item_id, cu.company_id from item s left join company_users as cu on cu.company_id = s.company_id where s.user_id = 2 or cu.user_id = 2;
или
select title,item_id from item s where s.user_id = 2 or s.company_id in (select company_id from company_users where user_id = 2) ;
Два вышеупомянутых нативных запроса - это то, что япытаюсь добиться. Я не могу заставить работать построитель критериев, используя соединение или подзапрос.