2016-05-14 136 views
15

我有兩個JPA實體,一個帶有SDR導出的存儲庫,另一個帶有Spring MVC控制器和一個非導出的存儲庫。混合彈簧MVC +彈簧數據休息導致奇怪的MVC響應

MVC公開實體具有對SDR被管實體的引用。請參閱下面的代碼參考。

UserController檢索User時,問題就會發揮作用。 SDR管理實體不會序列化,並且似乎Spring可能試圖在響應中使用HATEOAS參考。

這裏是一個完全填充User一個GET樣子:

{ 
    "username": "[email protected]", 
    "enabled": true, 
    "roles": [ 
    { 
     "role": "ROLE_USER", 
     "content": [], 
     "links": [] // why the content and links? 
    } 
    // no places? 
    ] 
} 

如何,我不是從我的控制器與嵌入式SDR返回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 = "https://stackoverflow.com/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 
} 
+0

我假設你有PlaceRepository的@Repository註釋 - 只是沒有發佈在這裏?你可以添加例外的文字嗎? – lenach87

+0

@ lenach87 - 除非需要進一步配置它,否則SDR不需要「@ Repository」註釋。也沒有例外,只是沒有序列化。 – bvulaj

+0

也許你的JPA實現存在問題?如果你使用的是Hibernate,它可以使得只有一個包的急切加載。你可以創建一個自定義查詢來迫使它加載,或者只是在將它作爲響應發送之前調用屬性的訪問者(這將強制hibernate加載屬性包)。 –

回答

-1

您可以很好地在Controller中使用@ResponseEntity,然後在ResponseEntity中設置User Object。

請參見下面的例子:

ResponseEntity<User> respEntity = new ResponseEntity<User>(user, HttpStatus.OK); 

然後在客戶端可以調用,restTemplate.getForEntity

請參考下面restTemplate文檔:

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#getForObject-java.lang.String-java.lang.Class-java.lang.Object...-

+0

我不太清楚這對我有什麼幫助。 – bvulaj

1

一言以蔽之:春天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管理Address時,Spring將執行此操作。如果它完全不是由SDR管理的,它將在響應中包含整個地址對象。我懷疑這一點解釋了你所看到的行爲。

角色

您還沒有列入UserRole信息,但根據您的代碼似乎是可能的User之外不進行管理,因此不具有彈簧數據倉庫註冊。如果是這種情況,這就是爲什麼它被嵌入 - 沒有其他存儲庫'鏈接'。

在角色下的contentlinks看起來像Spring試圖序列化它像Page。通常content將具有一系列資源,並且links將具有諸如「自我」的鏈接或指向其他資源的鏈接。我不確定是什麼原因造成的。

廣場

地方都有它自己的Spring數據倉庫,所以它會被當作一個管理實體,並鏈接到而不是嵌入。我懷疑你在找什麼是投影。結帳春天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)。