【问题标题】:JPA/Hibernate/SpringBoot: Inserting @OneToMany relationship entitiesJPA/Hibernate/SpringBoot:插入@OneToMany 关系实体
【发布时间】:2021-12-21 08:43:30
【问题描述】:

我正在努力在 JPA-Hibernate 设置中插入 @OneToMany 实体。

有两个关联的表,其中一个表的外键作为源表的主键。

employee

- id (PK)

employee_location

- employee_id (FK to employee)

这是我的实体:

Employee

@Entity(name = "employee")
class Employee {
     
     @Id
     @Column(name = "id")
     @GeneratedValue()
     private Long id;

     @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
     @JoinColumn(name = "employee_id", referencedColumnName = "id")
     private List<EmployeeLocation> employeeLocations;

} 

Employee Location

@Entity(name = "employee_location")
class EmployeeLocation {
     
         @Id
         @Column(name = "employee_id")
         private Long employeeId;

         @ManyToOne(cascade = CascadeType.ALL)
         @JoinColumn(name = "employee_id", referencedColumnName = "id", insertable = false, updatable = false)
         private Employee employee;

} 

保存实体:

List<EmployeeLocation> locations = Arrays.asList(new EmployeeLocation(), new EmployeeLocation()); 

Employee employee = new Employee();
employee.setLocations(locations);

employee.save(); // Throws exceptions

这引发了我这个错误: org.springframework.orm.jpa.JpaSystemException: ids for this class must be manually assigned before calling save():

我尝试将@Entity 更改为@Embeddable 并删除EmployeeLocation 上的@Id,但它给了我其他Unmapped entity 例外。

如何处理插入/更新@OneToMany 实体?这可能吗?

【问题讨论】:

    标签: java spring hibernate jpa spring-data-jpa


    【解决方案1】:

    如何处理插入/更新 @OneToMany 实体?这可能吗?

    如果您希望 DB 为您生成 primary key 值,您需要使用 @GeneratedValue 注释来请求它

    @Entity(name = "employee")
    class Employee {
         
         @Id
         @Column(name = "id")
         @GeneratedValue // mean -> "Hey, DB, give me an ID"!
         private Long id;
    

    同样适用于EmployeeLocation

    更多详情可以查看here

    如果这不能完全解决您的问题,请发表评论。

    【讨论】:

    • 是的,没错。我忘了在Employee 上添加@GeneratedValue 注释。我现在做了。但是对于EmployeeLocation,employee_id 列是 FK。不是生成值,而是依赖于Employee生成的值
    【解决方案2】:

    在您的EmployeeLocation 实体(详细信息)中,您不能将主主键作为主键,它需要自己的主键。如下:

    @Entity(name = "employee_location")
    class EmployeeLocation {
    
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "employee_location_id")
      private Integer id;
    
      @ManyToOne(cascade = CascadeType.ALL)
      @JoinColumn(name = "employee_id", referencedColumnName = "id", insertable = false, updatable = false)
      private Employee employee;
    
    } 
    

    【讨论】:

    • 你能告诉我为什么这在 JPA 中是不可能的吗?或者指点我一些文档?
    • 当然。因为每个表都应该有自己的主键。检查这个 [stackoverflow.com/a/840182/2933780].
    • 是的,该表确实有一个主键。表employee_location的主键是employee_id,也是外键。两者都是。我现在编辑了我的问题以明确提及这一点。
    • 您不能有这种情况,因为主键是唯一键,而外键的目的是您可以在详细(子)表中对主(父)表有多个引用。跨度>
    • 我明白你的意思,你是对的。因为它是一对多的,所以没有意义。因此我需要一个PK。
    【解决方案3】:

    如果 Employee 实体与 EmployeeLocation 实体具有 OneToOne 关系,您可以只使用 @MapsId。这样,EmployeeLocation id 属性就填充了帖子关联的标识符。

    class EmployeeLocation {
         
        @Id
        @Column(name = "id")
        private Long id;
    
        @OneToOne
        @MapsId
        private Employee employee;
    } 
    

    但由于您的 Employee 实体与 EmployeeLocation 具有 OneToMany 关系,因此 Employee id 属性值不能用作 EmployeeLocation id 属性值,因为与同一 Employee 实体关联的两个或多个 EmployeeLocation 实体将具有相同的 id 值。

    你需要这样的东西:

    @Entity
    public class EmployeeLocation {    
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        ...
        @ManyToOne Employee employee;
    }
    

    在这两种情况下,您都需要将 EmployeeLocation 实体绑定到 Employee 实体,例如:

    class Employee {
        ....
        public void addLocation(EmployeeLocation location) {
            location.setEmployee(this);
            this.employeeLocations.add(location);
        }
    
        public void setLocations(List<EmployeeLocation> locations) {
            for (EmployeeLocation location : locations) {
                location.setEmployee(this);         
            }
            this.employeeLocations = locations;
        }
        ....
    }
    

    另一个选项:使用 ElementCollection

    
    @Entity(name = "employee")
    class Employee {
       
        @Id
        @Column(name = "id")
        @GeneratedValue
        private Long id;
    
        @ElementCollection
        @CollectionTable(
            name="employee_location",
            joinColumns=@JoinColumn(name="EMPLOYEE_ID"))    
        private Set<EmployeeLocation> employeeLocations;
    }
    
    @Embeddable
    class EmployeeLocation {
    
        // properties
    }
    
    
    

    【讨论】:

    • 我看到您在 employee_location 表中添加了一个新列 id。但我不想更改employee_location 表,因为它已经有一个 Primary Key = employee_id (Foreign Key)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-21
    • 2021-05-22
    • 2019-01-12
    相关资源
    最近更新 更多