Skip to content

Instantly share code, notes, and snippets.

@yurikilian
Last active March 17, 2023 04:38
Show Gist options
  • Save yurikilian/cc096e3384904001d4273de69ed4ea4c to your computer and use it in GitHub Desktop.
Save yurikilian/cc096e3384904001d4273de69ed4ea4c to your computer and use it in GitHub Desktop.
Soft Delete Mongo Repository
import br.com.radharsomeluz.inventory.configuration.SoftDeletionMongoRepository;
import br.com.radharsomeluz.inventory.configuration.SoftDeletionRepositoryFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@SpringBootApplication
@EnableSwagger2
@EnableMongoRepositories(
repositoryBaseClass = SoftDeletionMongoRepository.class,
repositoryFactoryBeanClass = SoftDeletionRepositoryFactoryBean.class)
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor;
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
import org.springframework.data.mongodb.repository.query.PartTreeMongoQuery;
import static org.springframework.data.mongodb.core.query.Criteria.where;
public class SoftDeletionMongoQuery extends PartTreeMongoQuery {
public SoftDeletionMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) {
super(method, mongoOperations);
}
@Override
protected Query createQuery(ConvertingParameterAccessor accessor) {
Query query = super.createQuery(accessor);
query.addCriteria(where("deleted").is(false));
return query;
}
@Override
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
Query countQuery = super.createCountQuery(accessor);
countQuery.addCriteria(where("deleted").is(false));
return countQuery;
}
}
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.data.util.StreamUtils;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static org.springframework.data.mongodb.core.query.Criteria.where;
public class SoftDeletionMongoRepository<T, ID> implements MongoRepository<T, ID> {
private static final String SOFT_DELETION_KEY = "deleted";
private final MongoOperations mongoOperations;
private final MongoEntityInformation<T, ID> entityInformation;
public SoftDeletionMongoRepository(
MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
Assert.notNull(metadata, "MongoEntityInformation must not be null!");
Assert.notNull(mongoOperations, "MongoOperations must not be null!");
this.entityInformation = metadata;
this.mongoOperations = mongoOperations;
}
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null!");
if (entityInformation.isNew(entity)) {
return mongoOperations.insert(entity, entityInformation.getCollectionName());
}
return mongoOperations.save(entity, entityInformation.getCollectionName());
}
@Override
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
Streamable<S> source = Streamable.of(entities);
boolean allNew = source.stream().allMatch(entityInformation::isNew);
if (allNew) {
List<S> result = source.stream().collect(Collectors.toList());
return new ArrayList<>(mongoOperations.insert(result, entityInformation.getCollectionName()));
}
return source.stream().map(this::save).collect(Collectors.toList());
}
@Override
public Optional<T> findById(ID id) {
Assert.notNull(id, "The given id must not be null!");
return Optional.ofNullable(
mongoOperations.findOne(
getIdQuery(id),
entityInformation.getJavaType(),
entityInformation.getCollectionName()));
}
@Override
public boolean existsById(ID id) {
Assert.notNull(id, "The given id must not be null!");
return mongoOperations.exists(
getIdQuery(id).addCriteria(where(SOFT_DELETION_KEY).is(false)),
entityInformation.getJavaType(),
entityInformation.getCollectionName());
}
@Override
public long count() {
Query query = new Query();
query.addCriteria(where(SOFT_DELETION_KEY).is(false));
return mongoOperations.count(query, entityInformation.getCollectionName());
}
@Override
public void deleteById(ID id) {
Assert.notNull(id, "The given id must not be null!");
mongoOperations.updateFirst(
getIdQuery(id),
new Update().set(SOFT_DELETION_KEY, true),
entityInformation.getJavaType(),
entityInformation.getCollectionName());
}
@Override
public void delete(T entity) {
Assert.notNull(entity, "The given entity must not be null!");
deleteById(entityInformation.getRequiredId(entity));
}
@Override
public void deleteAll(Iterable<? extends T> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
entities.forEach(this::delete);
}
@Override
public void deleteAll() {
Update update = new Update();
update.set(SOFT_DELETION_KEY, true);
mongoOperations.updateMulti(
new Query(),
update,
entityInformation.getJavaType(),
entityInformation.getCollectionName());
}
@Override
public List<T> findAll() {
return findAll(new Query());
}
@Override
public Iterable<T> findAllById(Iterable<ID> ids) {
return findAll(
new Query(
new Criteria(entityInformation.getIdAttribute())
.in(Streamable.of(ids).stream().collect(StreamUtils.toUnmodifiableList()))));
}
@Override
public Page<T> findAll(Pageable pageable) {
Assert.notNull(pageable, "Pageable must not be null!");
long count = count();
List<T> list = findAll(new Query().with(pageable));
return new PageImpl<>(list, pageable, count);
}
@Override
public List<T> findAll(Sort sort) {
Assert.notNull(sort, "Sort must not be null!");
return findAll(new Query().with(sort));
}
@Override
public <S extends T> S insert(S entity) {
Assert.notNull(entity, "Entity must not be null!");
return mongoOperations.insert(entity, entityInformation.getCollectionName());
}
@Override
public <S extends T> List<S> insert(Iterable<S> entities) {
Assert.notNull(entities, "The given Iterable of entities not be null!");
List<S> list = Streamable.of(entities).stream().collect(StreamUtils.toUnmodifiableList());
if (list.isEmpty()) {
return list;
}
return new ArrayList<>(mongoOperations.insertAll(list));
}
@Override
public <S extends T> Page<S> findAll(final Example<S> example, Pageable pageable) {
Assert.notNull(example, "Sample must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
Query q = new Query(new Criteria().alike(example)).with(pageable);
q.addCriteria(where(SOFT_DELETION_KEY).is(true));
List<S> list =
mongoOperations.find(q, example.getProbeType(), entityInformation.getCollectionName());
return PageableExecutionUtils.getPage(
list,
pageable,
() ->
mongoOperations.count(
q, example.getProbeType(), entityInformation.getCollectionName()));
}
@Override
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
Assert.notNull(example, "Sample must not be null!");
Assert.notNull(sort, "Sort must not be null!");
Query q = new Query(new Criteria().alike(example)).with(sort);
q.addCriteria(where(SOFT_DELETION_KEY).is(true));
return mongoOperations.find(q, example.getProbeType(), entityInformation.getCollectionName());
}
@Override
public <S extends T> List<S> findAll(Example<S> example) {
return findAll(example, Sort.unsorted());
}
@Override
public <S extends T> Optional<S> findOne(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example));
q.addCriteria(where(SOFT_DELETION_KEY).is(true));
return Optional.ofNullable(
mongoOperations.findOne(q, example.getProbeType(), entityInformation.getCollectionName()));
}
@Override
public <S extends T> long count(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example));
q.addCriteria(where(SOFT_DELETION_KEY).is(true));
return mongoOperations.count(q, example.getProbeType(), entityInformation.getCollectionName());
}
@Override
public <S extends T> boolean exists(Example<S> example) {
Assert.notNull(example, "Sample must not be null!");
Query q = new Query(new Criteria().alike(example));
q.addCriteria(where(SOFT_DELETION_KEY).is(true));
return mongoOperations.exists(q, example.getProbeType(), entityInformation.getCollectionName());
}
private Query getIdQuery(Object id) {
return new Query(getIdCriteria(id));
}
private Criteria getIdCriteria(Object id) {
return where(entityInformation.getIdAttribute()).is(id).and(SOFT_DELETION_KEY).is(false);
}
private List<T> findAll(@Nullable Query query) {
if (query == null) {
return Collections.emptyList();
}
query.addCriteria(where(SOFT_DELETION_KEY).is(false));
return mongoOperations.find(
query, entityInformation.getJavaType(), entityInformation.getCollectionName());
}
}
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
import org.springframework.data.mongodb.repository.query.StringBasedMongoQuery;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import java.lang.reflect.Method;
import java.util.Optional;
public class SoftDeletionMongoRepositoryFactory extends MongoRepositoryFactory {
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private final MongoOperations operations;
public SoftDeletionMongoRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.operations = mongoOperations;
}
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(
QueryLookupStrategy.Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of(
new MongoQueryLookupStrategy(
operations, evaluationContextProvider, operations.getConverter().getMappingContext()));
}
private static class MongoQueryLookupStrategy implements QueryLookupStrategy {
private final MongoOperations operations;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
public MongoQueryLookupStrategy(
MongoOperations operations,
QueryMethodEvaluationContextProvider evaluationContextProvider,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>
mappingContext) {
this.operations = operations;
this.evaluationContextProvider = evaluationContextProvider;
this.mappingContext = mappingContext;
}
@Override
public RepositoryQuery resolveQuery(
Method method,
RepositoryMetadata metadata,
ProjectionFactory factory,
NamedQueries namedQueries) {
MongoQueryMethod queryMethod =
new MongoQueryMethod(method, metadata, factory, mappingContext);
String namedQueryName = queryMethod.getNamedQueryName();
if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedMongoQuery(
namedQuery, queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(
queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
} else {
return new SoftDeletionMongoQuery(queryMethod, operations);
}
}
}
}
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.repository.query.MongoQueryMethod;
import org.springframework.data.mongodb.repository.query.StringBasedMongoQuery;
import org.springframework.data.mongodb.repository.support.MongoRepositoryFactory;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import java.lang.reflect.Method;
import java.util.Optional;
public class SoftDeletionMongoRepositoryFactory extends MongoRepositoryFactory {
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
private final MongoOperations operations;
public SoftDeletionMongoRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.operations = mongoOperations;
}
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(
QueryLookupStrategy.Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) {
return Optional.of(
new MongoQueryLookupStrategy(
operations, evaluationContextProvider, operations.getConverter().getMappingContext()));
}
private static class MongoQueryLookupStrategy implements QueryLookupStrategy {
private final MongoOperations operations;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
public MongoQueryLookupStrategy(
MongoOperations operations,
QueryMethodEvaluationContextProvider evaluationContextProvider,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty>
mappingContext) {
this.operations = operations;
this.evaluationContextProvider = evaluationContextProvider;
this.mappingContext = mappingContext;
}
@Override
public RepositoryQuery resolveQuery(
Method method,
RepositoryMetadata metadata,
ProjectionFactory factory,
NamedQueries namedQueries) {
MongoQueryMethod queryMethod =
new MongoQueryMethod(method, metadata, factory, mappingContext);
String namedQueryName = queryMethod.getNamedQueryName();
if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedMongoQuery(
namedQuery, queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(
queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
} else {
return new SoftDeletionMongoQuery(queryMethod, operations);
}
}
}
}
@YasserOn
Copy link

YasserOn commented Jan 6, 2021

Hello author, I use it , springboot startup error message is as follows

Description:
Parameter 0 of constructor in com.ddll.log.common.core.SoftDeletionMongoRepository required a bean of type 'org.springframework.data.mongodb.repository.query.MongoEntityInformation' that could not be found.

Action:
Consider defining a bean of type 'org.springframework.data.mongodb.repository.query.MongoEntityInformation' in your configuration.

How to solve

@yurikilian
Copy link
Author

yurikilian commented Jan 6, 2021

hello @YasserOn, for a certain reason your MongontityInformation is not getting injected by the application.
Did you add the constructor for the SoftDeletionMongoRepository or add the component scan for the org.springframework.data.mongodb.repository package at a @componentscan annotation on your Application class?

best regards,
Yuri

@pedrolps12
Copy link

Hi I had the same problem, I solved it by correcting the SoftDeletionRepositoryFactoryBean.java class, which apparently has the same code as SoftDeletionMongoRepositoryFactory.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment