使用 Spring JPA 时在 @AllArgsConstructor 中使用 id 字段是否正确?

Is using id field in @AllArgsConstructor while using Spring JPA correct?

使用 spring-boot 和 JPA 我有一个 Entity 我想使用 lombok 来减少样板代码。但是在我的实体中有 id 字段。我应该将它放在带有 @AllArgsConstructor 的构造函数参数中,还是应该将其从参数列表中删除(不知何故,如何?)因为它是使用 @id@GeneratedValue 注释自动生成的?

代码:

@Entity
@NoArgsConstructor // JPA requires empty constructor
@AllArgsConstructor // ? is id in constuctor needed?

@Getter
@Setter
@ToString(exclude = {"room", "customer"})
public class Reservation {

    @Id
    @GeneratedValue
    private long id;

    private Room room;
    private Customer customer;
    private Date dateFrom;
    private Date dateTo;    
}

在这种情况下 spring-data 使用 setter 来设置值,因此不需要 @AllArgsConstructor

对于您在代码中的问题:

@AllArgsConstructor // ? is id in constuctor needed?

不,不需要。另外,对于你标题中的问题:

Is using id field in @AllArgsConstructor while using Spring JPA correct?

字段 id 不建议将其暴露给任何构造函数或 setter 除非有充分的理由。字段 id 只能由 JPA 实现操作。

请注意,当您在 Reservation class 级别上声明 @Setter 时也会发生此暴露。

这可以避免从 class 级别删除注释并注释每个字段以公开,但更简单的方法是使用继承。

您可以创建一个基础class,例如:

@Entity
@Getter
// Choose your inheritance strategy:
//@Inheritance(strategy=InheritanceType.JOINED)
//@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class BaseEntity {
    @Id
    @GeneratedValue
    private Long id;
}

请注意,字段 id 没有 setter。在 class 上方扩展为:

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString(exclude = {"room", "customer"})
public class Reservation extends BaseEntity {
    private Room room;
    private Customer customer;
    private Date dateFrom;
    private Date dateTo;    
}

和构造函数 & setter 如下:

Reservation r1 = new Reservation();
Reservation r2 = new Reservation(room, customer, dateFrom, dateTo);

r1.setRoom(room);
r1.setCustomer(customer);
r1.setDateFrom(dateFrom);
r1.setDateTo(dateTo);

并且没有办法 - 其他 JPA 使用的反射 - 设置字段 id.

我不知道如何 exactly does setting the id but as there is a keyword JPA and tag 我假设这是 JPA 的事情,并且实际上根本不需要字段 id 的 setter。

Lombok 部分的评论:无法从 @AllArgsConstructor 中排除字段(不使用继承)。对于有限的构造函数,您只能使用 @RequiredArgsConstructor,但这通常对 JPA 实体没有帮助。

我的解决方案是当 id 由 jpa 自动生成并且您使用 lombok 来避免样板代码时。

@Entity
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@AllArgsConstructor
public class Patient {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long reg;

    @NotNull
    private String name;
    @NotNull
    private String gender;
    @NotNull
    private String specimen;
    @NotNull
    private String contact;
    @NotNull
    private String cnic;
    @NotNull
    private String referred_by;

    public Patient(String name, String gender, String specimen, String contact, String cnic, String referred_by) {
        this.name = name;
        this.gender = gender;
        this.specimen = specimen;
        this.contact = contact;
        this.cnic = cnic;
        this.referred_by = referred_by;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
        Patient patient = (Patient) o;
        return reg != null && Objects.equals(reg, patient.reg);
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
}

简单地使用了一个不包括 id 字段的自定义构造函数并且它有效。