1

我们在 jsp 页面上使用 JPA 实体 bean 作为 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);
}

}

4

1 回答 1

1

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

您需要:

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

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

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

于 2010-08-31T23:03:16.183 回答