我们在 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> </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);
}
}