Подход 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);
}