resthub / resthub-spring-stack

RESThub Spring stack
http://resthub.org/spring-stack.html
Other
121 stars 66 forks source link

JPA, Reflexive class and JsonView #205

Closed mvand closed 10 years ago

mvand commented 11 years ago

User.class

@Entity 
public class User {
    @JSonView({ShortView.class,FollowersView.class,FollowingView.class}) 
    @Id 
    private Long id;

    @JsonView(ShortView.class)
    private String name;

    @JsonView(FollowersView.class)
    @ManyToMany(cascade={CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinTable(name="FOLLOWERS",
            joinColumns = {@JoinColumn(name="ID_FOLLOWED")},
            inverseJoinColumns = {@JoinColumn(name="ID_FOLLOWER")})
    private Set<User> followers = new HashSet<>();

    @JsonView(FollowingView.class)
    @ManyToMany(mappedBy = "followers", fetch = FetchType.EAGER)
    private Set<User> following = new HashSet<>();

    ...

    public static interface ShortView {}
    public static interface FollowersView {}
    public static interface FollowingView {}

}

UserController.class

@Controller
@RequestMapping(value = "/api/user")
public class UserController extends RepositoryBasedRestController<User, Long, UserRepository> {

    ...

    @RequestMapping("/{id}/followers") 
    @ResponseView(User.FollowersView.class) 
    public @ResponseBody User getFollowers(@PathVariable("id") Long id) {
        return super.findById(id);   
    }

    ...

}

Note: My UserRepository class just extends JpaRepository<User,Long>

I just want the id and the list of followers for my 'Main' User (id 1) at the url http://[hostname]/api/user/1/followers (Cf FollowersView) And, for each follower of my list, the id and the name of the 'Follower' User (Cf ShortView)

But, the @ResponseView(User.FollowersView.class) is transmitted to my FollowersList, cause i have User inside User.

<User>
    <script/>
    <id>1</id>
    <followers>
        <id>2</id>
    </followers>
</User>

And i would like:

<User>
    <script/>
    <id>1</id>
    <followers>
        <id>2</id>
        <name>john doe</name>
    </followers>
</User>

In addition, thankfully my user 2 here does not follow user 1 and do not create infinite loop, allowing this code to work. I have some trouble otherwise.

How can i avoid this behaviour and specify in my code I want to use ShortView for my follower list?

bclozel commented 11 years ago

In my opinion, you've got two very different paths here.

You can try to bend everything (your tools, your application architecture) to achieve one thing: exposing your model with a CRUD interface. It works well for many simple use cases and that's why it is provided through abstract controllers in RESThub. But down the road, you'll find many issues when dealing with more complex apps; you'll want to:

In your case, you'd have to use custom serializers to serialize your object graph depending on its depth; and solve complex ORM issues (lazyInit exceptions or huge db requests...).

Or you could decide to separate your model from what you're actually trying to convey through your API. RESThub has a dependency on ModelMapper, which helps a great deal when mapping your model to DTOs.

In your case, you could have your User entity mapped to a UserDTO and a FollowerDTO; both having different jackson annotations (os you could solve your parent/child serialization problem with @JsonManagedReference and @JsonBackReference).