Values of @PathVariable and @ModelAttribute overla

2020-03-12 03:44发布

I have an User object stored in the session with @SessionAttributes. And a straight-forward method decorated with @ModelAttribute in order to initialize it whenever the session's value is null.

User class:

@Entity
@Table( name="USER")
public class User implements java.io.Serializable {

    private Long id;
    private String username;
    private String password;
    ....

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="ID")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    ...

Controller:

@RequestMapping("/item")
@Controller
@SessionAttributes({"user"})
public class MyController {

@ModelAttribute method:

@ModelAttribute("user")
    public User createUser(Principal principal) {
        return userService.findByUsername(principal.getName());
    }

It all seems to work as expected except in this particular method:

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String showItem(@PathVariable("id") Long id, @ModelAttribute("user") User user,
            Model uiModel) {
   ...    
}

The problem is that User.id is being set with @PathVariable("id"). I believe I ran into this with @RequestParam too. I'm assuming that's because both have the same name and type. After reading Spring's documentation (see below) I'm assuming this is expected behavior:

The next step is data binding. The WebDataBinder class matches request parameter names — including query string parameters and form fields — to model attribute fields by name. Matching fields are populated after type conversion (from String to the target field type) has been applied where necessary.

However, I would think this scenario is fairly common, how are other people handling this? If my findings are correct and this is expected behavior (or bug), this seems to be very error prone.

Possible solutions:

  1. Change @PathVariable("id") to @PathVariable("somethingElse"). Works but it's not as straightforward with @RequestParam (e.g. I don't know how to change jqgrid's request parameter id to something else but this is another issue).
  2. Change @PathVariable("id") type from Long to Int. This will make User.id and id types differ but the cast to Long looks ugly :)
  3. Don't use @ModelAttribute here and query the DB for User again. Not consistent with other methods and involves redundant DB calls.

Any suggestions?

2条回答
叼着烟拽天下
2楼-- · 2020-03-12 03:45

How about this approach -

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String showItem(@PathVariable("id") Long id,
            Model uiModel) {
       User user = (User)uiModel.asMap().get("user");
   ...    
}
查看更多
狗以群分
3楼-- · 2020-03-12 04:05

use @SessionAttribute

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String showItem(@PathVariable("id") Long id, @SessionAttribute("user") User user,
            Model uiModel) {
   ...    
}
查看更多
登录 后发表回答