Customize form bean creation process in spring

148 Views Asked by At

I have the following bean:

public class TerminalAdmin {

    @Id
    @Column(name = "admin_id", nullable = false, unique = true)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id")
    @SequenceGenerator(name = "user_id", sequenceName = "user_id")
    private Long adminId;

    @Column(name = "email", nullable = false)
    private String email;

    @Column(name = "phone")
    @Size(max = 255)
    private String phone;

    @Size(max = 255)
    @Column(name = "name")
    private String name;

    @Column(name = "registration_date")
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar createDate;

    @Column(name = "password", nullable = false)
    @Size(min=1, max = 255, message = "введите пароль длиной от 1 до 255 символов")
    private String password;

    @ManyToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    @JoinTable(name = "admin_role", joinColumns = { 
            @JoinColumn(name = "admin_id", nullable = false) }, 
            inverseJoinColumns = { @JoinColumn(name = "role_id", 
                    nullable = false) })
    private Set<AdminRole> adminRoles;

    @Column(name = "blocked")
    private boolean blocked;
    ...
}

and this:

public class AdminRole {    

    @Id
    @Column(name = "role_id", nullable = false, unique = true)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id")
    @SequenceGenerator(name = "user_id", sequenceName = "user_id")
    private Long id;

    @Column(name = "role")
    private String role;
    ....
}

Inside controller:

@RequestMapping(value = "/admin/addNewAdmin")
public String adminUsers(@Valid TerminalAdmin terminalAdmin,
            BindingResult bindingResult, ModelMap model, Principal principal, HttpSession session) {

from client side I send following request:

enter image description here

terminalAdmin comes to the method looks like this

enter image description here

  1. Why spring writes values into role field?
  2. How to force spring write 250/251 into id field?

P.S.

I tried to write

InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(AdminRole.class, new PropertyEditorSupport() {
        public void setAsText(String name) {
            ....
        }
    });
}

but setAsText method doesn't invoke.

1

There are 1 best solutions below

6
Oskar Dajnowicz On

This is not a good practice to populate model objects into to forms since Spring can bind fields to object even if they are not populated into the view if your init binder is not properly configured.

Easiest way is to create DTO objects, eg. you could create AdminTerminalDTO or AdminTerminalForm wich you populate to the view.

The Form could contain same fields as AdminTerminal excluding ID field or any other sensitive fields. You cant insert new ID's from the view since it can cause DB integrity errors.

After successful validation you just persist your model object filling it with DTO/Form Object.

Moreover your JSR-303 Annotations seem to be not used in a proper way.

The @Size Annotation is not proper a validation to check String length. You have to use @Length instead. You use @Size to check length of an arrays. @Size also works on Strings but @Length is more accurate.

  1. You can't just send an Integer and just try to bind to your Set(spring does some weird binding as you can see now) . Instead you already done addNewAdmin method in your controller wich already informs that it adds an Admin User.
  2. You have to assign admin role on the server side right in this method. First you can use DTO wich will contain eg. username,password and other fields. You annote them with proper JSR-303 Annotations. Using bindingResult you check if there were any validation errors. If form is validated fine, you just convert your DTO/Form object to Model object. Then you can add admin role and persist your model object.

I can write some example code if this tips are not enough.

EDIT:

public class TerminalAdminDTO {

    private String username;

    @Length(max = 255)
    public String getUsername(){
        return username;
    }

    public void setUsername(String username){
        this.username = username;
    }

    public TerminalAdmin convertToTerminalAdmin(){
        TerminalAdmin terminalAdmin = new TerminalAdmin();
        terminalAdmin.setUsername(this.username);
        return terminAdmin;
    }

}


@Entity
@Table
public class TerminalAdmin {

    @Id
    @Column(name = "admin_id", nullable = false, unique = true)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_id")
    @SequenceGenerator(name = "user_id", sequenceName = "user_id")
    private Long adminId;

    @Column(name = "email", nullable = false)
    private String email;

    @Column(name = "phone")
    @Size(max = 255)
    private String phone;

    @Size(max = 255)
    @Column(name = "name")
    private String name;

    @Column(name = "registration_date")
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar createDate;

    @Column(name = "password", nullable = false)
    @Size(min=1, max = 255, message = "введите пароль длиной от 1 до 255 символов")
    private String password;

    @ManyToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    @JoinTable(name = "admin_role", joinColumns = { 
            @JoinColumn(name = "admin_id", nullable = false) }, 
            inverseJoinColumns = { @JoinColumn(name = "role_id", 
                    nullable = false) })
    private Set<AdminRole> adminRoles;

    @Column(name = "blocked")
    private boolean blocked;
    ...
}


@RequestMapping(value = "/admin/addNewAdmin")
public String adminUsers(@Valid TerminalAdminDTO terminalAdminDTO,
            BindingResult bindingResult, ModelMap model, Principal principal, HttpSession session) {
                    if(result.hasErrors()){
                        return "errorPage";
                    }else{
                        userService.createAdminUser(terminalAdminDTO);
                        return "successPage";
                    }
            }


@Service
@Transactional
public class UserServiceImpl implements UserService {

    private final int ADMIN_ROLE_ID = 0;

    @Autowired
    EntityManager entityManager;

    public void createAdminUser(TerminalAdminDTO terminalAdminDTO){
        TerminalAdmin terminalAdmin = terminalAdminDTO.convertToTerminalAdmin();
        AdminRole adminRole = entityManager.find(AdminRole.class,ADMIN_ROLE_ID);
        terminalAdmin.getAdminRoles().add(adminRole);
        entityManager.create(terminalAdmin);
    }

}

I wrote it as an example of way doing it, this is not a ready-made code