Лучший способ создать запрос в Hibernate с программными условиями - PullRequest
0 голосов
/ 07 мая 2020

Допустим, у меня есть метод, отвечающий за построение запроса на основе переданных ему параметров:

    public Query<User> buildQuery(String name, String city) {
        Session session = HibernateUtil.getSessionFactory().openSession();

        String queryString = "from Users where 1 = 1 ";
        if (name != null) queryString += "and name = :name ";
        if (city != null) queryString += "and city = :city ";

        Query<User> query = session.createQuery(queryString, User.class);

        if (name != null) query.setParameter("name", name);
        if (city != null) query.setParameter("city", city);

        return query;
    }

Мне это не нравится, потому что logi c повторяется два раза (для добавление условия и установки параметра).

Единственное решение, которое пришло мне в голову, заключалось в использовании запроса критериев:

    public Query<User> buildQuery2(String name, String city) {
        Session session = HibernateUtil.getSessionFactory().openSession();

        CriteriaBuilder builder = session.getCriteriaBuilder();         
        CriteriaQuery<User> criteriaQuery = builder.createQuery(User.class);
        Root<User> root = criteriaQuery.from(User.class);
        Predicate predicate = builder.conjunction();
        if (name != null) {
            predicate = builder.and(predicate, builder.equal(root.get("name"), name));
        }
        if (city != null) {
            predicate = builder.and(predicate, builder.equal(root.get("city"), city));
        }
        criteriaQuery.select(root).where(predicate);            
        Query<User> query = session.createQuery(criteriaQuery);
        return query;
    }

Есть ли лучшее решение для этого?

1 Ответ

1 голос
/ 07 мая 2020

Подход JPA Criteria API является односторонним, но добавление новых фильтров потребует изменений в вашем бизнес-коде , что не очень желательно. Кроме того, в какой-то момент очень больно перечислять все возможные значения фильтров в качестве аргументов метода.

Я бы порекомендовал вам взглянуть на то, что Blaze-Persistence Entity-Views должен offer.

Я создал библиотеку, позволяющую легко отображать модели JPA и модели, определенные пользовательским интерфейсом, что-то вроде Spring Data Projection на стероидах. Идея состоит в том, что вы определяете свою целевую структуру так, как вам нравится, и сопоставляете атрибуты (геттеры) через выражения JPQL с моделью сущности. Поскольку имя атрибута используется в качестве сопоставления по умолчанию, в большинстве случаев явное сопоставление не требуется, поскольку в 80% случаев использования DTO являются подмножеством модели сущности. Самое лучшее во всем этом то, что вы можете определять фильтры для своих проекций.

Сопоставление для вашей модели может выглядеть так же просто, как следующее

@EntityView(User.class)
interface UserView {
  @IdMapping
  Long getId();
  @AttributeFilter(EqualFilter.class)
  String getName();
  @AttributeFilter(EqualFilter.class)
  String getCity();
}

В слое представления, которое вы создаете a EntityViewSetting объект, который вы можете передать на свой уровень обслуживания.

EntityViewSetting<UserView, CriteriaBuilder<UserView>> setting = EntityViewSetting.create(UserView.class);
if (nameFilter != null) setting.addAttributeFilter("name", nameFilter);
if (cityFilter != null) setting.addAttributeFilter("city", cityFilter);

Тогда ваш сервис может быть чистым и просто бизнес-логом c, что-то вроде этого

<T> T findAll(EntityViewSetting<T, CriteriaBuilder<T>> setting) {
    CriteriaBuilder<User> cb = criteriaBuilderFactory.create(entityManager, User.class);
    // Your business logic
    return entityViewManager.applySetting(setting, cb);
}
...