javaspring-mvcmodelattribute

Should the name of a @ModelAttribute be the same as the model in a session


Should the name of a @ModelAttribute be the same as a model before it would be saved in a session? I have a an attribute with a different name from the model but it is not saving in the session.

DesignTacoController

@Slf4j
@Controller
@RequestMapping("/design")
@SessionAttributes("tacoOrder")
public class DesignTacoController {

 @ModelAttribute(name = "tacoOrder")
    public TacoOrderData order(){
        return new TacoOrderData();
    }

    @ModelAttribute(name = "taco")
    public TacoData taco(){
        return new TacoData();
    }

    @GetMapping
    public String showDesignForm(){
        return "view/design";
    }

    @PostMapping
    public String processOrder(@Valid TacoData taco,
                               Errors errors, @ModelAttribute TacoOrderData tacoOrder, HttpSession Session){
        if (errors.hasErrors()){
            return "view/design";
        }
        tacoOrder.addTaco(taco);
        System.out.println("Processing taco: {}", taco);
        System.out.println("tacoOrder model in session: "+ Session.getAttribute("tacoOrder"));
        return "redirect:/orders/current";
    }
}

OrderController

@Slf4j
@Controller
@RequestMapping("/orders")
@SessionAttributes("tacoOrder")
public class OrderController {
    @GetMapping("/current")
    public String orderForm(){
            return "view/orderForm";
        }

    @PostMapping
    public String processOrder(@Valid TacoOrderData order,
                               Errors error, SessionStatus sessionStatus) {

        if (error.hasErrors()){return "view/orderForm";}
        log.info("Order submitted: {}", order);
        sessionStatus.setComplete();
        return "redirect:/";
    }

}

Taco Model

@Data
public class TacoData {
    private Long id;
    private Date createdAt;
    @NotNull
    @Size(min = 5, message = "Name must be at least 5 characters long")
    private String name;
    @NotNull
    @Size(min = 1, message = "You must choose at least 1 IngredientData")
    private List<IngredientData> ingredients;

}

TacoOrder Model*

@Data
public class TacoOrderData {

    private Long id;
    private Date placedAt;
    private String deliveryName;
    private String deliveryCity;
    private String deliveryStreet;
    private String deliveryState;
    private String deliveryZip;
    private String ccNumber;
    private String ccExpiration;
    private String ccCVV;

    private List<TacoData> tacos = new ArrayList<>();

    public void addTaco(TacoData taco){
        this.tacos.add(taco);
    }
}

Results when Post action in DesignTacoController is executed

Processing taco: TacoData(id=null, createdAt=null, name=tacos, ingredients=[IngredientData(id=FLTO, name=Flour Tortilla, type=WRAP), IngredientData(id=CHED, name=Cheddar, type=CHEESE), IngredientData(id=SLSA, name=Salsa, type=SAUCE)])

tacoOrder model in session: TacoOrderData(id=null, placedAt=null, deliveryName=null, deliveryCity=null, deliveryStreet=null, deliveryState=null, deliveryZip=null, ccNumber=null, ccExpiration=null, ccCVV=null, tacos=[])


Solution

  • In Spring MVC, when you use @SessionAttributes to store a model attribute in the session, the name specified in @ModelAttribute(name = "...") should match the name of the attribute in the model. In your case, it seems that the names do not match:

    1. In your DesignTacoController, you have:
    @ModelAttribute(name = "tacoOrder")
    public TacoOrderData order(){
        return new TacoOrderData();
    }
    
    @ModelAttribute(name = "taco")
    public TacoData taco(){
        return new TacoData();
    }
    

    Here, you are specifying the names "tacoOrder" and "taco" for your model attributes.

    1. However, in your processOrder method, you are using the @ModelAttribute annotation without specifying the name:
    public String processOrder(@Valid TacoData taco,
                               Errors errors, @ModelAttribute TacoOrderData tacoOrder, HttpSession Session){
        // ...
    }
    

    In this method, you should specify the name using @ModelAttribute("tacoOrder") to match the name specified in @SessionAttributes.

    Here's the modified processOrder method:

    public String processOrder(@Valid TacoData taco,
                               Errors errors, @ModelAttribute("tacoOrder") TacoOrderData tacoOrder, HttpSession Session){
        // ...
    }
    

    With this change, Spring will correctly associate the "tacoOrder" attribute in the session with the tacoOrder parameter in your processOrder method. Make sure to apply a similar change if needed in your OrderController as well.

    Also, ensure that your view templates are using the correct attribute names when rendering the forms, as the name consistency is crucial for the data to be properly saved and retrieved from the session.