【问题标题】:Using different projection for each entity subtype对每个实体子类型使用不同的投影
【发布时间】:2017-02-19 11:28:46
【问题描述】:

是否可以通过 SubTypes 和 Spring Data REST 定义不同的 Projections 使用关于类类型的最具体的投影?

此问题已在 JIRA 问题 DATAREST-739 上公开,并且还存在合并提交,但这未出现在官方 changelog 上,而且我没有找到任何文档或指南来解决当前版本的问题。

用于问题的用例示例是:

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="type")
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public abstract class Message implements Identifiable<UUID> { ... }

@Entity
@DiscriminatorValue("TEXT")
@JsonTypeName("TEXT")
public class TextMessage extends Message { ... }

@Entity
@DiscriminatorValue("TODO")
@JsonTypeName("TODO")
public class TodoMessage extends Message { Boolean isDone; }

@Projection(name = "summary", types = TodoMessage.class)
public class TodoMessageSummary { Boolean getIsDone(); }

@Projection(name = "summary", types = TextMessage.class)
public class TextMessageSummary { ... }

public interface MessageRepo extends JpaRepository<Message, UUID> { ... }

@RepositoryRestResource(excerptProjection = TodoMessageSummary.class)
public interface TodoMessageRepo extends JpaRepository<Message, UUID> { ... }

@RepositoryRestResource(excerptProjection = TextMessageSummary.class)
public interface TextMessageRepo extends JpaRepository<TextMessage, UUID> { ... }

第一个问题:如何为 MessageRepo 定义一个摘录投影,以便将 TodoMessageSummary 用于 TodoMessage 实体和 TextMessageSummary 用于 TextMessage?

第二个问题:如何为另一个具有 Message 字段的实体定义投影?假设您有以下内容:

@Projection(name = "summary", types = Dashboard.class)
public class DashboardSummary {
  List<Message> getMessages();
}

已解决:

【问题讨论】:

    标签: java spring inheritance spring-data spring-data-rest


    【解决方案1】:

    诀窍是在子类型投影中使用继承:

    @Projection(name = "summary", types = Message.class)
    public class MessageSummary { 
        @Value("#{target.getClass().getSimpleName()}")
        String getType();    
    }
    
    @Projection(name = "summary", types = TextMessage.class)
    public class TextMessageSummary extends MessageSummary { ... }
    
    @Projection(name = "summary", types = TodoMessage.class)
    public class TodoMessageSummary extends MessageSummary {
         Boolean getIsDone();
    }
    

    Spring REST @RepositoryRestResource 使用具体子类型投影返回一个消息数组(isDone 必须出现在 TodoMessage 实例中)

    这个问题有点复杂,如果你需要把同样的变成一个extendend @RequestMapping 变成一个Controller,这样做我使用下一个snnipeed:

        Page<Message> results = repository.findAll(predicate, pageable);
        Converter<? super Message, ? extends MessageSummary> converter= l -> {
            if(l instanceof TextMessage){
                return projectionFactory.createProjection(TextMessageSummary.class,l);
            }
            else if(l instanceof TodoMessage){
                return projectionFactory.createProjection(TodoMessageSummary.class,l);
            }
            else { 
                return projectionFactory.createProjection(MessageSummary.class,l);
            }
        };
        Page<MessageSummary> projected =results.map(converter);
        return pagedAssembler.toResource(projected);
    

    请注意,如果前端只需要 resource type info 用于只读目的(即对于 POST/PUT 使用具体的子类型端点),我认为这并不是真正需要使用 @JsonTypeInfo 因为在投影中使用 SpEL 可以更轻松灵活地获取此类信息:

    @Value("#{target.getClass().getSimpleName()}")
    String getType();
    

    【讨论】:

      【解决方案2】:

      是的,这是可能的。我们的应用程序中有这样的子类型结构。

      @JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
              include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true)
      public abstract class Vehicle {
        protected VehicleType type;
      }
      
      public class Plane extends Vehicle {
        private String title;
      }
      

      他们的预测:

      public interface VehicleProjection {
        String getType();
      }
      
      @Projection(name = "default", types = Plane.class)
      public interface PlaneProjection extends VehicleProjection {
        String getTitle();
      }
      

      其余存储库:

      @RepositoryRestResource(collectionResourceRel = "collectionName", path = "path",
              excerptProjection = VehicleProjection.class)
      public interface RestVehicleRepository<T extends Vehicle> extends MongoRepository<T, String> {
      
      }
      

      我们还在配置中注册了这些预测。不确定您的情况是否需要它,因为我们对每个子类型都有不止一个投影:

      @Configuration
      public class CustomRestConfigurerAdapter extends RepositoryRestConfigurerAdapter {
       @Override
              public void configureRepositoryRestConfiguration(final RepositoryRestConfiguration config) {
                config.getProjectionConfiguration().addProjection(PlaneProjection.class,
                          "default", Plane.class);
        }
      }
      

      【讨论】:

      • 感谢您的回答,我的麻烦是我需要控制器自定义映射上的子类型投影。我终于解决了它,在这个过程中它让我了解了其他有用的问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-13
      相关资源
      最近更新 更多