覆盖 QuerydslPredicateExecutor 的 findAll() 方法
overwrite findAll() method of QuerydslPredicateExecutor
我的目标是将动态 Predicate
添加到 QuerydslPredicateExecutor
的 findAll
方法中。这应该用于根据当前活动用户的组织过滤实体。
我正在使用 Spring 数据和 Spring 数据 REST 来获得开箱即用的 REST API,即我没有可以拦截的专用 REST 服务输入的数据并修改它。
通过扩展 SimpleJpaRepository
并将其注册到 @EnableJpaRepositories
可以覆盖方法并更改其默认行为。我想这样做,但是我的 Repository
接口正在实现 QuerydslPredicateExecutor
,这似乎不起作用。
我失败的方法开始于:
public class CustomizedJpaRepositoryIml<T, ID extends Serializable> extends
SimpleJpaRepository<T, ID> {
private EntityManager entityManager;
@Autowired
public CustomizedJpaRepositoryIml(JpaEntityInformation<T, ?>
entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
}
但是显然这个扩展没有提供覆盖的方法。我调试了实现 QuerydslJpaPredicateExecutor
的连接方式,但这相当复杂,我看不出有什么办法可以轻松地在此处插入一些东西。
另一个想法是使用过滤器拦截 URL 调用并添加参数,但这听起来不太好。
我也可以用 @BasePathAwareController
覆盖 finder 的控制器路径,但这意味着要对我拥有的所有实体执行此操作,而不是在一个地方执行此操作。
有什么想法可以实现我的目标吗?也许还有完全不同的选项可以实现我向 Querydsl 添加额外过滤的目标 Predicate
同时我找到了一个方法。它需要提供自己的 QuerydslPredicateExecutor
实现。但这在 Spring 数据中并不容易。答案是由 提出的,但同时构造函数已更改为较新的 Spring 数据,为什么不能采用 1:1.
我在我的问题中使用了不同的示例,但是使用此解决方案,您还可以完全自由地添加和附加任何 Predicate
。作为示例,我在这里使用自定义的 Querydsl 实现,如果没有传递任何内容,则始终使用实体的 creationDate
作为排序标准。我假设在此示例中,此列存在于所有实体的某些 @MappedSuperClass
中。在现实生活中使用生成的静态元数据代替硬编码字符串 "creationDate".
首先包装委托所有 CustomQuerydslJpaRepositoryIml
将所有方法委托给 QuerydslJpaPredicateExecutor
:
/**
* Customized Querydsl JPA repository to apply custom filtering and sorting logic.
*
*/
public class CustomQuerydslJpaRepositoryIml<T> implements QuerydslPredicateExecutor<T> {
private final QuerydslJpaPredicateExecutor querydslPredicateExecutor;
public CustomQuerydslJpaRepositoryIml(QuerydslJpaPredicateExecutor querydslPredicateExecutor) {
this.querydslPredicateExecutor = querydslPredicateExecutor;
}
private Sort applyDefaultOrder(Sort sort) {
if (sort.isUnsorted()) {
return Sort.by("creationDate").ascending();
}
return sort;
}
private Pageable applyDefaultOrder(Pageable pageable) {
if (pageable.getSort().isUnsorted()) {
Sort defaultSort = Sort.by(AuditableEntity_.CREATION_DATE).ascending();
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), defaultSort);
}
return pageable;
}
@Override
public Optional<T> findOne(Predicate predicate) {
return querydslPredicateExecutor.findOne(predicate);
}
@Override
public List<T> findAll(Predicate predicate) {
return querydslPredicateExecutor.findAll(predicate);
}
@Override
public List<T> findAll(Predicate predicate, Sort sort) {
return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(sort));
}
@Override
public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
return querydslPredicateExecutor.findAll(predicate, orders);
}
@Override
public List<T> findAll(OrderSpecifier<?>... orders) {
return querydslPredicateExecutor.findAll(orders);
}
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(pageable));
}
@Override
public long count(Predicate predicate) {
return querydslPredicateExecutor.count(predicate);
}
@Override
public boolean exists(Predicate predicate) {
return querydslPredicateExecutor.exists(predicate);
}
}
接下来 CustomJpaRepositoryFactory
施展魔法并提供 Querydsl 包装器 class 而不是默认包装器。默认一个作为参数传递并包装。
/**
* Custom JpaRepositoryFactory allowing to support a custom QuerydslJpaRepository.
*
*/
public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {
/**
* Creates a new {@link JpaRepositoryFactory}.
*
* @param entityManager must not be {@literal null}
*/
public CustomJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager);
}
@Override
protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
final RepositoryComposition.RepositoryFragments[] modifiedFragments = {RepositoryComposition.RepositoryFragments.empty()};
RepositoryComposition.RepositoryFragments fragments = super.getRepositoryFragments(metadata);
// because QuerydslJpaPredicateExecutor is using som internal classes only a wrapper can be used.
fragments.stream().forEach(
f -> {
if (f.getImplementation().isPresent() &&
QuerydslJpaPredicateExecutor.class.isAssignableFrom(f.getImplementation().get().getClass())) {
modifiedFragments[0] = modifiedFragments[0].append(RepositoryFragment.implemented(
new CustomQuerydslJpaRepositoryIml((QuerydslJpaPredicateExecutor) f.getImplementation().get())));
} else {
modifiedFragments[0].append(f);
}
}
);
return modifiedFragments[0];
}
}
终于CustomJpaRepositoryFactoryBean
了。这必须在 Spring 引导应用程序中注册,以使 Spring 知道从哪里获取存储库实现,例如与:
@SpringBootApplication
@EnableJpaRepositories(basePackages = "your.package",
repositoryFactoryBeanClass = CustomJpaRepositoryFactoryBean.class)
...
这里是 class:
public class CustomJpaRepositoryFactoryBean<T extends Repository<S, I>, S, I> extends JpaRepositoryFactoryBean<T, S, I> {
/**
* Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
*/
public CustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomJpaRepositoryFactory(entityManager);
}
}
我的目标是将动态 Predicate
添加到 QuerydslPredicateExecutor
的 findAll
方法中。这应该用于根据当前活动用户的组织过滤实体。
我正在使用 Spring 数据和 Spring 数据 REST 来获得开箱即用的 REST API,即我没有可以拦截的专用 REST 服务输入的数据并修改它。
通过扩展 SimpleJpaRepository
并将其注册到 @EnableJpaRepositories
可以覆盖方法并更改其默认行为。我想这样做,但是我的 Repository
接口正在实现 QuerydslPredicateExecutor
,这似乎不起作用。
我失败的方法开始于:
public class CustomizedJpaRepositoryIml<T, ID extends Serializable> extends
SimpleJpaRepository<T, ID> {
private EntityManager entityManager;
@Autowired
public CustomizedJpaRepositoryIml(JpaEntityInformation<T, ?>
entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
}
但是显然这个扩展没有提供覆盖的方法。我调试了实现 QuerydslJpaPredicateExecutor
的连接方式,但这相当复杂,我看不出有什么办法可以轻松地在此处插入一些东西。
另一个想法是使用过滤器拦截 URL 调用并添加参数,但这听起来不太好。
我也可以用 @BasePathAwareController
覆盖 finder 的控制器路径,但这意味着要对我拥有的所有实体执行此操作,而不是在一个地方执行此操作。
有什么想法可以实现我的目标吗?也许还有完全不同的选项可以实现我向 Querydsl 添加额外过滤的目标 Predicate
同时我找到了一个方法。它需要提供自己的 QuerydslPredicateExecutor
实现。但这在 Spring 数据中并不容易。答案是由
我在我的问题中使用了不同的示例,但是使用此解决方案,您还可以完全自由地添加和附加任何 Predicate
。作为示例,我在这里使用自定义的 Querydsl 实现,如果没有传递任何内容,则始终使用实体的 creationDate
作为排序标准。我假设在此示例中,此列存在于所有实体的某些 @MappedSuperClass
中。在现实生活中使用生成的静态元数据代替硬编码字符串 "creationDate".
首先包装委托所有 CustomQuerydslJpaRepositoryIml
将所有方法委托给 QuerydslJpaPredicateExecutor
:
/**
* Customized Querydsl JPA repository to apply custom filtering and sorting logic.
*
*/
public class CustomQuerydslJpaRepositoryIml<T> implements QuerydslPredicateExecutor<T> {
private final QuerydslJpaPredicateExecutor querydslPredicateExecutor;
public CustomQuerydslJpaRepositoryIml(QuerydslJpaPredicateExecutor querydslPredicateExecutor) {
this.querydslPredicateExecutor = querydslPredicateExecutor;
}
private Sort applyDefaultOrder(Sort sort) {
if (sort.isUnsorted()) {
return Sort.by("creationDate").ascending();
}
return sort;
}
private Pageable applyDefaultOrder(Pageable pageable) {
if (pageable.getSort().isUnsorted()) {
Sort defaultSort = Sort.by(AuditableEntity_.CREATION_DATE).ascending();
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), defaultSort);
}
return pageable;
}
@Override
public Optional<T> findOne(Predicate predicate) {
return querydslPredicateExecutor.findOne(predicate);
}
@Override
public List<T> findAll(Predicate predicate) {
return querydslPredicateExecutor.findAll(predicate);
}
@Override
public List<T> findAll(Predicate predicate, Sort sort) {
return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(sort));
}
@Override
public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
return querydslPredicateExecutor.findAll(predicate, orders);
}
@Override
public List<T> findAll(OrderSpecifier<?>... orders) {
return querydslPredicateExecutor.findAll(orders);
}
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable) {
return querydslPredicateExecutor.findAll(predicate, applyDefaultOrder(pageable));
}
@Override
public long count(Predicate predicate) {
return querydslPredicateExecutor.count(predicate);
}
@Override
public boolean exists(Predicate predicate) {
return querydslPredicateExecutor.exists(predicate);
}
}
接下来 CustomJpaRepositoryFactory
施展魔法并提供 Querydsl 包装器 class 而不是默认包装器。默认一个作为参数传递并包装。
/**
* Custom JpaRepositoryFactory allowing to support a custom QuerydslJpaRepository.
*
*/
public class CustomJpaRepositoryFactory extends JpaRepositoryFactory {
/**
* Creates a new {@link JpaRepositoryFactory}.
*
* @param entityManager must not be {@literal null}
*/
public CustomJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager);
}
@Override
protected RepositoryComposition.RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
final RepositoryComposition.RepositoryFragments[] modifiedFragments = {RepositoryComposition.RepositoryFragments.empty()};
RepositoryComposition.RepositoryFragments fragments = super.getRepositoryFragments(metadata);
// because QuerydslJpaPredicateExecutor is using som internal classes only a wrapper can be used.
fragments.stream().forEach(
f -> {
if (f.getImplementation().isPresent() &&
QuerydslJpaPredicateExecutor.class.isAssignableFrom(f.getImplementation().get().getClass())) {
modifiedFragments[0] = modifiedFragments[0].append(RepositoryFragment.implemented(
new CustomQuerydslJpaRepositoryIml((QuerydslJpaPredicateExecutor) f.getImplementation().get())));
} else {
modifiedFragments[0].append(f);
}
}
);
return modifiedFragments[0];
}
}
终于CustomJpaRepositoryFactoryBean
了。这必须在 Spring 引导应用程序中注册,以使 Spring 知道从哪里获取存储库实现,例如与:
@SpringBootApplication
@EnableJpaRepositories(basePackages = "your.package",
repositoryFactoryBeanClass = CustomJpaRepositoryFactoryBean.class)
...
这里是 class:
public class CustomJpaRepositoryFactoryBean<T extends Repository<S, I>, S, I> extends JpaRepositoryFactoryBean<T, S, I> {
/**
* Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
*/
public CustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomJpaRepositoryFactory(entityManager);
}
}