我们使用 JPA 实体 bean 作为 jsp 页面上 Spring MVC 控制器的模型。我们的 jsp 页面之一是该实体的部分视图,它不显示所有属性。每当我们尝试使用控制器中的服务层更新实体时,只会保留 jsp 表单上使用的属性,而所有其他属性都会被清空。处理这种情况的正确方法是什么?我们不想在表单上指定隐藏字段。

因此,在这种情况下,当控制器调用 service.update(client) 方法时,name 字段将为 null,因为它不存在于 form.jsp 中。

表单.jsp

<form:form modelAttribute="client" method="get" action="${action}">
<table width="100%">
    <tr>
        <td>
            <table>
                <tr>
                    <td valign="top"><spring:message code="label.tradeOrderManagementSystem"/>:</td>
                    <td>
                        <form:select path="tradeOrderManagementSystems" >
                            <form:options items="${tradeOrderManagementSystemList}" itemValue="id" itemLabel="name" />
                        </form:select>
                        <a href="<spring:url value="/tradeOrderManagementSystem/add"/>" class="addAndReturn"><span><spring:message code="add"/></span></a>
                    </td>
                    <td>
                        <form:errors path="tradeOrderManagementSystems" cssClass="errors" />
                    </td>
                </tr>
                <tr><td></td><td>&nbsp;</td></tr>
            </table>
        </td>
    </tr>
</table>
<input type="hidden" name="submitted" value="true">

控制器

@RequestMapping("/{id}/edit")
public ModelAndView edit(HttpServletRequest request,
        HttpServletResponse response,
        @ModelAttribute("client") Client client,
        BindingResult result,
        @PathVariable("id") int id,
        Model model) {
    ControllerContext ctx = new ControllerContext(request, response);
    init(ctx);

    setAdvancedSearchAvailable(ctx, true);
    buildShowAndEditVerticalMenu(ctx, id, false);

    if (id == 0) {
        result.addError(new ObjectError("client", getMessage("error.idNeeded")));
        return getModelAndView(ctx, "itEfficiencies/form");
    } else {
        if (!isSubmission(ctx)) {
            client = clientService.find(id);
            model.addAttribute("client", client);
            fillClientForm(model);
            return getModelAndView(ctx, "itEfficiencies/form");
        } else {
            //clientValidator.validate(client, result);
            if (result.hasErrors()) {
                fillClientForm(model);
                return getModelAndView(ctx, "itEfficiencies/form");
            } else {
                try {
                    //checkClientProperties(client);
                    client.setId(id);
                    client = clientService.update(client);  //method updates only form fields and nulls out all others
                } catch (Exception e) {
                    e.printStackTrace();
                    result.addError(new ObjectError("client", getMessage("error.save")));
                    fillClientForm(model);
                    return getModelAndView(ctx, "itEfficiencies/form");
                }
                return getModelAndView(ctx, "/staffingByClient/" + client.getId() + "/show", true);
            }
        }
    }
}    

客户端.java

@Entity
public class Client extends AbstractEntity<Integer> {

private static final long serialVersionUID = 1L;

public static final String FIND_BY_NAME = "Client.FIND_BY_NAME";

public static final String COUNT_BY_NAME = "Client.COUNT_BY_NAME";

@Basic(optional = false)
@Column(nullable = false, length = 125)
private String name;

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(inverseJoinColumns = {
    @JoinColumn(name = "trade_order_management_system_id")}, uniqueConstraints =
@UniqueConstraint(name = "UK_client_trade_order_mgmt_client_id_trade_order_mgmt_id",
columnNames = {"client_id", "trade_order_management_system_id"}))
@ForeignKey(name = "FK_client_trade_order_management_systems_client_id",
inverseName = "FK_client_trade_order_mgmt_sys_trade_order_management_system_id")
private List<TradeOrderManagementSystem> tradeOrderManagementSystems;

public Client() {
}

public Client(Integer id) {
    this.id = id;
}

public Client(Integer id, String name) {
    this.id = id;
    this.name = name;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}   

public List<TradeOrderManagementSystem> getTradeOrderManagementSystems() {
    return tradeOrderManagementSystems;
}

public void setTradeOrderManagementSystems(List<TradeOrderManagementSystem> tradeOrderManagementSystems) {
    this.tradeOrderManagementSystems = tradeOrderManagementSystems;
}

@Override
public boolean equals(Object object) {
    // TODO: Warning - this method won't work in the case the id fields are not set
    if (!(object instanceof Client)) {
        return false;
    }
    Client other = (Client) object;
    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
        return false;
    }
    return true;
}

}

服务方式

public abstract class CrudService<T, ID extends Serializable> extends DAOImpl<T, ID> {

/**
 * Updates an entity from an existing entity.
 *
 * @since 0.0.1
 * 
 * @param entity
 * @return the managed instance of the updated entity
 */
@Override
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
public T update(T entity, ID id) {
    return super.update(assignDefaultValues(entity), id);
}

}

public abstract class DAOImpl<T, ID extends Serializable> implements DAO<T, ID> {

private Class<T> persistentClass;

@PersistenceContext(unitName = "krfsPersistenceUnit")
protected EntityManager entityManager;

/**
 * Instantiates an instance of this class and sets the <code>persistentClass</code>
 * based on the identifier type
 *
 * @since 0.0.1
 */
public DAOImpl() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

/**
 * @since 0.0.1
 * 
 * @return the type to be persisted
 */
@Override
public Class<T> getPersistentClass() {
    return persistentClass;
}

/**
 * Updates an entity from an existing entity.
 *
 * @since 0.0.1
 * 
 * @param entity
 * @param id the identifier of the entity
 * 
 * @return the managed instance of the updated entity
 */
@Override
public T update(T entity, ID id) {
    //Find a managed instance of the entity first and copy the properties
    //to the passed in entity before merging.  This ensures that entityManager
    //will not create a new entity with merge.
    Object ref = this.entityManager.getReference(persistentClass, id);
    if (ref != null) {
        BeanUtils.copyProperties(entity, ref);
    }
    return (T) this.entityManager.merge(ref);
}

}

有帮助吗?

解决方案

您并没有真正提供足够的详细信息(具体来说,显示如何保存表单中的值的代码会有所帮助),但我怀疑您正在将一个独立的实体与 null 属性。并且因为方式 merge 有效(它将分离实体的状态复制到持久性上下文中加载的具有相同数据库标识符的实体上),您会得到 NULL。

您需要:

  • 以某种方式保留分离的实体,将表单中的值复制到其中,然后 merge 它~或者~
  • 实施“手动合并”,即使用其 id 加载要更新的实体,从模型中复制新值并让 JPA 更新它。

如果我错过了这一点,请提供更多详细信息以了解问题。

更新: 我不明白你的代码。您正在复制属性 ref entity (来自视图的分离客户端),然后合并 ref...不,我不明白。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top