比方说,我们使用软删除策略:什么都不会从存储中删除;而是在记录/文档/任何要使其“删除”的属性上将“已删除”属性/列设置为true。稍后,查询方法只应返回未删除的条目。
让我们以MongoDB为例(尽管JPA也很有趣)。
对于MongoRepository
定义的标准方法,我们可以扩展默认实现(SimpleMongoRepository
),覆盖感兴趣的方法,并使它们忽略“已删除”的文档。
但是,当然,我们也想使用自定义查询方法,例如
List<Person> findByFirstName(String firstName)
在软删除环境中,我们被迫做类似的事情
List<person> findByFirstNameAndDeletedIsFalse(String firstName)
或使用@Query
手动编写查询(始终添加关于“未删除”的相同样板条件)。
这是一个问题:是否可以将此“未删除”条件自动添加到任何生成的查询中?我没有在文档中找到任何内容。
我正在查看Spring Data(Mongo和JPA)2.1.6。
@Where
批注,该批注仅适用于JPA + Hibernate,如果您仍需要访问某些查询中的已删除项,则不清楚如何覆盖它。@Where
的相同方法,要么解决方案的适用性受到已经定义的标准方法的限制,而不是自定义方法。答案 0 :(得分:0)
事实证明,对于Mongo(至少对于spring-data-mongo 2.1.6),我们可以侵入标准的QueryLookupStrategy
实现中,以添加所需的“发现者的行为不可见的软删除文档” :
public class SoftDeleteMongoQueryLookupStrategy implements QueryLookupStrategy {
private final QueryLookupStrategy strategy;
private final MongoOperations mongoOperations;
public SoftDeleteMongoQueryLookupStrategy(QueryLookupStrategy strategy,
MongoOperations mongoOperations) {
this.strategy = strategy;
this.mongoOperations = mongoOperations;
}
@Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
NamedQueries namedQueries) {
RepositoryQuery repositoryQuery = strategy.resolveQuery(method, metadata, factory, namedQueries);
// revert to the standard behavior if requested
if (method.getAnnotation(SeesSoftlyDeletedRecords.class) != null) {
return repositoryQuery;
}
if (!(repositoryQuery instanceof PartTreeMongoQuery)) {
return repositoryQuery;
}
PartTreeMongoQuery partTreeQuery = (PartTreeMongoQuery) repositoryQuery;
return new SoftDeletePartTreeMongoQuery(partTreeQuery);
}
private Criteria notDeleted() {
return new Criteria().orOperator(
where("deleted").exists(false),
where("deleted").is(false)
);
}
private class SoftDeletePartTreeMongoQuery extends PartTreeMongoQuery {
SoftDeletePartTreeMongoQuery(PartTreeMongoQuery partTreeQuery) {
super(partTreeQuery.getQueryMethod(), mongoOperations);
}
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
Query query = super.createQuery(accessor);
return withNotDeleted(query);
}
@Override
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
Query query = super.createCountQuery(accessor);
return withNotDeleted(query);
}
private Query withNotDeleted(Query query) {
return query.addCriteria(notDeleted());
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SeesSoftlyDeletedRecords {
}
除非所有@SeesSoftlyDeletedRecords
要求避免,否则我们只向所有查询添加“未删除”条件。
然后,我们需要以下基础结构来插入我们的QueryLiikupStrategy
实现:
public class SoftDeleteMongoRepositoryFactory extends MongoRepositoryFactory {
private final MongoOperations mongoOperations;
public SoftDeleteMongoRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.mongoOperations = mongoOperations;
}
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
Optional<QueryLookupStrategy> optStrategy = super.getQueryLookupStrategy(key,
evaluationContextProvider);
return optStrategy.map(this::createSoftDeleteQueryLookupStrategy);
}
private SoftDeleteMongoQueryLookupStrategy createSoftDeleteQueryLookupStrategy(QueryLookupStrategy strategy) {
return new SoftDeleteMongoQueryLookupStrategy(strategy, mongoOperations);
}
}
public class SoftDeleteMongoRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable>
extends MongoRepositoryFactoryBean<T, S, ID> {
public SoftDeleteMongoRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
@Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new SoftDeleteMongoRepositoryFactory(operations);
}
}
然后,我们只需要在@EnableMongoRepositories
注释中引用工厂bean,如下所示:
@EnableMongoRepositories(repositoryFactoryBeanClass = SoftDeleteMongoRepositoryFactoryBean.class)
如果需要动态确定特定存储库是需要“软删除”还是常规的“硬删除”存储库,则可以对存储库接口(或域类)进行内部检查,并决定是否需要更改QueryLookupStrategy
。
对于JPA,如果不重写(可能重复)PartTreeJpaQuery
中的大部分代码,这种方法将无法工作。