英文:
Class entity returned from method findAll of JpaRepository has field value of null on field calculated inside the constructor
问题
我有一个Student类,其中包含一些字段,包括"age"和"dateOfBirth"。age的值是在构造函数内部计算并赋值的,基于dateOfBirth,如下所示:
//Student.java
private Integer id;
private String name;
private String email;
private LocalDate dateOfBirth;
@Transient
private Integer age;
public Student(String name, String email, LocalDate dateOfBirth) {
this.name = name; // 例如:"John Doe"
this.email = email; // 例如:"johndoe@gmail.com"
this.dateOfBirth = dateOfBirth; // 例如:LocalDate.of(2002, Month.MAY, 1));
this.age = Period.between(dateOfBirth, LocalDate.now()).getYears(); // 结果:21
}
Student类内的getAge()
方法返回age字段的值:
//Student.java
public Integer getAge() {
return age; // 返回期望的值(21)
}
(其他字段的getter方法具有与getAge()
相同的结构:它们返回字段的值)
我还有一个简单的Repository,用于从数据库中查询数据。它继承自JpaRepository
,内部没有任何内容:
//StudentRepository.java
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> { }
在StudentController类内,我有一个路由到"/"
,它只是从数据库中获取所有的学生并将它们返回:
//StudentController.java
@GetMapping("/")
public List<Student> getStudents() {
return studentService.getStudents();
}
我期望的结果是以下这样,age字段的值为21:
[{"id":1,"name":"John Doe","email":"johndoe@gmail.com","dateOfBirth":"2002-05-01","age":21}]
但是,实际上,age字段返回了null
值:
[{"id":1,"name":"John Doe","email":"johndoe@gmail.com","dateOfBirth":"2002-05-01","age":null}]
但是,如果我将Student类内的getAge()
方法实现更改为:
// Student.java
public Integer getAge() {
return Period.between(dateOfBirth, LocalDate.now()).getYears();
}
我得到了期望的21
,而不是null
:
[{"id":1,"name":"John Doe","email":"johndoe@gmail.com","dateOfBirth":"2002-05-01","age":21}]
英文:
I have a Student class that has some fields, including "age" and "dateOfBirth". The age value is calculated and attributed inside the constructor, based on the dateOfBirth, as following:
//Student.java
private Integer id;
private String name;
private String email;
private LocalDate dateOfBirth;
@Transient
private Integer age;
public Student(String name, String email, LocalDate dateOfBirth) {
this.name = name; // ex: "John Doe"
this.email = email; // ex: "johndoe@gmail.com"
this.dateOfBirth = dateOfBirth; // ex: LocalDate.of(2002, Month.MAY, 1));
this.age = Period.between(dateOfBirth, LocalDate.now()).getYears(); // result: 21
}
The getAge()
method inside the Student class returns the age field value
//Student.java
public Integer getAge() {
return age; // Returns the expected value (21)
}
(getters for every other field have the same structure as the getAge()
: they return the field's value)
I also have a simple Repository, to query data from DB. It extends from the JpaRepository
and has nothing inside it:
//StudentRepository.java
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> { }
Inside the StudentController class, I have a route to "/"
, which simply gets all the students from DB and return them:
//StudentController.java
@GetMapping("/")
public List<Student> getStudents() {
return studentService.getStudents();
}
The result I was expecting is the following, with the age field's value being 21:
[{"id":1,"name":"John Doe","email":"johndoe@gmail.com","dateOfBirth":"2002-05-01","age":21}]
Instead, I got a null
value for the age field:
[{"id":1,"name":"John Doe","email":"johndoe@gmail.com","dateOfBirth":"2002-05-01","age":null}]
But if I change the getAge()
method implementation of the Student class to:
// Student.java
public Integer getAge() {
return Period.between(dateOfBirth, LocalDate.now()).getYears();
}
I get the 21
as expected, not null
.
[{"id":1,"name":"John Doe","email":"johndoe@gmail.com","dateOfBirth":"2002-05-01","age":21}]
答案1
得分: 2
JPA 不使用您的构造函数。JPA 规范要求无参构造函数。如果您没有提供无参构造函数,您的 JPA 实现可以为您创建一个(例如,openjpa 这样做,参见 https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_pc.html#jpa_overview_pc_no_arg)。
这就是为什么当您将 getter 更改为计算年龄值时,您的代码可以正常工作。
如果您不想每次调用 getter 时都重新计算年龄,您可以:
- 在 setDateOfBirth() 方法中设置出生日期时计算年龄。
- 在 getAge() 方法中懒惰地计算并存储年龄。类似于:
public Integer getAge() {
if (age == null && dateOfBirth != null) {
age = Period.between(dateOfBirth, LocalDate.now()).getYears();
}
return age;
}
英文:
JPA does not use your constructor. JPA specification requires no-arg constructor. You JPA implementation can create one for you if you don't provide one no-arg constructor (For example openjpa does it, see https://openjpa.apache.org/builds/1.2.3/apache-openjpa/docs/jpa_overview_pc.html#jpa_overview_pc_no_arg).
That's why your code works when you change the getter to compute the age value.
If you don't want to recompute the age every time the getter is called you can :
- compute the age when the birth date is set, in the setDateOfBirth() method.
- lazily compute and store the age in the getAge() method. Something like :
public Integer getAge() {
if (age == null && dateOfBirth != null) {
age = Period.between(dateOfBirth, LocalDate.now()).getYears();
}
return age;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论