【问题标题】:Spring Data Rest Repository with abstract class / inheritance具有抽象类/继承的 Spring Data Rest Repository
【发布时间】:2017-02-08 19:01:49
【问题描述】:

我无法在类继承工作的情况下获得 Spring Data Rest。

我想要一个 JSON 端点来处理我所有的具体类。

回购:

public interface AbstractFooRepo extends KeyValueRepository<AbstractFoo, String> {}

抽象类:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = MyFoo.class, name = "MY_FOO")
})
public abstract class AbstractFoo {
  @Id public String id;
  public String type;
}

具体类:

public class MyFoo extends AbstractFoo { }

现在当用{"type":"MY_FOO"} 调用POST /abstractFoos 时,它告诉我:java.lang.IllegalArgumentException: PersistentEntity must not be null!

这似乎发生了,因为 Spring 不知道 MyFoo

有没有办法告诉 Spring Data REST 关于MyFoo 而无需为其创建存储库和 REST 端点?

(我使用的是 Spring Boot 1.5.1 和 Spring Data REST 2.6.0)

编辑:

Application.java:

@SpringBootApplication
@EnableMapRepositories
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

【问题讨论】:

  • 你读过这个吗:stackoverflow.com/a/25241995/1201725
  • 请出示您的完整课程。
  • 这些是我完整的课程。剩下的唯一类是我的默认应用程序类。我会附加它,但它有点没用。

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


【解决方案1】:

我正在使用 Spring Boot 1.5.1 和 Spring Data Release Ingalls

KeyValueRepository 不适用于继承。它使用每个已保存对象的类名来查找相应的键值存储。例如。 save(new Foo()) 将保存的对象放在Foo 集合中。而abstractFoosRepo.findAll() 将在AbstractFoo 集合中查找,并且找不到任何Foo 对象。

这是使用 MongoRepository 的工作代码:

Application.java

默认 Spring Boot 应用程序启动器。

@SpringBootApplication
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

AbstractFoo.java

  • 我已经测试了include = JsonTypeInfo.As.EXISTING_PROPERTYinclude = JsonTypeInfo.As.PROPERTY。两者似乎都可以正常工作!

  • 甚至可以使用自定义 JacksonModule 注册 Jackson 子类型。

  • 重要提示: 强烈推荐@RestResource(path="abstractFoos")。否则_links.self 链接将指向/foos/bars 而不是/abstractFoos


@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@JsonSubTypes({
  @JsonSubTypes.Type(value = Foo.class, name = "MY_FOO"),
  @JsonSubTypes.Type(value = Bar.class, name = "MY_Bar")
})
@Document(collection="foo_collection")
@RestResource(path="abstractFoos")
public abstract class AbstractFoo {
  @Id public String id;
  public abstract String getType();
}

AbstractFooRepo.java

这里没什么特别的

public interface AbstractFooRepo extends MongoRepository<AbstractFoo, String> { }

Foo.java & Bar.java

@Persistent
public class Foo extends AbstractFoo {
  @Override
  public String getType() {
    return "MY_FOO";
  }
}

@Persistent
public class Bar extends AbstractFoo {
  @Override
  public String getType() {
    return "MY_BAR";
  }
}

FooRelProvider.java

  • 如果没有这部分,对象的输出将被分隔在_embedded.foos_embedded.bars 下的两个数组中。
  • supports 方法确保对于扩展 AbstractFoo 的所有类,对象将被放置在 _embedded.abstractFoos 中。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class FooRelProvider extends EvoInflectorRelProvider {
  @Override
  public String getCollectionResourceRelFor(final Class<?> type) {
    return super.getCollectionResourceRelFor(AbstractFoo.class);
  }

  @Override
  public String getItemResourceRelFor(final Class<?> type) {
    return super.getItemResourceRelFor(AbstractFoo.class);
  }

  @Override
  public boolean supports(final Class<?> delimiter) {
    return AbstractFoo.class.isAssignableFrom(delimiter);
  }
}

编辑

  • @Persistent 添加到Foo.javaBar.java。 (将其添加到 AbstractFoo.java 不起作用)。如果没有这个注释,我在继承的类中尝试使用 JSR 303 验证注释时得到 NullPointerExceptions。

重现错误的示例代码:

public class A {
  @Id public String id;
  @Valid public B b;

  // @JsonTypeInfo + @JsonSubTypes
  public static abstract class B {
    @NotNull public String s;
  }

  // @Persistent <- Needed!
  public static class B1 extends B { }
}

【讨论】:

  • 如果使用它来将数据移入和移出 mongodb,请注意:gist.github.com/letalvoj/978e6c975398693fc6843c5fe648416d
  • @Benjamin 我在 SDR 中看到了关于继承的 JIRA 问题,你提到你可以使用继承层次结构的投影。如果您能在此处更新有关如何使预测发挥作用的信息,我将不胜感激
【解决方案2】:

请参阅此已解决的 jira task 中的讨论,以了解 spring-data-rest 当前支持的有关 JsonTypeInfo 的详细信息。而这个jira task 仍然缺少什么。

总结一下 - 目前只有 @JsonTypeInfoinclude=JsonTypeInfo.As.EXISTING_PROPERTY 用于序列化和反序列化。

此外,您需要 spring-data-rest 2.5.3 (Hopper SR3) 或更高版本才能获得此有限支持。

请查看我的示例应用程序 - https://github.com/mduesterhoeft/spring-data-rest-entity-inheritance/tree/fixed-hopper-sr3-snapshot

使用include=JsonTypeInfo.As.EXISTING_PROPERTY,类型信息是从常规属性中提取的。一个例子有助于理解这种添加类型信息的方式:

抽象类:

@Entity @Inheritance(strategy= SINGLE_TABLE)
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
        include=JsonTypeInfo.As.EXISTING_PROPERTY,
        property="type")
@JsonSubTypes({
        @Type(name="DECIMAL", value=DecimalValue.class),
        @Type(name="STRING", value=StringValue.class)})
public abstract class Value {

    @Id @GeneratedValue(strategy = IDENTITY)
    @Getter
    private Long id;

    public abstract String getType();
}

还有子类:

@Entity @DiscriminatorValue("D")
@Getter @Setter
public class DecimalValue extends Value {

    @Column(name = "DECIMAL_VALUE")
    private BigDecimal value;

    public String getType() {
        return "DECIMAL";
    }
}

【讨论】:

  • 这不起作用。我正在使用 spring-data-rest 2.6.0。并使用KeyValueRepository(无 JPA)。我的AbstractFoo 类现在有include=JsonTypeInfo.As.EXISTING_PROPERTY 和一个抽象方法public abstract String getType()。将数据发布到/abstractFoos 时,我仍然得到PersistentEntity must not be null!。 (还使用 Spring Boot 1.4.4 尝试了 Hopper SR7)
  • 你在github上有完整的源代码吗?想仔细看看。否则,很难猜出哪里出了问题。
  • 我刚刚从KeyValueRepository 切换到MongoRepository,现在它有点工作了!似乎KeyValueRepository 无法处理继承。 还有一些问题: 执行GET /abstractFoos 时,我的对象位于_embedded.foos_embedded.bars 下。为什么它们不只是一个集合?现在我无法对它们进行排序。 (可修复:stackoverflow.com/questions/29021221/…
  • 我将发布一个对MongoRepository 行为正确的答案。我遇到了很多陷阱...感谢您的帮助。你会得到奖励:)
  • 简单类名也可以作为“作为现有属性”的鉴别器: public String getType2() { return getClass().getSimpleName().toUpperCase();}
猜你喜欢
  • 2020-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-16
  • 2011-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多