【问题标题】:Customize form bean creation process in springspring中自定义form bean创建过程
【发布时间】:2016-01-31 17:31:13
【问题描述】:

我有以下 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;
    ...
}

还有这个:

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;
    ....
}

内部控制器:

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

我从客户端发送以下请求:

terminalAdmin 来的方法是这样的

  1. 为什么 spring 将值写入role 字段?
  2. 如何强制spring将250/251写入id字段?

附言

我试着写

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

但是setAsText 方法没有调用。

【问题讨论】:

  • 您遗漏了一些重要信息。 Spring 不会为字段随机分配值。 AdminRole 可能有一个转换器或类似的东西。
  • @zeroflagL 实际上这段代码是很久以前写的。我在哪里可以找到这样的东西?
  • 不幸的是,转换数据或自定义绑定的方法不止一种。唯一可靠的方法是搜索对AdminRole 的引用。也许AdminRole 有一个构造函数期望String
  • @zeroflagL 删除构造函数后 - 一切正常

标签: java spring spring-mvc spring-form spring-mvc-initbinders


【解决方案1】:

将模型对象填充到表单中不是一个好习惯,因为如果您的 init binder 配置不正确,Spring 可以将字段绑定到对象,即使它们没有填充到视图中。

最简单的方法是创建 DTO 对象,例如。您可以创建填充到视图中的 AdminTerminalDTO 或 AdminTerminalForm。

表单可以包含与 AdminTerminal 相同的字段,但不包括 ID 字段或任何其他敏感字段。您不能从视图中插入新 ID,因为它会导致数据库完整性错误。

验证成功后,您只需使用 DTO/Form 对象填充模型对象即可。

此外,您的 JSR-303 注释似乎没有以正确的方式使用。

@Size 注解不适合检查字符串长度。您必须改用@Length。您使用 @Size 检查数组的长度。 @Size 也适用于字符串,但 @Length 更准确。

  1. 您不能只发送一个 Integer 并尝试绑定到您的 Set(如您现在所见,spring 做了一些奇怪的绑定)。相反,您已经在控制器中完成了 addNewAdmin 方法,该方法已经通知它添加了一个管理员用户。
  2. 您必须在此方法中在服务器端分配管理员角色。首先,您可以使用 DTO,其中将包含例如。用户名、密码和其他字段。您使用正确的 JSR-303 注释对它们进行注释。使用 bindingResult 检查是否有任何验证错误。如果表单验证良好,您只需将 DTO/Form 对象转换为模型对象。然后您可以添加管理员角色并保留您的模型对象。

如果这些提示还不够,我可以编写一些示例代码。

编辑:

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);
    }

}

我把它写成一个例子,这不是现成的代码

【讨论】:

  • 请提供样品
  • “您不能从视图中插入新 ID,因为它会导致数据库完整性错误” - 这就是我们有限制的原因。但更重要的是:在 OP 的情况下不会插入任何 ID。 @Length 不是标准注释。如果你不使用 Hibernate 验证器,你甚至不能使用它。 “你不能只发送一个整数并尝试绑定到你的 Set” - 是的,如果你知道怎么做的话,你可以。 “您必须在服务器端分配管理员角色” - 绑定发生在服务器端,不是吗?
  • 如果你不使用 Hibernate 验证器,你甚至无法使用它。 “你不能只发送一个整数并尝试绑定到你的 Set” - 是的,如果你知道怎么做,你可以这样做遵循这个建议 - 如果你知道怎么做,一切都可以完成,像这样发送整数会导致严重的安全漏洞,您可以通过这种方式添加任何角色而无需适当的验证“您必须在服务器端分配管理员角色” - 绑定发生在服务器端,不是吗? 是吗?它绑定了所有东西而不是管理员角色关于约束的事情你为什么还要尝试约束新对象 id?
  • 如果你不提到我的名字,我不会收到通知,也永远不会知道你回复了我的评论。 每个输入都是潜在的威胁,这就是我们相应对待它的原因。如果发送 ID 会导致安全问题,那么应用程序是不安全的。时期。我们可以假设在 OP 的情况下 AdminRole 指的是预定义的角色。约束将阻止对不存在的角色的引用。
  • @zeroflagL 好吧,我正在讨论这种具体情况,您的提示总体上很好,但在这里这些提示不是很有用。 1)不将模型对象直接绑定到表单被认为是一种好习惯 2)在创建 DTO 时,您不需要创建额外的约束 3)如果该方法已经命名为 addAdminRole 为什么应该将角色 ID 直接添加到您所在的视图中可以添加一些不同的 ID 4)我不喜欢实现这个问题的整个结构,相反 AdminRole 应该只有一个 Role 和 UserRole ,这将是 ManyToMany 关联,它更清晰
猜你喜欢
  • 2015-05-16
  • 2016-03-18
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-07
相关资源
最近更新 更多