不为空的属性引用了一个空或临时值 – Spring Boot – Thymeleaf

huangapple go评论78阅读模式
英文:

Not-null property references a null or transient value - Spring Boot - Thymeleaf

问题

I have a problem with the update on my webapp.

每次尝试更新元素时,我都会遇到这个错误:
not-null属性引用了null或临时值:me.lucacirfeta.model.Element.user

To solve this problem i need to add on inside my form an input hidden with all the variable of User model, example:

为了解决这个问题,我需要在表单内部添加一个带有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 />

But i don't want to add on my form this hidden data.

但我不想在我的表单中添加这些隐藏的数据。

I also tried to remove in the Element model "nullable = false" in the JoinColumn annotation but when i try to update Hibernate lost the reference on User setting it up to "null".

我还尝试在Element模型中删除JoinColumn注释中的"nullable = false",但当我尝试更新时,Hibernate丢失了对User的引用,并将其设置为"null"。

How can I resolve?

我该如何解决?

This is my code:

这是我的代码:

Model
模型

// 代码太长,请参考原文。

Controller
控制器

// 代码太长,请参考原文。

application.properties
应用程序配置

# 代码太长,请参考原文。

HTML Page
HTML页面

// 代码太长,请参考原文。

以上是您提供的内容的翻译。

英文:

I have a problem with the update on my webapp.

Everytime I try to update an Element i got this error:
not-null property references a null or transient value : me.lucacirfeta.model.Element.user

To solve this problem i need to add on inside my form an input hidden with all the variable of User model, example:

&lt;input th:field=&quot;${element.user.id}&quot; class=&quot;form-control&quot; type=&quot;text&quot; readonly /&gt;
&lt;input th:field=&quot;${element.user.username}&quot; class=&quot;form-control&quot; type=&quot;text&quot; readonly /&gt;
&lt;input th:field=&quot;${element.user.firstName}&quot; class=&quot;form-control&quot; type=&quot;text&quot; readonly /&gt;
&lt;input th:field=&quot;${element.user.lastName}&quot; class=&quot;form-control&quot; type=&quot;text&quot; readonly /&gt;
&lt;input th:field=&quot;${element.user.password}&quot; class=&quot;form-control&quot; type=&quot;text&quot; readonly /&gt;

But i don't want to add on my form this hidden data.
I also tried to remove in the Element model "nullable = false" in the JoinColumn annotation but when i try to update Hibernate lost the reference on User setting it up to "null".

How can I resolve?

This is my code:

Model

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 = &quot;ELEMENT&quot;, schema = &quot;TEST&quot;)
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&lt;ElementImage&gt; elementImages = new HashSet&lt;ElementImage&gt;(0);

	public Element() {
	}

	@Id
	@Column(name = &quot;ID&quot;, 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 = &quot;ID_DATE&quot;, 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 = &quot;ID_PERIOD&quot;, nullable=false, 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 = &quot;ID_USER&quot;, nullable=false, 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 = &quot;ID_PLACE_OF_DISCOVERY&quot;)
	public PlaceOfDiscovery getPlaceOfDiscovery() {
		return this.placeOfDiscovery;
	}

	public void setPlaceOfDiscovery(PlaceOfDiscovery placeOfDiscovery) {
		this.placeOfDiscovery = placeOfDiscovery;
	}

	@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
	@JoinColumn(name = &quot;ID_EL_TYPE&quot;, nullable=false, 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 = &quot;ID_EL_DIMENSION&quot;, nullable=false, insertable=false)
	public ElDimension getElDimension() {
		return this.elDimension;
	}

	public void setElDimension(ElDimension elDimension) {
		this.elDimension = elDimension;
	}

	@Column(name = &quot;DESCRIPTION&quot;)
	public String getDescription() {
		return this.description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	@Column(name = &quot;OTHER_DETAILS&quot;)
	public String getOtherDetails() {
		return this.otherDetails;
	}

	public void setOtherDetails(String otherDetails) {
		this.otherDetails = otherDetails;
	}

	@Column(name = &quot;CREATED&quot;)
	@Temporal(TemporalType.TIMESTAMP)
	public Date getCreated() {
		return this.created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	@Column(name = &quot;VALIDATION&quot;, nullable = false, precision = 1, scale = 0)
	public boolean isValidation() {
		return this.validation;
	}

	public void setValidation(boolean validation) {
		this.validation = validation;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = &quot;DISCOVERY_DATE&quot;, length = 7)
	public Date getDiscoveryDate() {
		return this.discoveryDate;
	}

	public void setDiscoveryDate(Date discoveryDate) {
		this.discoveryDate = discoveryDate;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = &quot;element&quot;, cascade = {CascadeType.ALL})
	public Set&lt;ElementImage&gt; getElementImages() {
		return this.elementImages;
	}

	public void setElementImages(Set&lt;ElementImage&gt; elementImages) {
		this.elementImages = elementImages;
	}
	
}

Controller

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 = &quot;/admin/update/{id}&quot;)
public class AdminUpdateElementController {

