Class entity returned from method findAll of JpaRepository has field value of null on field calculated inside the constructor

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

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: &quot;John Doe&quot;
    this.email = email; // ex: &quot;johndoe@gmail.com&quot;
    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&lt;Student, Integer&gt; { }

Inside the StudentController class, I have a route to &quot;/&quot;, which simply gets all the students from DB and return them:

//StudentController.java

@GetMapping(&quot;/&quot;)
public List&lt;Student&gt; getStudents() {
     return studentService.getStudents();
}

The result I was expecting is the following, with the age field's value being 21:

[{&quot;id&quot;:1,&quot;name&quot;:&quot;John Doe&quot;,&quot;email&quot;:&quot;johndoe@gmail.com&quot;,&quot;dateOfBirth&quot;:&quot;2002-05-01&quot;,&quot;age&quot;:21}]

Instead, I got a null value for the age field:

[{&quot;id&quot;:1,&quot;name&quot;:&quot;John Doe&quot;,&quot;email&quot;:&quot;johndoe@gmail.com&quot;,&quot;dateOfBirth&quot;:&quot;2002-05-01&quot;,&quot;age&quot;: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.

[{&quot;id&quot;:1,&quot;name&quot;:&quot;John Doe&quot;,&quot;email&quot;:&quot;johndoe@gmail.com&quot;,&quot;dateOfBirth&quot;:&quot;2002-05-01&quot;,&quot;age&quot;: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 &amp;&amp; dateOfBirth != null) {
        age = Period.between(dateOfBirth, LocalDate.now()).getYears();
       }
       return age;
    }

huangapple
  • 本文由 发表于 2023年5月22日 05:49:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76302053.html
匿名

发表评论

匿名网友

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

确定