我正在使用Hibernate和Spring MVC构建一个 "Campground "网站。我有一个 "Campground "页面,显示了一个营地的详细信息和用户提交的评论。有一个 "编辑 "按钮,可以用来编辑一个营地的详细信息。Comment
. 这个按钮会把你发送到一个表单(Spring表单),在这个表单中你可以更改信息,然后提交的更改会被保存到对象中,然后使用Hibernate保存到数据库中。
问题是这样的。Hibernate实体中的一些事情 Comment.java
是从数据库中的其他表中检索的对象,Hibernate使用其Hibernate Magic处理检索这些对象。问题是,Entity类中的这些字段会被检索回来。null
检索时 Model
(提交表格后)。这使得使用表单更新对象成为一个挑战。字段如 author
类型 User
జజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజ Campground
相关联的字段全部为空。有什么方法可以让Spring在发送表单结果时不将这些字段清空?我找到了一种方法来避免使用`来使注释的id不被清空,但是当在对象上尝试这样做时,它会抛出一个异常。
我想出的一个办法是,只需更新 author
和 campground
场上 Comment
在下 @PostMapping
但这需要从数据库中检索这些对象,如果有办法在一开始就不把它们清空的话,这似乎是多余的。这感觉就像一个创可贴,而我可能忽略了一个合法的解决方案。
下面是 Entity
类,以及编辑该类的表格。Comment
还有我的Contoller
Comment Entity class
@Entity
@Table(name = "comments")
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "text")
private String text;
@ManyToOne
@JoinColumn(name = "author")
private User author;
@ManyToOne
@JoinColumn(name = "campground")
private Campground campground;
public Comment() {
}
public Comment(String text, User author) {
this.text = text;
this.author = author;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public User getAuthor() {
return author;
}
public void setAuthor(User author) {
this.author = author;
}
public Campground getCampground() {
return campground;
}
public void setCampground(Campground campground) {
this.campground = campground;
}
@Override
public String toString() {
return "Comment{" +
"id=" + id +
", text='" + text + '\'' +
", author=" + author +
", campground=" + campground +
'}';
}
}
@RequestMapping
从 Controller class
@GetMapping("/campgrounds/{campgroundId}/comments/{commentId}/edit")
public String editComment(@PathVariable Long commentId, @PathVariable Long campgroundId, Model model, HttpSession session) {
User user = (User) session.getAttribute("user");
Comment comment = commentDAO.getComment(commentId);
if (!user.equals(comment.getAuthor())) {
return "redirect:/campgrounds/" + campgroundId;
}
model.addAttribute("editComment", comment);
return "editComment";
}
@PostMapping("/campgrounds/{campgroundId}/comments/{commentId}/edit")
public String editPostComment(@ModelAttribute("editComment") Comment comment,
@PathVariable Long campgroundId, @PathVariable Long commentId, HttpSession session) {
User author = (User) session.getAttribute("user");
comment.setAuthor(author); // because it comes back null
Campground campground = campgroundDAO.getCampgound(campgroundId);
comment.setCampground(campground); // because it comes back null
commentDAO.saveComment(comment);
return "redirect:/campgrounds/" + campgroundId;
}
最后 editComment.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ include file="partials/header.jsp"%>
<div class="container mx-auto my-3">
<spring:form modelAttribute="editComment" action="${pageContext.request.contextPath}/campgrounds/${campgroundId}/comments/${commentId}/edit" method="post">
<div class="card border-info">
<h3 class="h3 text-center card-header badge-info">Edit Comment</h3>
<div class="my-3">
<spring:hidden path="id"/>
<div class="form-group col-5 m-auto">
<label for="comment_text">Comment Text</label>
<spring:textarea id="comment_text" path="text" cssClass="form-control"/>
</div>
<input class="btn btn-primary form-control mt-3" type="submit" value="Submit">
</div>
</div>
</spring:form>
</div>
<%@ include file="partials/footer.jsp"%>
在开发Web应用时,我们经常需要在多个视图中或者在同一个视图中多次引用同一个属性。
所以为了存储,spring有两个有用的功能。
1.Scoped Proxy。
我们知道spring在定义一个bean的时候,已经给出了多个scope。
参考 这更详细
在这里,我们将使用Session Scope,因为我们希望数据在多个请求中都可以使用。
所以我们可以通过提供范围来创建一个@Bean。
@Scope(
value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.TARGET_CLASS)
由于上下文初始化时没有会话,Spring将创建一个Bean对象的代理来作为依赖注入。
在你的例子中
@Bean
@Scope(
value = WebApplicationContext.SCOPE_SESSION,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public Comment getComment(){
return new Comment();
}
--- In Controller
@Autowired
private Comment comment;
所以在singelton控制器中Comment对象将作为一个Sesscion作用域注入。
请注意 评论 是可供其他组件注入的。这可能是一个优点,也可能是一个缺点,这取决于用例。如果让整个应用程序都可以使用Bean是有问题的,那么可以使用@SessionAttributes将实例的范围扩大到控制器。
我们没有定义 评论 作为一个Spring管理的@Bean。相反,我们将它声明为一个@ModelAttribute,并指定@SessionAttributes注解来将其作用域扩展到控制器的会话。
@Controller
@SessionAttributes("comment")
public class Controller{
@ModelAttribute("comment")
public Comment getComment() {
return new Comment();
}
}
所以这个属性 评论 将在会话中可用,直到控制器没有被销毁。
去 在本文档中,@ModelAttribute是如何发挥作用的。
使用 @SessionAttributes
的控制器上。
@Controller
@RequestMapping("/foo")
@SessionAttributes("editComment") // multiple command names supported with {}
public class CommnetController {
}
使用这个注解可以将你的命令对象保存到一个透明的会话中,从表单提交中找不到的数据将从透明的会话中被绑定。
读取 如何使用命令对象解析 @ModelAttribute
在Spring框架文档中。