如何解决非空属性引用空值或瞬态值-Spring Boot-Thymeleaf
我的网络应用程序更新存在问题。
每次我尝试更新一个元素时,我都会收到此错误: not-null属性引用的是null或瞬态值:me.lucacirfeta.model.Element.user
要解决此问题,我需要在表单内部添加一个隐藏有User模型的所有变量的输入,例如:
<input th:field="${element.user.id}" class="form-control" type="text" readonly />
<input th:field="${element.user.username}" class="form-control" type="text" readonly />
<input th:field="${element.user.firstName}" class="form-control" type="text" readonly />
<input th:field="${element.user.lastName}" class="form-control" type="text" readonly />
<input th:field="${element.user.password}" class="form-control" type="text" readonly />
但是我不想在表单上添加此隐藏数据。 我还尝试在JoinColumn批注中删除Element模型中的“ nullable = false”,但是当我尝试更新时,Hibernate丢失了用户将其设置为“ null”的引用。
我该如何解决?
这是我的代码:
型号
package me.lucacirfeta.model;
// default package
// Generated 29-lug-2020 10.31.08 by Hibernate Tools 4.3.5.Final
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* Element generated by hbm2java
*/
@Entity
@Table(name = "ELEMENT",schema = "TEST")
public class Element implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 7331854204646419731L;
private long id;
private ElDate elDate;
private Period period;
private User user;
private PlaceOfDiscovery placeOfDiscovery;
private ElType elType;
private ElDimension elDimension;
private String description;
private String otherDetails;
private Date created;
private boolean validation;
private Date discoveryDate;
private Set<ElementImage> elementImages = new HashSet<ElementImage>(0);
public Element() {
}
@Id
@Column(name = "ID",unique = true,nullable = false,precision = 22,scale = 0)
@GeneratedValue(strategy = GenerationType.IDENTITY)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name = "ID_DATE",nullable=false,insertable=false)
public ElDate getElDate() {
return this.elDate;
}
public void setElDate(ElDate elDate) {
this.elDate = elDate;
}
@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name = "ID_PERIOD",insertable=false)
public Period getPeriod() {
return this.period;
}
public void setPeriod(Period period) {
this.period = period;
}
@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name = "ID_USER",insertable=false)
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name = "ID_PLACE_OF_DISCOVERY")
public PlaceOfDiscovery getPlaceOfDiscovery() {
return this.placeOfDiscovery;
}
public void setPlaceOfDiscovery(PlaceOfDiscovery placeOfDiscovery) {
this.placeOfDiscovery = placeOfDiscovery;
}
@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name = "ID_EL_TYPE",insertable=false)
public ElType getElType() {
return this.elType;
}
public void setElType(ElType elType) {
this.elType = elType;
}
@ManyToOne(fetch = FetchType.LAZY,cascade = {CascadeType.ALL})
@JoinColumn(name = "ID_EL_DIMENSION",insertable=false)
public ElDimension getElDimension() {
return this.elDimension;
}
public void setElDimension(ElDimension elDimension) {
this.elDimension = elDimension;
}
@Column(name = "DESCRIPTION")
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
@Column(name = "OTHER_DETAILS")
public String getOtherDetails() {
return this.otherDetails;
}
public void setOtherDetails(String otherDetails) {
this.otherDetails = otherDetails;
}
@Column(name = "CREATED")
@Temporal(TemporalType.TIMESTAMP)
public Date getCreated() {
return this.created;
}
public void setCreated(Date created) {
this.created = created;
}
@Column(name = "VALIDATION",precision = 1,scale = 0)
public boolean isValidation() {
return this.validation;
}
public void setValidation(boolean validation) {
this.validation = validation;
}
@Temporal(TemporalType.DATE)
@Column(name = "DISCOVERY_DATE",length = 7)
public Date getDiscoveryDate() {
return this.discoveryDate;
}
public void setDiscoveryDate(Date discoveryDate) {
this.discoveryDate = discoveryDate;
}
@OneToMany(fetch = FetchType.LAZY,mappedBy = "element",cascade = {CascadeType.ALL})
public Set<ElementImage> getElementImages() {
return this.elementImages;
}
public void setElementImages(Set<ElementImage> elementImages) {
this.elementImages = elementImages;
}
}
控制器
package me.lucacirfeta.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import me.lucacirfeta.model.Element;
import me.lucacirfeta.service.ElementService;
import me.lucacirfeta.service.ServiceException;
@Controller
@RequestMapping(value = "/admin/update/{id}")
public class AdminUpdateElementController {
@Autowired
private ElementService elementService;
@GetMapping
public ModelAndView updateElement(@PathVariable(name = "id") Long id) {
Element element = null;
try {
element = elementService.findById(id);
} catch (ServiceException e) {
System.out.println(e.getMessage());
}
ModelAndView mv = new ModelAndView("updateElement");
mv.addObject("element",element);
return mv;
}
@PostMapping
@RequestMapping(value = "/formUpdate")
public String formUpdateElement(@ModelAttribute Element element) {
try {
this.elementService.update(element);
} catch (ServiceException e) {
System.out.println(e.getMessage());
}
return "redirect:/admin/elements";
}
}
application.properties
# ===============================
# = DATA SOURCE
# ===============================
# Set here configurations for the database connection
# Connection url for the database "netgloo_blog"
spring.datasource.url= jdbc:****
# Username and password
spring.datasource.username= ****
spring.datasource.password= ****
spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
# Keep the connection alive if idle for a long time (needed in production)
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
# ===============================
# = JPA / HIBERNATE
# ===============================
# Show or not log for each sql query
spring.jpa.show-sql = false
spring.jpa.hibernate.format_sql= true
# Hibernate ddl auto (create,create-drop,update): with "update" the database
# schema will be automatically updated accordingly to java entities found in
# the project
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
# Allows Hibernate to generate SQL optimized for a particular DBMS
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
# ===============================
# = OTHERS
# ===============================
entitymanager.packagesToScan= me.lucacirfeta
server.port=8081
server.session.timeout=15
spring.mvc.hiddenmethod.filter.enabled=true
logging.level.org.springframework.web=DEBUG
html页面
<!DOCTYPE html>
<html lang="it" xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="header :: header"></div>
<link th:href="@{/css/style.css}" rel="stylesheet" type="text/css" />
<title th:text="Aggiorna + ' ' + Elemento + ' ' + ID + ' ' + ${element.id}"></title>
</head>
<body>
<div th:insert="navbar :: navbar"></div>
<h2 style="text-align: center;">Aggiorna Elemento</h2>
<!-- Start of main body of record -->
<div class="container-fluid" style="width: 60%;">
<!-- Start of descriptive data -->
<div class="row-fluid">
<div>
<form th:action="@{'/admin/update/' + ${element.id} + '/formUpdate'}" th:object="${element}"
th:method="post">
<!-- Header of section -->
<hr>
<p><strong>ID Univoco:</strong> <span th:text="${id}"></span></p>
<div class="form-group">
<h1 class="lead">Tipo</h1>
<input th:field="*{elType.elementType}" type="text" class="form-control" />
</div>
<!-- The description of the object -->
<div>
<div class="form-group">
<h4 class="lead">Descrizione</h4>
<textarea rows="5" th:field="*{description}" class="form-control"></textarea>
</div>
</div>
<p>
</p>
<!-- Others details -->
<div class="form-group">
<h4 class="lead">Altri dettagli</h4>
<textarea th:field="*{otherDetails}" class="form-control"></textarea>
</div>
<div class="container" style="display: flex; justify-content: space-between;">
<div class="row">
<div class="col">
<h4 class="lead">Cronologia</h4>
<p>
Periodo dal: <input th:field="${element.Period.periodFrom}" class="form-control"
type="text" />
Periodo al: <input th:field="${element.Period.periodTo}" class="form-control"
type="text" />
Data dal: Circa AD <input th:field="${element.elDate.dateFrom}" class="form-control"
type="text" />
Data al: Circa AD <input th:field="${element.elDate.dateTo}" class="form-control"
type="text" />
</p>
</div>
</div>
<div class="row">
<div class="col">
<h4 class="lead">Dimensioni e Peso</h4>
<p>
Lunghezza: mm <input th:field="${element.elDimension.elLength}" class="form-control"
type="text" />
Peso: g. <input th:field="${element.elDimension.elWeight}" class="form-control"
type="text" />
Spessore: g. <input th:field="${element.elDimension.elThickness}"
class="form-control" type="text" />
Diametro: mm <input th:field="${element.elDimension.elDiameter}"
class="form-control" type="text" />
</p>
</div>
</div>
<div class="row">
<div class="col">
<h4 class="lead">Materiale</h4>
<p>
Materiale: <input th:field="${element.elType.material}" class="form-control"
type="text" />
</p>
</div>
</div>
</div>
<div class="container" style="display: flex; justify-content: space-between;">
<div class="row">
<div class="col">
<h4 class="lead">Validazione</h4>
<p>
<input th:field="${element.validation}" class="form-control" type="text" />
</p>
</div>
</div>
<div class="row">
<div class="col">
<h4 class="lead">User</h4>
<p>
<input th:field="${element.user.id}" class="form-control" type="text" readonly />
<input th:field="${element.user.username}" class="form-control" type="text"
readonly />
<input th:field="${element.user.firstName}" class="form-control" type="text"
readonly />
<input th:field="${element.user.lastName}" class="form-control" type="text"
readonly />
<input th:field="${element.user.password}" class="form-control" type="text"
readonly />
</p>
</div>
</div>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Aggiorna</button>
</div>
</form>
<!-- End of descriptive data -->
</div>
</div>
</div>
<div th:insert="scripts :: scripts"></div>
</body>
</html>
解决方法
在这种情况下,最好创建一个简单的DTO类,例如ElementDTO
public class ElementDTO {
//all the fields that you need,getters,setters and default constructor
}
然后通过在控制器中添加以下方法将其传递给更新表单
@ModelAttribute("element")
public ElementDTO elementDTO(){
return new ElementDTO();
}
这种方法更好的原因有几个:
- 您不需要在每次加载表单时都预先从数据库中加载对象
- 您只需定义要更新的字段,而不必担心非空字段上的休眠规则。
然后在controlle方法中期望对象只是将参数更改为(ElementDTO elementDTO)
并将所有更新的字段复制到actial Element对象,然后将其保存在数据库中
我找到了解决方法。
我在添加有@SessionAttributes的会话中使用一个元素,并且没有丢失与User的引用。
这是已编辑的控制器:
package me.lucacirfeta.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import me.lucacirfeta.model.Element;
import me.lucacirfeta.service.ElementService;
import me.lucacirfeta.service.ServiceException;
@Controller
@RequestMapping(value = "/admin/update/{id}")
@SessionAttributes(value = "element")
public class AdminUpdateElementController {
@Autowired
private ElementService elementService;
@ModelAttribute("element")
public Element updateElement(@PathVariable(name = "id") Long id) {
Element element = null;
try {
element = elementService.findById(id);
} catch (ServiceException e) {
System.out.println(e.getMessage());
}
return element;
}
@GetMapping
public String updateElement() {
return "updateElement";
}
@PostMapping
@RequestMapping(value = "/formUpdate")
public String formUpdateElement(Element element) {
try {
this.elementService.update(element);
} catch (ServiceException e) {
System.out.println(e.getMessage());
}
return "redirect:/admin/elements";
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。