【问题标题】:Spring form binding issue with nested object empty fields嵌套对象空字段的Spring表单绑定问题
【发布时间】:2017-05-09 15:15:34
【问题描述】:

我在使用 spring @ModelAttribute 表单绑定时遇到了一个相当尴尬的问题。我有以下实体类,它在表单绑定中用作 Policy 类的嵌套类。

@javax.persistence.Entity
@Table(name = "entity")
@XmlRootElement
public class Entity implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "entity_id")
    private Integer entityId;

    @Basic(optional = false)
    @NotNull
    @Column(name = "entity_type")
    private int entityType;

    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 2147483647)
    @Column(name = "entity_name")
    private String entityName;

    public Entity() {
    }

    public Entity(Integer entityId) {
        this.entityId = entityId;
    }

    public Entity(Integer entityId, int entityType, String entityName) {
        this.entityId = entityId;
        this.entityType = entityType;
        this.entityName = entityName;
    }

    public Integer getEntityId() {
        return entityId;
    }

    public void setEntityId(Integer entityId) {
        this.entityId = entityId;
    }

    public int getEntityType() {
        return entityType;
    }

    public void setEntityType(int entityType) {
        this.entityType = entityType;
    }

    public String getEntityName() {
        return entityName;
    }

    public void setEntityName(String entityName) {
        this.entityName = entityName;
    }
}

我的问题的主要兴趣是 Policy 类,它是顶级类和主绑定如下:

@Entity
@Table(name = "policy")
@XmlRootElement
public class Policy implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "policy_id")
    private Integer policyId;

    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 2147483647)
    @Column(name = "reference_number")
    private String referenceNumber;

    @JoinColumn(name = "adjuster_id", referencedColumnName = "entity_id", nullable = true)
    @ManyToOne(optional = true)
    private Entity adjusterId;

    public Policy() {
    }

    public Policy(Integer policyId) {
        this.policyId = policyId;
    }

    public Integer getPolicyId() {
        return policyId;
    }

    public void setPolicyId(Integer policyId) {
        this.policyId = policyId;
    }

    public String getReferenceNumber() {
        return referenceNumber;
    }

    public void setReferenceNumber(String referenceNumber) {
        this.referenceNumber = referenceNumber;
    }


    public Entity getAdjusterId() {
        return adjusterId;
    }

    public void setAdjusterId(Entity adjusterId) {
        this.adjusterId = adjusterId;
    }
}

这是控制器方法,它从视图绑定中接收 ModelAttribute 注释的 Policy 参数。

@RequestMapping(value = "/create", method = RequestMethod.POST)
    public ModelAndView create(@ModelAttribute Policy p) {
        policyService.create(p);
        return new ModelAndView("viewName");
    }

最后这是代码的表单/视图部分:

<form id="policyInformationForm" role="form" th:object="${policy}" th:action="@{${mode == 'CREATE'} ? '/policy/create' : '/policy/update'}" method="post">
                        <div class="box-body">
                            <div class="form-group">
                                <label for="policyId">Policy Id</label>
                                <input type="text" th:field="*{policyId}" class="form-control" id="policyId" placeholder="" readonly="readonly" />
                            </div>

                            <div class="form-group">
                                <label for="referenceNumber">Policy Number (May change while saving!!!)</label>
                                <input type="text" th:field="*{referenceNumber}" class="form-control" id="referenceNumber" placeholder="Enter Policy Number" th:readonly="${mode == 'UPDATE'}" />
                            </div>

                            <div class="form-group">
                                <label for="adjusterName">Adjuster</label>
                                <div class="input-group">
                                    <input type="hidden" th:field="*{adjusterId.entityId}" class="form-control" id="adjusterId" />
                                    <input type="text" th:field="*{adjusterId.entityName}" class="form-control" id="adjusterName" placeholder="Enter Adjuster" />
                                    <div class="input-group-btn">
                                        <button id="adjusterSearchBtn" type="button" class="btn btn-success"><span class="glyphicon glyphicon-search"></span></button>
                                        <button id="adjusterCreateBtn" type="button" class="btn btn-success"><span class="glyphicon glyphicon-plus"></span></button>
                                    </div>
                                </div>
                            </div>

                        </div>

                        <div class="box-footer">
                            <button type="submit" class="btn btn-primary">Submit</button>
                            <a th:href="@{'/policy/list'}" class="btn btn-warning">Cancel</a>
                        </div>
                    </form>

问题是当我提交表单时; spring 表单绑定将表单参数绑定到 Policy 类的各个字段,但如果未选择调整器,即 adjusterId 值的隐藏字段为空白(这对于应用程序来说非常好),那么 spring 实例化一个新的实体类,所有字段都具有空值。由于 ManyToOne 关系和 JPA 验证,这会在应用程序的 JPA 持久性部分产生问题。

有关如何克服此问题的任何指示。如果视图中的adjusterId(表单隐藏输入字段)为空;那么表单绑定不应实例化具有空字段值的新实体类。相反,它应该将 Policy 类的 adjusterId 属性设置为 null。

顺便说一句,我已经解决了许多类似的问题,而这些问题中的大多数问题并不相关。其中之一就是same question which is unanswered

提前谢谢...

【问题讨论】:

  • 顺便说一句,我将问题追踪到弹簧表单绑定 IdToEntityConverter。任何覆盖默认转换器的信息将不胜感激

标签: java spring spring-mvc jpa thymeleaf


【解决方案1】:

在控制器中,你可以这样做:

@RequestMapping(value = "/create", method = RequestMethod.POST)
public ModelAndView create(@ModelAttribute Policy p) {
    if (p.getAdjusterId().getEntityId() == null) {
        p.setAdjusterId(null);
    }
    policyService.create(p);
    return new ModelAndView("viewName");
}

【讨论】:

  • 我已经这样做了,这是最合适的解决方法;这就是它被接受为答案的方式。我相信 IdToEntityConverter 应该以某种方式理解传入(字段)参数为 null 并且不应该构造对象。
  • 其实这不是变通办法,而是解决方案本身。覆盖 IdToEntityConverter 将是一种非标准方式,在这种情况下不值得付出努力。如果您不想在Policy 涉及的每个控制器方法中编写相同的逻辑(检查entityId 是否为空),您可以在方法级别使用ModelAttribute 注释。看看doc
猜你喜欢
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 2014-05-08
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多