	@Autowired
	private ElementService elementService;

	@GetMapping
	public ModelAndView updateElement(@PathVariable(name = &quot;id&quot;) Long id) {
		Element element = null;

		try {
			element = elementService.findById(id);

		} catch (ServiceException e) {
			System.out.println(e.getMessage());
		}
		ModelAndView mv = new ModelAndView(&quot;updateElement&quot;);
		mv.addObject(&quot;element&quot;, element);
		
		return mv;
	}

	@PostMapping
	@RequestMapping(value = &quot;/formUpdate&quot;)
	public String formUpdateElement(@ModelAttribute Element element) {

		try {
			this.elementService.update(element);
		} catch (ServiceException e) {
			System.out.println(e.getMessage());
		}

		return &quot;redirect:/admin/elements&quot;;
	}

}

application.properties

# ===============================
# = DATA SOURCE
# ===============================

# Set here configurations for the database connection

# Connection url for the database &quot;netgloo_blog&quot;
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 &quot;update&quot; 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 page

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;it&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;
&lt;head&gt;
&lt;div th:replace=&quot;header :: header&quot;&gt;&lt;/div&gt;
&lt;link th:href=&quot;@{/css/style.css}&quot; rel=&quot;stylesheet&quot; type=&quot;text/css&quot; /&gt;
&lt;title th:text=&quot;Aggiorna + &#39; &#39; + Elemento + &#39; &#39; + ID + &#39; &#39; + ${element.id}&quot;&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div th:insert=&quot;navbar :: navbar&quot;&gt;&lt;/div&gt;
&lt;h2 style=&quot;text-align: center;&quot;&gt;Aggiorna Elemento&lt;/h2&gt;
&lt;!--  Start of main body of record --&gt;
&lt;div class=&quot;container-fluid&quot; style=&quot;width: 60%;&quot;&gt;
&lt;!--  Start of descriptive data --&gt;
&lt;div class=&quot;row-fluid&quot;&gt;
&lt;div&gt;
&lt;form th:action=&quot;@{&#39;/admin/update/&#39; + ${element.id} + &#39;/formUpdate&#39;}&quot; th:object=&quot;${element}&quot;
th:method=&quot;post&quot;&gt;
&lt;!-- Header of section --&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;ID Univoco:&lt;/strong&gt; &lt;span th:text=&quot;${id}&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;form-group&quot;&gt;
&lt;h1 class=&quot;lead&quot;&gt;Tipo&lt;/h1&gt;
&lt;input th:field=&quot;*{elType.elementType}&quot; type=&quot;text&quot; class=&quot;form-control&quot; /&gt;
&lt;/div&gt;
&lt;!-- The description of the object --&gt;
&lt;div&gt;
&lt;div class=&quot;form-group&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;Descrizione&lt;/h4&gt;
&lt;textarea rows=&quot;5&quot; th:field=&quot;*{description}&quot; class=&quot;form-control&quot;&gt;&lt;/textarea&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;
&lt;/p&gt;
&lt;!-- Others details --&gt;
&lt;div class=&quot;form-group&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;Altri dettagli&lt;/h4&gt;
&lt;textarea th:field=&quot;*{otherDetails}&quot; class=&quot;form-control&quot;&gt;&lt;/textarea&gt;
&lt;/div&gt;
&lt;div class=&quot;container&quot; style=&quot;display: flex; justify-content: space-between;&quot;&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;Cronologia&lt;/h4&gt;
&lt;p&gt;
Periodo dal: &lt;input th:field=&quot;${element.Period.periodFrom}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
Periodo al: &lt;input th:field=&quot;${element.Period.periodTo}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
Data dal: Circa AD &lt;input th:field=&quot;${element.elDate.dateFrom}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
Data al: Circa AD &lt;input th:field=&quot;${element.elDate.dateTo}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;Dimensioni e Peso&lt;/h4&gt;
&lt;p&gt;
Lunghezza: mm &lt;input th:field=&quot;${element.elDimension.elLength}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
Peso: g. &lt;input th:field=&quot;${element.elDimension.elWeight}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
Spessore: g. &lt;input th:field=&quot;${element.elDimension.elThickness}&quot;
class=&quot;form-control&quot; type=&quot;text&quot; /&gt;
Diametro: mm &lt;input th:field=&quot;${element.elDimension.elDiameter}&quot;
class=&quot;form-control&quot; type=&quot;text&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;Materiale&lt;/h4&gt;
&lt;p&gt;
Materiale: &lt;input th:field=&quot;${element.elType.material}&quot; class=&quot;form-control&quot;
type=&quot;text&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;container&quot; style=&quot;display: flex; justify-content: space-between;&quot;&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;Validazione&lt;/h4&gt;
&lt;p&gt;
&lt;input th:field=&quot;${element.validation}&quot; class=&quot;form-control&quot; type=&quot;text&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;row&quot;&gt;
&lt;div class=&quot;col&quot;&gt;
&lt;h4 class=&quot;lead&quot;&gt;User&lt;/h4&gt;
&lt;p&gt;
&lt;input th:field=&quot;${element.user.id}&quot; class=&quot;form-control&quot; type=&quot;text&quot; readonly /&gt;
&lt;input th:field=&quot;${element.user.username}&quot; class=&quot;form-control&quot; type=&quot;text&quot;
readonly /&gt;
&lt;input th:field=&quot;${element.user.firstName}&quot; class=&quot;form-control&quot; type=&quot;text&quot;
readonly /&gt;
&lt;input th:field=&quot;${element.user.lastName}&quot; class=&quot;form-control&quot; type=&quot;text&quot;
readonly /&gt;
&lt;input th:field=&quot;${element.user.password}&quot; class=&quot;form-control&quot; type=&quot;text&quot;
readonly /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class=&quot;form-group&quot;&gt;
&lt;button class=&quot;btn btn-primary&quot; type=&quot;submit&quot;&gt;Aggiorna&lt;/button&gt;
&lt;/div&gt;
&lt;/form&gt;
&lt;!-- End of descriptive data --&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div th:insert=&quot;scripts :: scripts&quot;&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;

答案1

得分: 0

对于这种情况,最好创建一个简单的DTO类,例如ElementDTO:

public class ElementDTO {
    //所有需要的字段,包括getter、setter和默认构造函数
}

然后通过在你的控制器中添加以下方法,将它传递给更新表单:

@ModelAttribute("element")
public ElementDTO elementDTO(){
    return new ElementDTO();
}

这种方法有以下几个优点:

  1. 每次加载表单时,你不需要预先从数据库中加载对象。
  2. 你只需要定义要更新的字段,不必担心Hibernate对非空字段的规则。

然后,在期望接收对象的控制器方法中,将参数更改为(ElementDTO elementDTO),并将所有更新的字段复制到实际的Element对象中,然后保存在数据库中。

英文:

For such situations it is best to create a simple DTO class like ElementDTO

public class ElementDTO {
//all the fields that you need, getters, setters and default constructor
}

And then pass it to the update form by adding the following method in you controller

@ModelAttribute(&quot;element&quot;)
public ElementDTO elementDTO(){
return new ElementDTO();
}

This approach is better for several reasons:

  1. You don't need to load an object from your database in advance everytime the form is loaded
  2. You only need to define fields that you want to update and don't have to worry about hibernates rules on non-null fields.

Then in the controlle method expecting the object you just change the parameter to (ElementDTO elementDTO) and copy all updated fields to the actial Element object that you then save in the database

答案2

得分: 0

我找到了解决方案。

我在会话中使用了@SessionAttributes添加的Element,没有丢失与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";
    }

}

注意:我已经删除了注释中的HTML实体字符编码(如&quot;),以便使代码更清晰。

英文:

I found the solution.

I use an Element in the session added with @SessionAttributes and no lost the references with User.

This is the edited controller:

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 = &quot;/admin/update/{id}&quot;)
@SessionAttributes(value = &quot;element&quot;)
public class AdminUpdateElementController {
@Autowired
private ElementService elementService;
@ModelAttribute(&quot;element&quot;)
public Element updateElement(@PathVariable(name = &quot;id&quot;) 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 &quot;updateElement&quot;;
}
@PostMapping
@RequestMapping(value = &quot;/formUpdate&quot;)
public String formUpdateElement(Element element) {
try {
this.elementService.update(element);
} catch (ServiceException e) {
System.out.println(e.getMessage());
}
return &quot;redirect:/admin/elements&quot;;
}
}

huangapple
  • 本文由 发表于 2020年8月14日 05:11:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/63403244.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定