我正在将 spring-data 用于带有 querydsl 的 mongodb。 我有一个存储库
public interface DocumentRepository extends MongoRepository<Document, String> ,QueryDslPredicateExecutor<Document> {}
和一个实体
@QueryEntity
public class Document {
private String id;
private String name;
private String description;
private boolean locked;
private String message;
}
我需要加载包含 id 和名称信息的文档列表。 因此,只有 id 和 name 应该在我的实体中加载和设置。 我认为查询投影是正确的词。 支持吗?
此外我需要实现一些延迟加载逻辑。 存储库中是否有类似“跳过”和“限制”的功能?
这涉及很多方面,不幸的是,这不是一个问题,而是多个问题。
对于投影,您可以简单地使用
fields
注释的 @Query
属性:
interface DocumentRepository extends MongoRepository<Document, String>, QuerydslPredicateExecutor<Document> {
@Query(value = "{}", fields = "{ 'id' : 1, 'name' : 1 }")
List<Document> findDocumentsProjected();
}
您可以将其与查询派生机制(通过不设置
query
)、分页(见下文)甚至返回子句中的专用投影类型结合起来(例如仅包含 DocumentExcerpt
和 id
的 name
)字段)。
存储库抽象完全支持分页。通过扩展基本接口,您已经获得了
findAll(Pageable)
和该方法的 Querydsl 特定版本。您还可以在查找器方法中使用分页 API,添加 Pageable
作为参数并返回 Page
Page<Document> findByDescriptionLike(String description, Pageable pageable)
请参阅参考文档了解更多信息。
据我所知,默认的 Spring Data 存储库不支持投影。如果您想确保仅将投影从数据库发送到您的应用程序(例如出于性能原因),您将必须自己实现相应的查询。将自定义方法添加到标准存储库的扩展中应该不会花费太多精力。
如果您只是想对调用您的应用程序的某些客户端隐藏某些字段的内容,您通常会使用另一组实体对象,并在其之间有适当的映射。对不同级别的细节使用相同的 POJO 总是令人困惑,因为您不知道某个字段是否实际上是
null
或者该值是否只是在特定上下文中被抑制。
我目前无法测试任何代码,但根据 QueryDslPredicateExecutor 的文档,方法 findAll(predicate, pageable) 应该是你想要的:
Page
对象,该对象是您的 Iterable
的常规
Document
Pageable
,你可以例如使用 PageRequest
;将其初始化为已知的跳过和限制值应该是微不足道的根据此答案 -> 问题 <- I implemeted following solution.
实体
@QueryEntity
public class Document extends AbstractObject {
}
自定义查询dslMongoRepository
public interface CustomQuerydslMongoRepository<T extends AbstractObject,ID extends Serializable> extends MongoRepository<T, ID> ,QueryDslPredicateExecutor<T>{
Page<T> findAll(Predicate predicate, Pageable pageable,Path... paths);
Page<T> findAll(Predicate predicate, Pageable pageable,List<Path> projections);
}
自定义查询dslMongoRepository实现
public class CustomQuerydslMongoRepositoryImpl<T extends AbstractObject,ID extends Serializable> extends QueryDslMongoRepository<T,ID> implements CustomQuerydslMongoRepository<T,ID> {
//All instance variables are available in super, but they are private
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final PathBuilder<T> pathBuilder;
private final MongoOperations mongoOperations;
public CustomQuerydslMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations) {
this(entityInformation, mongoOperations,DEFAULT_ENTITY_PATH_RESOLVER);
}
public CustomQuerydslMongoRepositoryImpl(MongoEntityInformation<T, ID> entityInformation, MongoOperations mongoOperations, EntityPathResolver resolver) {
super(entityInformation, mongoOperations, resolver);
this.path=resolver.createPath(entityInformation.getJavaType());
this.pathBuilder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.mongoOperations=mongoOperations;
}
@Override
public Page<T> findAll( Predicate predicate, Pageable pageable,Path... paths) {
Class<T> domainType = getEntityInformation().getJavaType();
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(mongoOperations, domainType);
long total = query.count();
List<T> content = total > pageable.getOffset() ? query.where(predicate).list(paths) : Collections.<T>emptyList();
return new PageImpl<T>(content, pageable, total);
}
@Override
public Page<T> findAll(Predicate predicate, Pageable pageable, List<Path> projections) {
Class<T> domainType = getEntityInformation().getJavaType();
MongodbQuery<T> query = new SpringDataMongodbQuery<T>(mongoOperations, domainType);
long total = query.count();
List<T> content = total > pageable.getOffset() ? query.where(predicate).list(projections.toArray(new Path[0])) : Collections.<T>emptyList();
return new PageImpl<T>(content, pageable, total);
}
}
自定义存储库工厂
public class CustomQueryDslMongodbRepositoryFactoryBean<R extends QueryDslMongoRepository<T, I>, T, I extends Serializable> extends MongoRepositoryFactoryBean<R, T, I> {
@Override
protected RepositoryFactorySupport getFactoryInstance(MongoOperations operations) {
return new CustomQueryDslMongodbRepositoryFactory<T,I>(operations);
}
public static class CustomQueryDslMongodbRepositoryFactory<T, I extends Serializable> extends MongoRepositoryFactory {
private MongoOperations operations;
public CustomQueryDslMongodbRepositoryFactory(MongoOperations mongoOperations) {
super(mongoOperations);
this.operations = mongoOperations;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new CustomQuerydslMongoRepositoryImpl(getEntityInformation(metadata.getDomainType()), operations);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CustomQuerydslMongoRepository.class;
}
}
}
实体存储库
public interface DocumentRepository extends CustomQuerydslMongoRepository<Document, String>{
}
服务中的使用
@Autowired
DocumentRepository repository;
public List<Document> getAllDocumentsForListing(){
return repository.findAll( QDocument.document.id.isNotEmpty().and(QDocument.document.version.isNotNull()), new PageRequest(0, 10),QDocument.document.name,QDocument.document.version).getContent();
}
QueryDSL 和 MongoRepository 已更新,支持通过 FetchableFluentQuery 进行投影和分页。
实体
@Document
public class User {
@Id // making this variable as ID, will be auto-generated by MongoDB
private String id;
@NonNull
private String email;
private String password;
@NonNull
private UserStatus status;
@NonNull
private String firstName;
@NonNull
private String lastName;
}
存储库
@Repository
public interface UserRepository extends MongoRepository<User, String>
, QuerydslPredicateExecutor<User> {
}
利用 QueryDSL 和 FetchableFluentQuery
public PagedDTO<UserSearchDTO> get(int page,
int size,
Optional<String> firstName,
Optional<String> lastName) {
var paging = PageRequest.of(page, size);
var builder = new BooleanBuilder();
if (firstName.isPresent()) {
builder.and(QUser.firstName.eq(firstName.get()));
}
if (lastName.isPresent()) {
builder.and(QUser.lastName.eq(lastName.get()));
}
var results = this.userRepository.findBy(builder.getValue(), getFetchableFluentQueryPageFunction(paging));
return new PagedDTO<UserSearchDTO>()
.setItems(results.getContent())
.setItemsCount(results.getNumberOfElements())
.setCurrentPage(results.getNumber() + 1)
.setTotalPages(results.getTotalPages())
.setTotalItems(results.getTotalElements());
}
private Function<FluentQuery.FetchableFluentQuery<user>, Page<UserSearchDTO>> getFetchableFluentQueryPageFunction(PageRequest paging) {
Function<FluentQuery.FetchableFluentQuery<user>, Page<UserSearchDTO>> queryFunction =
fetchableFluentQuery ->
fetchableFluentQuery
.project("id", "email", "lastName")
.page(paging)
.map(user -> getUserSearchDTO(user));
return queryFunction;
}