I am getting an invalid property exception when I am trying to bind a collection in my model object in my form object. I was trying some trick and now I think that the problem is hibernate session with spring MVC , so in the controller I edited create function so that before my form saves, I try to get session and get list of typesite's but still get the same error:
org.springframework.beans.InvalidPropertyException: Invalid property ‘siteesTypeSite[idTypeSite]’ of bean class [model.Sites]:
Property referenced in indexed property path ‘siteesTypeSite[idTypeSite]’ is neither an array nor a List nor a Map; returned value was [2]
Is there something wrong with my mapping?
Sites.java mapping
public class Sites implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#JsonBackReference("site-typeSite")
#LazyCollection(LazyCollectionOption.FALSE)
#ManyToOne
#JoinColumn(name = “idTypeSite”)
private TypeSites siteesTypeSite;
Getter/setters
}
TypeSites.java mapping :
public class TypeSites implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="idTypeSite")
private int idTypeSite;
private String typeSite;
#LazyCollection(LazyCollectionOption.FALSE)
#JsonManagedReference("site-typeSite")
#OneToMany(mappedBy = “siteesTypeSite”,fetch = FetchType.LAZY)
// private Set<Sites> sitees= new HashSet<Sites>(0);
private List<Sites> sitees = new AutoPopulatingList<Sites>(Sites.class);
Getter/setters
}
controller class :
#RequestMapping(method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> create(
#ModelAttribute("site") #Valid Sites site,
#ModelAttribute("typeSites") TypeSites typeSites,
#RequestParam(required = false) String searchFor,
#RequestParam(required = false,
defaultValue = DEFAULT_PAGE_DISPLAYED_TO_USER) int page,
Locale locale) {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
HttpSession session = attr.getRequest().getSession();
TypeSites existingTypeSites = (TypeSites) ((Session) session).get(TypeSites.class, site.getSiteesTypeSite().getIdTypeSite());
site.setSiteesTypeSite(existingTypeSites);
siteService.save(site);
}
Angularjs code :
$scope.createObject = function (newObjectForm) {
if (!newObjectForm.$valid) {
$scope.displayValidationError = true;
return;
}
$scope.lastAction = ‘create';
var url = $scope.url;
var config = {headers: {‘Content-Type': ‘application/x-www-form-urlencoded; charset=UTF-8′}};
$scope.addSearchParametersIfNeeded(config, false);
$scope.startDialogAjaxRequest();
$scope.sites.siteesTypeSite =JSON.parse($scope.sites.siteesTypeSite);
$http.post(url, $.param($scope.sites), config)
.success(function (data) {
$scope.finishAjaxCallOnSuccess(data, “#addObjectsModal”, false);
})
.error(function(data, status, headers, config) {
$scope.handleErrorInDialogs(status);
});
};
JSP : Here I a call to another angular controller to get a list of type site on the select
<div ng-controller="siteTypesiteController">
<select required
ng-model="sites.siteesTypeSite"
name="siteesTypeSite"
ng-options="typesites as typesites.typeSite for typesites in page.source "
>
<option value="">-- Select Type site --</option>
</select><br>
and I have this before submit in the browser :
Object {codeOracle: "test", codeGSM: "test", area: "test", siteesTypeSite: Object}
area: "test"
codeGSM: "test"
codeOracle: "test"
siteesTypeSite: Object
idTypeSite: 2
sitees: Array[0]
typeSite: "Public"
__proto__: Object
__proto__: Object
The problem is : I can't submit the nested object , if I change value of select to typesites.typeSite instead of typesites like this
<div ng-controller="siteTypesiteController">
<select required
ng-model="sites.siteesTypeSite"
name="siteesTypeSite"
ng-options="typesites.typeSite as typesites.typeSite for typesites in page.source "
>
<option value="">-- Select Type site --</option>
</select><br>
The submit work fine, but I have two inserts : an insert in the typesite table, and an insert in the site table with the new foreign key.
Related
I am trying to make a Recipe Builder form using Thymeleaf.
The functionality I am trying to implement involves adding a List of selected objects (ingredients) to the bound recipe.
Here is a snippet of the Form from the Template file:
<form action="#" th:action="#{/recipes/userRecipes/new/save}" th:object="${userRecipe}"
method="post">
...
<td>Ingredients:</td>
<td>
<ul>
<li th:each="ingredient : ${allIngredients}">
<input type = "checkbox" th:name="sel_ingredients" th:value="${ingredient.id}" />
<label th:text="${ingredient.name}">Ingredient</label>
</li>
</ul>
</td>
</tr>
...
</table>
</form>
Here is a snippet from my controller:
#RequestMapping("/userRecipes/new")
public String createNewUserRecipe(Model model){
UserRecipe userRecipe = new UserRecipe();
model.addAttribute("userRecipe", userRecipe);
return "new-recipe";
}
#RequestMapping(value = "/userRecipes/new/save", method = RequestMethod.POST)
public String saveRecipe(
#ModelAttribute("userRecipe") UserRecipe userRecipe,
#RequestParam(value = "sel_ingredients", required = false) int[] sel_ingredients,
BindingResult bindingResult, Model model){
for(int i : sel_ingredients){
Ingredient ing = new Ingredient();
ing = ingredientRepo.getOne((long)i);
userRecipe.addIngredient(ing);
}
userRecipeRepo.save(userRecipe);
return "redirect:../";
}
Along with a #ModelAttribute to set Ingredients:
#ModelAttribute
public void populateIngredients(Model model){
List<Ingredient> ingredientList = this.ingredientRepo.findAll();
//Iterator<Ingredient> itr = ingredientList.iterator();
model.addAttribute("allIngredients", ingredientList);
}
Entities:
#Entity
#Table(name = "user_recipes")
public class UserRecipe extends AuditModel{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String title;
#NotNull
#Size(max = 250)
private String description;
#ManyToMany
#JoinTable(
name = "used_ingred",
joinColumns = #JoinColumn(name = "user_recipe_id"),
inverseJoinColumns = #JoinColumn(name = "ingredientid")
)
private List<Ingredient> ingredients;
public void addIngredient(Ingredient i){
this.ingredients.add(i);
}
#NotNull
#Lob
private String preparation;
#Entity
#Table(name = "ingredients_master")
public class Ingredient extends AuditModel{
#Column(name = "ingredient")
private String name;
#Column(name="description")
private String description;
#Id
#GeneratedValue
private long id;
// public Ingredient(String n, String d){
// this.setName(n);
// this.setDescription(d);
// }
// public Ingredient(int id, String n, String d){
// this.setId(id);
// this.setName(n);
// this.setDescription(d);
// }
#ManyToMany(mappedBy = "ingredients")
Set<UserRecipe> recipes;
#ManyToMany
Set<Tag> tags;
Originally, I tried to grab the Ingredient objects directly, but couldn't figure out how to do that, so instead opted to grab the ids, and then reference them from the Repository; however,
I get the following error when I click the save button:
There was an unexpected error (type=Internal Server Error, status=500).
No message available
java.lang.NullPointerException
at com.donovanuy.mixmix.entities.UserRecipe.addIngredient(UserRecipe.java:85)
at com.donovanuy.mixmix.controllers.RecipeController.saveRecipe(RecipeController.java:103)
If possible, I'd like to be able to grab the Ingredient object directly, and add it to the List field of my userRecipe.
I am considering making a separate RecipeMakerForm, that I can instead pass into a service. Something like:
public class RecipeMakerForm{
String newTitle;
Long[] ingredientIds;
Long[] tagIds;
and the service would like like:
public class RecipeMaker{
void makeRecipe(RecipeMakerForm form){
Recipe r = new Recipe(form.getNewTitle());
if (ingredientIds != null){
for (Long id : ingredientIds){
r.addIngredient(ingredientRepo.getOne(l));
}
//etc
recipeRepo.save(r);
}
Even still, I suspect my errors have something to do with the way Thymeleaf is passing the model data back to the controller, so my new RecipeMaker & RecipeMakerForm methods wouldn't work. I'm still new to using Thymeleaf and working with Spring.data, so I expect my solution to be somewhere in my Thymeleaf or Controller methods.
I have 2 Entity classes the "Menu" which only has one field called "name" and second Entity - "Ingredients" which has 2 fields - "ingredientName" and "ingredientDescription". Database Structure
I'm creating a simple CRUD web-app , but the update method instead of updating the Entity , it inserts new values in the DB. I checked and when user clicks on the update on specified menu, the first entity's id and its ingredients id's as well are predifined. Im new to spring boot and thymeleaf and Don't really know how to work with JPA when you have more than 1 entity.
Menu entity :
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private String id;
#Column(name = "name")
private String name;
// Mapping To second table
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "menu_ingredient",
joinColumns = #JoinColumn(name = "menu_id"),
inverseJoinColumns = #JoinColumn(name = "ingredient_id"))
private List<Ingredients> ingredient = new ArrayList<>();
//Getters/Setters/Constructors/ToString
Ingredients entity :
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Column(name = "ingredient")
private String ingredientName;
#Column(name = "description")
private String ingredientDescription;
//Getters/Setters/Constructors/ToString
Controller(Only the update methods) :
#GetMapping("/edit/{id}")
public String edit(#PathVariable(name = "id")String id, Model model){
Optional<Menu> menu = menuRepository.findById(id);
List<Ingredients> ingredients = menu.get().getIngredient();
for (Ingredients ing : ingredients){
System.out.println(ing);
}
model.addAttribute("ingredients", ingredients);
model.addAttribute("newMenu",menu);
return "edit-page";
}
#PostMapping("/postEditMenu")
public String postEdit(#ModelAttribute("newMenu")Menu menu){
menuRepository.save(menu);
return "redirect:/recipeList";
}
edit-page.html :
<form action = "#" th:action="#{/postEditMenu}" th:object="${newMenu}" method="post">
<p>Menu Name: <br><input type="text" th:field="*{name}"></p>
<div id="wrapper" th:each="ing: ${ingredients}">
<label for="ingredientName"></label>
<p>Ingredient Name: <br><input th:value="${ing.ingredientName}" id="ingredientName" type="text" name="ingName"></p>
<label for="ingredientDescription"></label>
<p>Ingredient Description:</p> <textarea id="ingredientDescription" type="text" th:text="${ing.ingredientDescription}" name="ingDesc"></textarea>
</div>
<br>
<input type="button" id="more_fields" onclick="add_fields();" value="Add More" />
<br>
<input type="submit" th:value="Submit">
</form>
FIX I actually figured it out with the help of below answers. Here's the code :
#PostMapping("/postEditMenu")
public String postEdit(#ModelAttribute("newMenu")Menu menu,
#RequestParam String ingName,
#RequestParam String ingDesc){
String[] ingNameSplit = ingName.split(",");
String[] ingDescSplit = ingDesc.split(",");
Menu menuToUpdate = menuRepository.getOne(menu.getId());
List<Ingredients> newIngredientList = menuToUpdate.getIngredient();
newIngredientList.clear();
for(int a = 0, b = 0; a<ingNameSplit.length; b++, a++){
newIngredientList.add(new Ingredients(ingNameSplit[a], ingDescSplit[b]));
}
menuToUpdate.setIngredient(newIngredientList);
menuRepository.save(menuToUpdate);
return "redirect:/recipeList";
}
So, First I added hidden "id" fields to each of the items required , like this :
<input type="text" th:field = "*{id}" hidden>
and
<input type="text" th:value = "${ing.id}" hidden>
Then, in the postEditMenu method, I added #RequestParam String ingName, and #RequestParam String ingDesc to get the input of new items from thymeleaf, then I split that String and add it to String[] array with String[] ingNameSplit = ingName.split(",") Because the input would be one big comma separated String and not array[] . Then I get the menu which user wants to update - Menu menuToUpdate = menuRepository.getOne(menu.getId()); The menu.getId() isn't null because I set hidden "id" fields in thymeleaf. Then I get the Ingredients of this menu - List<Ingredients> newIngredientList = menuToUpdate.getIngredient(); because the list would already be filled with existed ingredients I clear that list and add new ingredients which user will fill the form with -
for(int a = 0, b = 0; a<ingNameSplit.length; b++, a++){
newIngredientList.add(new Ingredients(ingNameSplit[a], ingDescSplit[b]));
}
after that I set this newIngredientsList and save the menu itself to the db -
menuToUpdate.setIngredient(newIngredientList);
menuRepository.save(menuToUpdate);
Thanks for all the help guys :)
At this point:
#PostMapping("/postEditMenu")
public String postEdit(#ModelAttribute("newMenu")Menu menu){
menuRepository.save(menu);
return "redirect:/recipeList";
}
You receive menu from edit-page.html and it has no id, that is why it always creates new records in database.
To edit the desired menu, you would need to have it's id before.
You can create endpoint for obtaining list of menus and display them with edit button next to each menu in html site. Then if user clicks edit button redirect him to your edit form, but this time you can pass menu's id.
First you need to fetch Menu entity from database by id using getOne and then you can update it.
Edit your code in postEdit method as follows:
Fetch Menu entity:
Menu menuToUpdate = menuRepository.getOne(menu.getId());
Update attributes:
menuToUpdate.setName(menu.getName());
Save entity:
menuRepository.save(menuToUpdate);
Add a hidden id field to hold the menu's id, and add hidden id fields for each ingredient.
I'm writing a server with Spring-Boot using PostgreSQL
I'm trying to get information about images that are linked to a specific entity.
I'm trying to get User information from the server to my front-end Angular app.
In my system user have images linked to his account so i did class ImageEntity
#Entity #Table(name = "image") #Data
public class ImageEntity {
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
private String type;
#Lob
private byte[] image;
#JsonIgnore
public byte[] getImage() {
return image;
}
}
Then i linked the list of images to user account class
#Entity #Data
public class UserAccount{
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String firstName;
private String lastName
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(
name = "user_images",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "image_id", referencedColumnName = "id")}
)
private List<ImageEntity> images;
public void addImage(ImageEntity image) {
images.add(image);
}
}
Then i create endpoint to get user by id
#GetMapping("users/{id}")
public Optional<User> getUserById(#PathVariable Long id) {
return service.getUserById(id);
}
service method is very simple
#Transactional
public Optional<User> getUserById(Long id) {
return repository.findById(id);
}
I added some images through another endpoint works fine because i'm able to get image in my front-end.
Problem is when i want to get User info as a JSON from server( and i write #JsonIgnore on #Lob field because i only want to have info of image not the actual image) i get this error
Resolved exception caused by handler execution: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Unable to access lob stream; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Unable to access lob stream (through reference chain: com.app.model.user.User["images"])
I read some similar articles and i try to give #JsonIgnore on getter of the Image #Lob image i added #Transactional to service method retrieving elements but it's not working.
I simply want to achieve that kind of message from server:
{
id: "1"
firstName: "test",
lstName: "test_ln",
images: {
{
"id": 10,
"name": "IMG12.jpg",
"type": "image/jpeg"
},
{
"id": 20,
"name": "IMG456.jpg",
"type": "image/jpeg"
}
}
}
The fastest solution (not the best one) is to add fecth = EAGER to the OneToMany images relationship... the problem with this solution is that always will load the images entity (including the byte[] image) when dealing with users entities (which could be a performance problem)...
The next "best" solution is to omit the EAGER configuration described previously, and create a new method in your repository... such method should execute a JPA Query like this:
SELECT ua
FROM
UserAccount ua
LEFT JOIN FECTH ua.images img
WHERE
ua.id = :id
This will load the user and its related images... then in your service, you call such method (the problem with this solution is that loads the byte[] image even if you only want the other attributes of the ImageEntity)
The best solution is to extend the solution #2 to retrieve only the attributes that you want of the ImageEntity, resulting in a query like this:
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
WHERE
ua.id = :id
Then, your repository method should return a JPA Tuple and in your service method you transform that tuple into the User that you want return (including the associated images' metadata) ... (UPDATE) Example (using the method that you indicate in your comments):
// #Transactional // Remove the transactional annotation to avoid cascade issues!
public User getUserById(Long id) {
List<ImageEntity> images;
List<Tuple> tuples;
User user;
tuples = repository.getUserById(id);
user = null;
if (!tuples.isEmpty()) {
user = tuples.get(0).get(0, User.class);
images = new ArrayList<>();
for (Tuple t : tuples) {
if (t.get(1) != null) {
images.add(new ImageEntity(
t.get(1, Long.class),
t.get(2, String.class)
));
}
}
user.setImages(images);
}
return user;
}
In order to this to work, you need:
Modify the signature of the method getUserById (in your repository) to return a List of Tuple
Create a constructor method in the ImageEntity class with this signature: ImageEntity(long id, String name) { ... }
The User Entity should have a method setImages(List<ImageEntity> images) { ... }
UPDATE2: in order to do something like this, while retrieving all users, you will need:
1) Create (or override) a method in the User repository whose query will be like (let's call it, findAll):
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
2) In your service, implement a method like this:
public List<User> findAll(Long id) {
List<ImageEntity> images;
List<Tuple> tuples;
Map<Long, User> index;
tuples = repository.findAll();
index = new HashMap<>();
for (Tuple t : tuples) {
user = t.get(0, User.class);
if (!index.containsKey(user.getId()) {
images = new ArrayList<>();
user.setImages(images);
index.put(user.getId(), user)
} else {
user = index.get(user.getId());
images = user.getImages():
}
if (t.get(1) != null) {
images.add(new ImageEntity(
t.get(1, Long.class),
t.get(2, String.class)
));
}
}
return index.values();
}
EXPLANATION: The key point is we want to retrieve the user with the image metadata (only code, name and type) avoiding to load the lob attribute (because, the images can be MB and they won't be used/serialized) ... that is why we execute a query like this:
SELECT
ua,
img.id, img.name, img.type
FROM
UserAccount ua
LEFT JOIN ua.images img
The LEFT JOIN force to retrieve all user (including those without images)
The ORM (i.e. JPA implementation, for example, hibernate) maps this kind of query to a Tuple Object (always)!
The query produces N x M tuples ... where N are the total of Users and M are total of images... for example, if you have only 1 user with 2 images... the result will be 2 tuples, where first tuple's component is the always the same user, the other components will be the attributes of each images...
Then, you need transform the tuple object to a User Object (this is what we do in the service methods) ... the key point here is the use of a new ArrayList for the images attribute, before adding a new ImageEntity to it ... we need to do this because the ORM injects a proxy list for each User loaded ... if we add something to this proxy, the ORM executes the lazy loading of such proxy, retrieving the associated images (which is something that we want to avoid)...
is there a way in Thymeleaf to validate an attribute in object property of a bean?
Consider that we do have a Departement class as below :
public class Departement {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long idDept;
#NotEmpty
private String name;
}
And another Employee class as follow
public class Employee{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long idEmp;
#NotEmpty
#Size(min = 5, message="At least five characters needed")
private String employeeName;
#NotNull
private Departement departement;
}
With the code above using thymeleaf in the employee form only 'employeeName' will be validated by spring because of annotations.
Let's take a look here
In my controller
#GetMapping( value = "/emp" )
public String save(Model model){
Employee emp = new Employee();
emp.setDepartement(new Departement());
model.addAttribute('employee', emp);
return 'view';
}
//------------- Form in PostMapping
#PostMapping( value = "/save", #Valid Emp emp, BindingResult bindingResult )
public String savePost(Model model){
if( ! bindingResult.hasErrors() )
{
/* Even if departement has not been choosen, my code always goes here
and print "Form Ok. Departement : 0" instead of reaching the 'else' block, but if departement choosen,
it prints the correct value of departemnt
*/
System.out.println( "Form Ok.\n Departement : " + emp.getDepartement().getIdDept() );
}else{
System.out.println( "Missing attributes." );
}
return 'view';
}
And here goes the employee form
<form th:action="#{save}" th:object="${emp}" th:method="POST" >
<span th:if="${#fields.hasErrors('employeeName') }"th:errors="*{employeeName}"></span>
<input th:field="*{employeeName}" th:value="${employeeName}" />
//--------
<div th:object="${emp.departement}">
<span th:if="${#fields.hasErrors('idDept') }"th:errors="*{idDept}"></span>
<input th:field="*{idDept}" th:value="${idDept}" />
</div>
</form>
**Here is my question :
How can I validate the employee departement identifier (idDept field) without using javacript in the emplpoyee form?
**
NB : I don't use drowpdownlist for displaying departements but prefer an autocomplete field and a hidden field that take the choosen departement id.
JSR-303 mandates the use of a #Valid annotation to recursively validate nested components as mentioned in the Hibernate Validator docs.
So just put #Valid on your nested components, in your case on the department field within the employee class:
public class Employee {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long idEmp;
#NotEmpty
#Size(min = 5, message="At least five characters needed")
private String employeeName;
#NotNull
#Valid
private Departement departement;
}
Is it possible to select whole Java Object from Spring Form's drop down menu? I used LinkedHashMap, but it doesn't work.
I have relation Many To One between table Agent and table Roles (every Agent has one role eg. user, admin). I use hibernate so I have to operate on Object, not Id's from database. My problem is that I want to create drop down menu with list of all roles from my database and when I pick one element, this Object goes to my Agent Object and save in my database.
I have List with my Roles Objects
List<Roles> rolesList = rolesService.getAllRoles();
Which comes from this:
public List<Roles> getAllRoles() {
return session().createQuery("from Roles").list();
}
and I tried something like this:
In my AgentController:
#RequestMapping("/createagent")
public String createAgent(Model model) {
Agent agent = new Agent();
List<Roles> rolesList = rolesService.getAllRoles();
Map<Roles, String> rolesMap = new LinkedHashMap<Roles,String>();
for (int i=0; i<rolesList.size(); i++){
rolesMap.put(rolesList.get(i), rolesList.get(i).getRole());
}
model.addAttribute("rolesMap", rolesMap);
model.addAttribute("agent", agent);
return "createagent";
}
In my jsp file:
<tr><td>Roles:</td><td>
<sf:select path="roles" multiple="false">
<sf:options items="${rolesMap}"></sf:options>
</sf:select>
</td></tr>
My Roles Object:
#Entity
#Table(name = "roles")
public class Roles {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "roles_seq_gen")
#SequenceGenerator(name = "roles_seq_gen", sequenceName = "roles_id_seq")
#Column(name = "id")
private long id;
#Column(name = "role")
private String role;
It shows excactly what I want but when I click position in my drop down menu and submit it, my form don't save my Object properly. It nested it ... I don't know how to subscribe this, maybe my toString() function output clear little bit.
Agent [id=0, username=TestUsername, password=TestPassword, roles=Roles[id=0, roles=Roles[id=0, roles=user]] ...
My Agent Object:
#Entity
#Table(name="agent")
public class Agent {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="agent_seq_gen")
#SequenceGenerator(name="agent_seq_gen", sequenceName="agent_id_seq")
#Column(name="id")
private long id;
#Column(name="username")
private String username;
#Column(name="password")
private String password;
#ManyToOne
#JoinColumn(name="roles_id")
private Roles roles;
My jUnit test runs fine, it's something wrong with my Spring Form or with my controllers... I don't know.
I would suggest to use ModelAttribute and reference the form object to the said properties, like, agent.getRoles().
createAgent(#ModelAttribute("agent") Agent agent) should work in what you're trying to accomplish.