如何从 JSP 页面保存 JPA 实体模型属性的特定属性
我们使用 JPA 实体 bean 作为 jsp 页面上 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> </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);
}
}
We are using JPA entity beans as our model for Spring MVC controller on a jsp page. One of our jsp pages is a partial view of this entity which does not show all properties. Whenever we try to update our entity using the service layer from the controller only the properties used on the jsp form are persisted and all others are nulled out. What is the correct way to handle this situation? We do not want to have to specify hidden fields on the form.
So in this case when the controller calls the service.update(client) method the name field will be null because it does not exist in the 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> </td></tr>
</table>
</td>
</tr>
</table>
<input type="hidden" name="submitted" value="true">
controller
@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;
}
}
service methods
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);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您并没有真正提供足够的详细信息(具体来说,显示如何保存表单中的值的代码会有所帮助),但我怀疑您正在将一个分离的实体与
null
属性合并。由于合并的工作方式(它将分离实体的状态复制到持久性上下文中加载的具有相同数据库标识符的实体上),您会得到 NULL。您需要:
合并
它~或者~如果我错过了这一点,请提供更多详细信息以了解问题。
更新:我不明白你的代码。您正在将属性从
ref
复制到实体
(来自视图的分离客户端),然后合并< code>ref... 不,我不明白。You aren't really giving enough details (specifically, the code showing how you save the values from the form would help) but I suspect that you're merging a detached entity with
null
attributes. And because of the waymerge
works (it copies the state of the detached entity onto a entity with the same database identifier loaded in the persistence context), you get NULLs.You need to either:
merge
it ~or~If I missed the point, please provide more details to understand the problem.
Update: I don't understand your code. You're copying properties from
ref
toentity
(the detached client coming from the view), then mergingref
... No, I don't get it.