Попытка получить набор результатов из таблицы с объединенной таблицей, используя Criteria API в Java - PullRequest
1 голос
/ 16 октября 2019

У меня есть три сопоставленных объекта: элемент, пользователь и компания. Есть таблица, которая определяет пользователя и его отношения с компанией (для разрешений)

Я пытаюсь найти все эти элементы, которые либо принадлежат пользователю, либо принадлежат компаниям, на которые у них есть разрешениедля просмотра / редактирования.

Я могу заставить запрос работать легко в нативном 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) ;

Два вышеупомянутых нативных запроса - это то, что япытаюсь добиться. Я не могу заставить работать построитель критериев, используя соединение или подзапрос.

1 Ответ

0 голосов
/ 17 октября 2019
public List<Item> findAllItem(Users selectedUser, int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {

        ArrayList<String> filterItems = new ArrayList<>();
        Map<String, Object> parameterMap = new HashMap<String, Object>();

        filters.entrySet().forEach((filter) -> {
                switch (filter.getKey()) {
                    case "userStatus":
                        filterItems.add(" AND s.statusType = :statusType");
                        parameterMap.put("statusType", (String) filter.getValue());
                        break;
                    case "title":
                        filterItems.add(" AND s.title like :title");
                        parameterMap.put("title", "%" + (String) filter.getValue() + "%");
                        break;
                    default:
                        break;
                }
            });
        }


        String sortDirection;
        if (sortOrder.equals(SortOrder.ASCENDING)) {
            sortDirection = "ASC";
        } else {
            sortDirection = "DESC";
        }

        String sortString;
        if (sortField == null) {
            sortString = "s.itemId";
        } else {
            switch (sortField) {
                case "title":
                    sortString = "s.title";
                    break;
                case "statusType":
                    sortString = "s.statusType";
                    break;
                case "startDate":
                    sortString = "s.startDate";
                    break;
                case "endDate":
                    sortString = "s.endDate";
                    break;
                default:
                    sortString = "s.itemId";
                    break;
            }
        }

        Query query = em.createQuery("SELECT s from Item s WHERE (s.userId = :userId or s.companyId in "
                + "(SELECT cu.companyId from CompanyUsers cu WHERE cu.userId = :userId))"
                + String.join(" ", filterItems)
                + " ORDER BY " + sortString + " " + sortDirection,
                 Item.class);

        query.setParameter("userId", selectedUser);
        for (String key : parameterMap.keySet()) {
            query.setParameter(key, parameterMap.get(key));
        }

        if (first >= 0) {
            query.setFirstResult(first);
        }
        if (pageSize >= 0) {
            query.setMaxResults(pageSize);
        }
        List<Item> result = query.getResultList();
        return result;
    }
...