【问题标题】:Using @PrimaryKeyJoinColumn annotation in spring data jpa在spring data jpa中使用@PrimaryKeyJoinColumn注解
【发布时间】:2021-07-03 03:56:43
【问题描述】:

我尝试使用@PrimaryKeyJoinColumn 注释。执行此操作时,我收到一个错误 - 尝试从空的一对一属性分配 id

用户已保存,但地址未保存。我想为UserAddress 表创建一个公共主键。我找到了一个例子here

请告诉我我做错了什么,为什么这个例子对我不起作用?

https://github.com/mytestPercon/TestHiber

User.java

@Entity
@Table(name = "user", schema = "TestKeyJoin")
public class User implements Serializable {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "id")
   private Long id;

   @Basic
   @Column(name = "name")
   private String name;

   @OneToOne(mappedBy = "user")
   @PrimaryKeyJoinColumn
   private Address activated;

   // Getter and Setter ...
}

Address.java

@Entity
@Table(name = "Address", schema = "TestKeyJoin")
public class Address implements Serializable {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "id")
   private long id;

   @Basic
   @Column(name = "city")
   private String city;

   @OneToOne
   @MapsId
   @JoinColumn(name = "id")
   private User user;

   // Getter and Setter ...

}

SaveController.java

@Controller
public class SaveController {

   @Autowired
   ServiceJpa serviceJpa;

   @GetMapping(value = "/saveUser")
   public String getJpa () {
      User user = new User();
      user.setId(1L);
      user.setName("Michael Joseph Jackson");
      serviceJpa.saveUser(user);

      Address address = new Address();
      address.setId(1L);
      address.setCity("Los Angeles");
      serviceJpa.saveActivated(address);

      return "/saveUser";
   }
}

【问题讨论】:

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


    【解决方案1】:

    尝试通过以下方式更正您的映射:

    @Entity
    @Table(name = "user", schema = "TestKeyJoin")
    public class User implements Serializable {
    
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       @Column(name = "id")
       private Long id;
    
       @Column(name = "name")
       private String name;
    
       @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
       private Address address;
    
       // ...
    }
    
    @Entity
    @Table(name = "Address", schema = "TestKeyJoin")
    public class Address implements Serializable {
    
       @Id
       private Long id;
    
       @Column(name = "city")
       private String city;
    
       /* You can use @PrimaryKeyJoinColumn instead of the @MapsId here.
          See the Example 153. Derived identifier @PrimaryKeyJoinColumn
          from the hibernate documentation.
       */
       @OneToOne
       @MapsId
       @JoinColumn(name = "id")
       private User user;
    
       // ...
    }
    

    然后通过以下方式保存用户:

    @GetMapping(value = "/saveUser")
    public String getJpa () {
    
       User user = new User();
       user.setName("Michael Joseph Jackson");
    
       Address address = new Address();
       address.setCity("Los Angeles");
    
       // make both sides of the bidirectional @OneToOne in-sync
       user.setAddress(address);
       address.setUser(user);
    
       serviceJpa.saveUser(user);
    
       return "/saveUser";
    }
    

    还有几个注意事项:

    1. 每当形成双向关联时,应用程序开发人员必须确保双方始终保持同步。

    2. 你不应该单独保存你的实体,你应该在你的关联中使用正确的cascading

    3. 您不应为generated identifier 设置值。

    4. 当您在Address.user 上使用@MapsId 时,实际上意味着Address 实体将从一对一关联中借用标识符。因此,您不应将@GeneratedValue 注释用于Address.id。 (见this

    【讨论】:

    • 这是一个很好的答案,而且很有效。非常感谢你的工作。我有一个小问题 - 请告诉我,您为什么不使用 @PrimaryKeyJoinColumn 注释?
    • @PrimaryKeyJoinColumn 注释用于与@MapsId 注释类似的目的(请参阅我回答的最后一个链接)。但如果我正确理解你的意图,你想在这里使用生成的标识符。
    • 如何在不接触用户的情况下删除地址?在此示例中,我无法删除“地址”。它没有被删除。如何在不接触用户的情况下删除地址?
    • 你做不到。这是一个代价:我想为用户和地址表创建一个公共主键。您可以在用户表中创建一个单独的字段,该字段将是地址表的FK(使用 ON DELETE SET NULL)。在这种情况下,您将能够删除地址记录,并且与该地址相关的所有 FK 都将设置为 NULL
    • 可以的。我已经完成了。指定 orphanRemoval = true 就足够了。非常感谢您的帮助。事实上,你让我走上了正轨,让我大开眼界。
    【解决方案2】:

    删除地址实体中id字段上的 @GeneratedValue(strategy = GenerationType.IDENTITY),因为它是用户实体的外键,不应自动递增。 编辑:尝试添加这些行

     public String getJpa () {
    
                User user = new User();
                user.setId(1L);
                user.setName("Michael Joseph Jackson");
                Address address = new Address();
                address.setId(1L);
                address.setCity("Los Angeles");
                user.setActivated(address);
                serviceJpa.saveUser(user);
                 return "/saveUser";
            }
    

    【讨论】:

    • 没用。即使您指定自动。
    • 不要为该列的生成指定任何开始,因为它是一个外键,它从用户主键中获取值。(删除所有该行)
    • 我按照你说的做了,错误消失了但是地址没有保存。因此,这个方案并不能解决问题。
    猜你喜欢
    • 2021-12-24
    • 2017-11-09
    • 1970-01-01
    • 2012-03-25
    • 2012-05-16
    • 2019-12-19
    • 1970-01-01
    • 2012-03-08
    相关资源
    最近更新 更多