我有两个JPA实体,一个具有SDR导出的存储库,另一个具有Spring MVC控制器和一个未导出的存储库.
MVC暴露实体引用了SDR管理实体.参见下面的代码参考.
从UserController检索用户时,问题出现. SDR管理实体不会序列化,似乎Spring可能正在尝试在响应中使用HATEOAS参考.
以下是完全填充用户的GET如下所示:
{ "username": "foo@gmail.com", "enabled": true, "roles": [ { "role": "ROLE_USER", "content": [], "links": [] // why the content and links? } // no places? ] }
如何使用嵌入式SDR管理实体清楚地从Controller中返回User实体?
Spring MVC管理
实体
@Entity @Table(name = "users") public class User implements Serializable { // UID @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @JsonIgnore private Long id; @Column(unique = true) @NotNull private String username; @Column(name = "password_hash") @JsonIgnore @NotNull private String passwordHash; @NotNull private Boolean enabled; // No Repository @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER) @NotEmpty private Set<UserRole> roles = new HashSet<>(); // The SDR Managed Entity @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) @JoinTable(name = "user_place", joinColumns = { @JoinColumn(name = "users_id") }, inverseJoinColumns = { @JoinColumn(name = "place_id")}) private Set<Place> places = new HashSet<>(); // getters and setters }
回购
@RepositoryRestResource(exported = false) public interface UserRepository extends PagingAndSortingRepository<User, Long> { // Query Methods }
调节器
@RestController public class UserController { // backed by UserRepository private final UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; } @RequestMapping(path = "/users/{username}", method = RequestMethod.GET) public User getUser(@PathVariable String username) { return userService.getByUsername(username); } @RequestMapping(path = "/users", method = RequestMethod.POST) public User createUser(@Valid @RequestBody UserCreateView user) { return userService.create(user); } // Other MVC Methods }
SDR管理
实体
@Entity public class Place implements Serializable { // UID @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @NotBlank private String name; @Column(unique = true) private String handle; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "address_id") private Address address; @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "contact_info_id") private ContactInfo contactInfo; // getters and setters }
回购
public interface PlaceRepository extends PagingAndSortingRepository<Place, Long> { // Query Methods }
简而言之:Spring Data REST和Spring HATEOAS劫持ObjectMapper,并希望将资源之间的关系用作链接,而不是嵌入资源.
拿一个与另一个实体有一一对应关系的实体:
@Entity public class Person { private String firstName; private String lastName; @OneToOne private Address address; }
SDR / HATEOAS将返回地址作为链接:
{ "firstName": "Joe", "lastName": "Smith", "_links": { "self": { "href": "http://localhost:8080/persons/123123123" }, "address": { "href": "http://localhost:8080/addresses/9127391273" } } }
默认格式可以根据您在类路径上的具体情况而有所不同.我相信这是我的例子中的HAL,这是你包括SDR和HATEOAS的默认值.根据所述配置可能不同但相似.
当地址由SDR管理时,Spring将会执行此操作.如果它不是由SDR管理的,那么它将包含响应中的整个地址对象.我怀疑单独解释你所看到的行为.
角色
您没有包含关于UserRole的信息,但是根据您的代码,它似乎可能不在用户管理之外,因此没有注册一个Spring数据存储库.如果是这种情况,那就是为什么它被嵌入 – 没有其他的“链接”的存储库.
角色下的内容和链接看起来像Spring试图将其序列化为一个页面.一般情况下,内容将会有一系列的资源,链接将会有诸如“self”的链接或其他资源的链接.我不知道是什么原因造成的.
地点
Place拥有自己的Spring Data存储库,因此它将被视为一个被管理实体,并且被链接而不是嵌入.我怀疑你正在寻找的是一个投影.结帐春天 documentation on projections .它看起来像这样:
@Projection(name = "embedPlaces", types = { User.class }) interface EmbedPlaces { String getUsername(); boolean isEnabled(); Set<Place> getPlaces(); }
这应该是序列化用户名,启用和角色,并省略一切.我没有亲自使用预测,所以我不能保证它的工作效率,但这是文档中的解决方案.
编辑:当我们在此时,请注意,这也适用于创建或更新资源. Spring将资源作为URL.因此,如果我正在创建一个新人,我的身体可能看起来像个人/地址示例:
{ "firstName": "New", "lastName": "Person", "address": "http://localhost:8080/addresses/1290312039123" }
很容易忘记这些东西,因为广泛,广泛,广泛,广泛,绝大多数的“REST”API不是REST,SDR / HATEOAS采取REST的观点(例如,应该是REST).
http://stackoverflow.com/questions/37232377/mixing-spring-mvc-spring-data-rest-results-in-odd-mvc-responses