如何在Spring MVC控制器中应用Spring数据投影?

如何在Spring MVC控制器中应用Spring数据投影?,spring,spring-security,spring-data,Spring,Spring Security,Spring Data,直接调用数据存储库方法时,是否可以指定投影?以下是存储库代码-注意,我不想通过REST公开它,而是希望能够从服务或控制器调用它: @RepositoryRestResource(exported = false) public interface UsersRepository extends PagingAndSortingRepository<User, Long> { @Query(value = "SELECT u FROM User u WHERE ....")

直接调用数据存储库方法时,是否可以指定
投影
?以下是存储库代码-注意,我不想通过REST公开它,而是希望能够从服务或控制器调用它:

@RepositoryRestResource(exported = false)
public interface UsersRepository extends PagingAndSortingRepository<User, Long> {

    @Query(value = "SELECT u FROM User u WHERE ....")
    public Page<User> findEmployeeUsers(Pageable p);
}
@RepositoryRestResource(exported=false)
公共接口用户存储库扩展了分页和排序存储库{
@查询(value=“从用户u中选择u,其中….”)
公共页面findEmployeeUsers(可分页);
}
然后在控制器中执行以下操作:

@PreAuthorize(value = "hasRole('ROLE_ADMIN')")
@RequestMapping(value = "/users/employee")
public Page<User> listEmployees(Pageable pageable) {
    return usersRepository.findEmployeeUsers(pageable);
}
@PreAuthorize(value=“hasRole('ROLE\u ADMIN'))
@请求映射(value=“/users/employee”)
公共页面列表员工(可分页){
return users respository.findeEmployeeUsers(可分页);
}
当像上面那样直接调用
findeEmployeeUsers
方法时,有没有办法为其指定
projection

我意识到上面的代码对某些人来说可能很奇怪。。。可以通过REST公开存储库,并将
@PreAuthorize
东西放在存储库中。ThinkController是进行安全检查的更合适的地方——它更自然,测试也更简单


所以,
projection
这个东西能以某种方式传递到一个直接调用的存储库方法中吗?

不,它不是,特别是当投影通常是根据具体情况应用于查询执行的结果时。因此,它们目前被设计为选择性地应用于域类型

从最新的SpringDataFowler发行版TrainGA发行版开始,投影基础设施可以在SpringMVC控制器中以编程方式使用。只需为
SpelAwareProxyProjectionFactory
声明一个Springbean:

@Configuration
class SomeConfig {

  @Bean
  public SpelAwareProxyProjectionFactory projectionFactory() {
    return new SpelAwareProxyProjectionFactory();
  }
}
然后将其注入控制器并使用:

@Controller
class SampleController {

  private final ProjectionFactory projectionFactory;

  @Autowired
  public SampleController(ProjectionFactory projectionFactory) {
    this.projectionFactory = projectionFactory;
  }

  @PreAuthorize(value = "hasRole('ROLE_ADMIN')")
  @RequestMapping(value = "/users/employee")
  public Page<?> listEmployees(Pageable pageable) {

    return usersRepository.findEmployeeUsers(pageable).//
      map(user -> projectionFactory.createProjection(Projection.class, user);
  }
}
@控制器
类采样控制器{
私人最终项目工厂项目工厂;
@自动连线
公共采样控制器(ProjectionFactory ProjectionFactory){
this.projectionFactory=projectionFactory;
}
@预授权(value=“hasRole('ROLE\u ADMIN'))
@请求映射(value=“/users/employee”)
公共页面列表员工(可分页){
return usersRepository.findeEmployeeUsers(可分页)//
map(用户->projectionFactory.createProjection(Projection.class,用户);
}
}

查看最新版本的
页面
如何具有
映射(…)
方法,可用于动态转换页面内容。我们使用JDK 8 lambda提供转换步骤,使用
ProjectionFactory

作为@Oliver答案的补充,如果您想像SpringDataRest那样按名称查找投影(而不是在控制器中硬连线),这是您必须做的:

  • RepositoryRestConfiguration
    注入控制器。此bean允许您访问名为
    ProjectionDefinitions
    (请参阅,
    getProjectionConfiguration()
    )的类,该类充当投影元数据目录
  • 使用
    ProjectionDefinitions
    可以检索给定名称的投影类及其关联的绑定类
  • 稍后,您可以使用@Oliver详述的方法创建投影实例
  • 这是一个小型控制器,实现了我所描述的:

    @RestController
    @RequestMapping("students")
    public class StudentController {
        /**
         * {@link StudentController} logger.
         */
        private static final Logger logger =
                LoggerFactory.getLogger(StudentController.class);
    
    
        /**
         * Projections Factory.
         */
        private ProjectionFactory p8nFactory;
    
        /**
         * Projections Directory.
         */
        private ProjectionDefinitions p8nDefs;
    
        /**
         * {@link Student} repository.
         */
        private StudentRepository repo;
    
        /**
         * Class Constructor.
         *
         * @param repoConfig
         *      {@code RepositoryRestConfiguration} bean
         * @param p8nFactory
         *      Factory used to create projections
         * @param repo
         *      {@link StudentRepository} instance
         */
        @Autowired
        public StudentController(
            RepositoryRestConfiguration repoConfig, 
            ProjectionFactory p8nFactory,
            StudentRepository repo
        ) {
            super();
            this.p8nFactory = p8nFactory;
            this.p8nDefs    = repoConfig.getProjectionConfiguration();
            this.repo       = repo;
        }
        
        ...
        
        /**
         * Retrieves all persisted students.
         *
         * @param projection
         *      (Optional) Name of the projection to be applied to
         *      students retrieved from the persistence layer
         * @return
         *      {@code ResponseEntity} whose content can be a list of Students
         *      or a projected view of them
         */
        @GetMapping(path = "", produces = APPLICATION_JSON_VALUE)
        public ResponseEntity<Object> retrieveAll(
            @RequestParam(required = false) String projection
        ) {
            Class<?> type;                  // Kind of Projection to be applied
            List<?> rawData;                // Raw Entity Students
            List<?> pjData;                 // Projected students (if applies)
    
            rawData = this.repo.findAll();
            pjData  = rawData;
    
            if (projection != null) {
                type   = this.p8nDefs.getProjectionType(Student.class, projection);
                pjData = rawData
                            .stream()
                            .map(s -> this.p8nFactory.createProjection(type, s))
                            .collect(Collectors.toList());
            }
            return new ResponseEntity<>(pjData, HttpStatus.OK);
        }
    }
    
    @RestController
    @请求映射(“学生”)
    公共班级学生控制员{
    /**
    *{@link StudentController}记录器。
    */
    专用静态最终记录器=
    LoggerFactory.getLogger(StudentController.class);
    /**
    *投影厂。
    */
    私营项目工厂P8N工厂;
    /**
    *投影目录。
    */
    私人项目定义p8nDefs;
    /**
    *{@link Student}存储库。
    */
    私人储蓄回购;
    /**
    *类构造函数。
    *
    *@param repoConfig
    *{@code RepositoryRestConfiguration}bean
    *@param p8n工厂
    *用于创建投影的工厂
    *@param repo
    *{@link StudentRepository}实例
    */
    @自动连线
    公共学生控制员(
    RepositoryRestConfiguration repoConfig,
    项目工厂P8N工厂,
    存款回购
    ) {
    超级();
    this.p8nFactory=p8nFactory;
    this.p8nDefs=repoConfig.getProjectionConfiguration();
    this.repo=回购;
    }
    ...
    /**
    *检索所有保留的学生。
    *
    *@param投影
    *(可选)要应用到的投影的名称
    *从持久层检索的学生
    *@返回
    *{@code ResponseEntity},其内容可以是学生列表
    *或者它们的投影视图
    */
    @GetMapping(path=”“,products=APPLICATION\u JSON\u值)
    公共响应检索所有(
    @RequestParam(必需=false)字符串投影
    ) {
    类类型;//要应用的投影类型
    列出rawData;//原始实体学生
    列出pjData;//预计学生(如果适用)
    rawData=this.repo.findAll();
    pjData=原始数据;
    if(投影!=null){
    type=this.p8nDefs.getProjectionType(Student.class,projection);
    pjData=rawData
    .stream()
    .map(s->this.p8nFactory.createProjection(type,s))
    .collect(Collectors.toList());
    }
    返回新的响应属性(pjData,HttpStatus.OK);
    }
    }
    

    愉快的编码!

    在最新的Spring数据Rest版本中,它可以轻松完成

    您需要做的就是:

  • 将投影名称作为请求参数传递

    `/api/users/search/findEmployeeUsers?projection=userView`
    
  • 从您的服务方法返回
    PagedModel
    ,而不是
    Page

  • 完成了

    我假设您想从您的
        @Override
        @Transactional(readOnly = true)
        public PagedModel<PersistentEntityResource> listEmployees(Pageable pageable, PersistentEntityResourceAssembler resourceAssembler) {
            Page<User> users = userRepository.findEmployeeUsers(pageable);
    
            List<User> entities = users.getContent();
            entities.forEach(user -> user.setOnVacation(isUserOnVacationNow(user)));
    
            CollectionModel<PersistentEntityResource> collectionModel = resourceAssembler.toCollectionModel(entities);
    
            return PagedModel.of(collectionModel.getContent(), new PagedModel.PageMetadata(
                    users.getSize(),
                    users.getNumber(),
                    users.getTotalElements(),
                    users.getTotalPages()));
        }
    
    @BasePathAwareController
    public class UsersController {
    
        @GetMapping(value = "/users/search/findEmployeeUsers")
        ResponseEntity<PagedModel<PersistentEntityResource>> findEmployeeUsers(Pageable pageable,
                                                                        PersistentEntityResourceAssembler resourceAssembler) {
            return ResponseEntity.status(HttpStatus.OK)
                    .body(userService.listEmployees(pageable, resourceAssembler));
        }
    }
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>