【问题标题】:How to save specific properties of a JPA entity model attribute from JSP page如何从 JSP 页面保存 JPA 实体模型属性的特定属性
【发布时间】:2011-04-06 07:43:31
【问题描述】:

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

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

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

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

}

【问题讨论】:

  • 我也知道为模型使用某种 DTO 也是一种解决方案,但是模型的一部分被清除是没有意义的,因为表单只指定了模型。如果我将模型推送到 ModelAndView 表单应该只保存模型的指定内容!

标签: java orm jpa spring-mvc modelandview


【解决方案1】:

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

您需要:

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

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

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

【讨论】:

  • 我相信您可能错过了重点,因为如果我进行合并,那么这些字段将被清空,因为它们在表单上不存在。我已经提供了一些详细的代码。谢谢!
  • @dukethrash 我认为您误读了我的回答。无论如何,我真的很想知道clientService.update(Client) 方法在做什么,这在某种程度上是问题的关键。你不觉得展示这部分会有用吗?
  • 我已经发布了更新方法。尽管看起来很奇怪,但请记住,传递给更新方法的实体除了 TradeOrderManagementSystem 之外只有空值。我知道可以从表单中复制值并合并,但这似乎是 DTO 方法。对我来说,Spring 将删除模型的所有字段并仅替换表单上的字段对我来说没有意义。如果我 clientService.findById 并将客户端作为模型推送,它应该保留为模型并且只更改表单上的内容,但这似乎不是 Spring MVC 的本质。
  • BeanUtils.copyProperties(entity, ref) 方法签名是 copyProperties(Object source, Object target) 所以我真的是在复制 from 实体 to参考
猜你喜欢
  • 2022-01-25
  • 2018-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-26
  • 2016-07-17
  • 1970-01-01
相关资源
最近更新 更多