为每个实体子类型使用不同的投影

时间:2017-02-19 11:28:46

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

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

此问题已在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定义一个摘录投影,以便为TodoMessage实体使用TodoMessageSummary,为TextMessage使用TextMessageSummary?

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

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

解决:

2 个答案:

答案 0 :(得分:1)

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

@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);
  }
}

答案 1 :(得分: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实例中)

如果你需要在@RequestMapping中将extendend Controller变成 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); ,这个问题会更复杂一些,这样做我会使用下一个snnipeed:

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

请注意,如果只需要前端的资源类型信息以便于读取(即对于POST / PUT使用具体的子类型端点)我认为并不是真的需要使用 @JsonTypeInfo < / em>因为将SpEL用于投影可以让这种类型的信息更加简单灵活:

watch i