Thymeleaf模板关联字段报错:Property or field 'ref' cannot be found on null
这个报错的原因其实很直接:你在模板里访问${p.iden.ref}时,p.iden的值是null,所以当你试图获取它的ref属性时,就会抛出这个找不到属性的异常。结合你提供的代码,我来拆解下可能的原因和对应的解决思路:
一、先排查数据库数据问题
首先去检查你的target表中的CODE_IDEN字段,有没有值为null的记录。如果数据库里某些Target记录没有关联的Iden ID,那么加载出来的Target对象的iden属性自然就是null,这时候访问ref肯定会报错。
解决方法:
- 清理数据库中的无效记录:把
CODE_IDEN为null的Target记录补充关联的Iden ID,或者删除这些无效数据。 - 如果业务允许存在无关联的Target,那就要在模板中做非空判断(后面会说具体写法)。
二、处理JPA懒加载导致的关联对象无法加载问题
你的Target类中,iden字段用的是@ManyToOne注解,JPA默认这个关联的加载方式是LAZY(懒加载)。也就是说,当你在控制器中调用trgrepo.findAll()查询Target时,只会加载Target本身的字段,关联的Iden对象并不会立即加载。而控制器方法执行完后,JPA的事务就会关闭,EntityManager也会被销毁,等到Thymeleaf模板渲染时再去访问p.iden,已经没有会话支持懒加载了,最终导致iden为null。
针对懒加载问题,有几种常见的解决方式:
1. 修改关联的加载策略为EAGER
把Target类中的@ManyToOne注解改成:
@ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "CODE_IDEN") public Iden iden;
这样查询Target时会自动加载关联的Iden对象,避免懒加载在视图阶段失效的问题。不过要注意,如果Target数据量很大,EAGER加载可能会带来性能损耗,需要根据实际业务场景权衡。
2. 在Repository中使用JOIN FETCH主动关联查询
在你的Targetrepository中自定义一个查询方法,主动关联加载Iden:
import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import java.util.List; public interface Targetrepository extends CrudRepository<Target, Long> { @Query("SELECT t FROM Target t JOIN FETCH t.iden") List<Target> findAllWithIden(); }
然后在控制器中调用这个方法:
@RequestMapping("/alltarget") public String formall(Model model) { List<Target> listTarget = trgrepo.findAllWithIden(); model.addAttribute("list",listTarget); return "allIdentifiant"; }
这种方式只在需要的时候主动加载关联对象,性能比EAGER更可控。
3. 开启Open Session In View(OSIV)
在application.properties中添加配置:
spring.jpa.open-in-view=true
这个配置会让JPA的会话在视图渲染阶段保持打开,这样懒加载就能正常触发。不过这个方式一直有争议,可能会导致意外的N+1查询或者性能问题,建议谨慎使用。
三、在Thymeleaf模板中添加防御性非空判断
不管上面的问题是否解决,为了让页面更健壮,避免前端报错,最好在模板中对iden做非空检查。这里有两种常用写法:
1. 使用安全导航运算符(Elvis运算符)
<td><span th:text="${p.iden?.ref} ?: '无关联标识'" ></span></td>
?.是安全导航运算符,当p.iden为null时,不会继续访问ref,而是返回null;?:则会在左侧为null时显示右侧的默认值。
2. 使用th:if做条件判断
<td> <span th:if="${p.iden != null}" th:text="${p.iden.ref}"></span> <span th:if="${p.iden == null}">无关联标识</span> </td>
这种写法更直观,适合需要区分显示不同内容的场景。
内容的提问来源于stack exchange,提问作者mimo2000

