【问题标题】:Persisting a @OneToOne child entity with @MapsId throws "error:detached entity passed to persist" in Hibernate使用 @MapsId 持久化 @OneToOne 子实体会在 Hibernate 中引发“错误:传递给持久化的分离实体”
【发布时间】:2018-05-20 02:19:30
【问题描述】:

我读过https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/。 我尝试了类似的建议配置(使用 spring data JPA,hibernate 5.0 作为供应商):

public class PaperSubjectType{
    @Id
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private PaperSetting paperSetting;
..
}

class PaperSetting{
  @Id
  @GeneratedValue
  private Long id;
..
}

首先我尝试了这个例子:

PaperSetting paperSettingInDb = paperSettingRepository.findOne(1);
PaperSubjectType paperSubjectType = new PaperSubjectType();
paperSubjectType.setSubjectCode("91");
paperSubjectType.setPaperSetting(paperSettingInDb);

paperSubjectTypeRepository.save(paperSubjectType);

错误:分离的实体传递给 persist:PaperSetting。 级联时似乎休眠将 PaperSetting 视为分离

2 如果我想同时创建 PaperSubjectType 和 PaperSetting,我需要这样做吗:

PaperSetting paperSetting = new PaperSetting();
paperSetting.setxx;
PaperSetting  paperSettingInDbNew = paperSettingRepository.save(paperSetting);
PaperSubjectType paperSubjectType = new PaperSubjectType();
paperSubjectType.setPaperSetting(paperSettingInDbNew);
paperSubjectTypeRepository.save(paperSubjectType);

或者我应该在这种情况下使用双向? 谢谢!

【问题讨论】:

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


    【解决方案1】:

    我认为您可能忘记将逻辑包装在 @Transactional 块中

    @Transactional
    PaperSetting paperSettingInDb = paperSettingRepository.findOne(1);
    PaperSubjectType paperSubjectType = new PaperSubjectType();
    paperSubjectType.setSubjectCode("91");
    paperSubjectType.setPaperSetting(paperSettingInDb);
    
    paperSubjectTypeRepository.save(paperSubjectType);
    

    没有crudRepository.findOne() 将打开它自己的短暂事务,因此当您获得 findOne() 的返回时,实体已经分离,因此出现错误

    【讨论】:

    • 我在测试中添加了@Transactional@Commit,这次“字段'id'没有默认值”
    • @yuxh 你的 PaperSubjectType 在 id 上没有自动生成注释...所以你要么在保存之前设置它,要么添加自动生成
    【解决方案2】:

    tried it Hibernate 5.2,它就像一个魅力。

    假设您有这些实体:

    @Entity(name = "Person")
    public static class Person  {
    
        @Id
        @GeneratedValue
        private Long id;
    
        @NaturalId
        private String registrationNumber;
    
        public Person() {}
    
        public Person(String registrationNumber) {
            this.registrationNumber = registrationNumber;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getRegistrationNumber() {
            return registrationNumber;
        }
    }
    
    @Entity(name = "PersonDetails")
    public static class PersonDetails  {
    
        @Id
        private Long id;
    
        private String nickName;
    
        @OneToOne
        @MapsId
        private Person person;
    
        public String getNickName() {
            return nickName;
        }
    
        public void setNickName(String nickName) {
            this.nickName = nickName;
        }
    
        public Person getPerson() {
            return person;
        }
    
        public void setPerson(Person person) {
            this.person = person;
        }
    }
    

    还有这个数据访问逻辑:

    Person _person = doInJPA( this::entityManagerFactory, entityManager -> {
        Person person = new Person( "ABC-123" );
        entityManager.persist( person );
    
        return person;
    } );
    
    doInJPA( this::entityManagerFactory, entityManager -> {
        Person person = entityManager.find( Person.class, _person.getId() );
    
        PersonDetails personDetails = new PersonDetails();
        personDetails.setNickName( "John Doe" );
        personDetails.setPerson( person );
    
        entityManager.persist( personDetails );
    } );
    

    在 Hibernate ORM 中测试通过就好了。

    也许是 5.0 中的一个 bug 得到了修复,所以你最好升级一下。

    【讨论】:

    • ,我找到原因了:我必须在private PaperSetting paperSetting上添加@JoinColumn(name = "id");或者它会在保存 PaperSetting 时在 PaperSubjectType 表中创建一个额外的“paper_setting_id”列。你知道为什么吗?
    • 如果您使用@MapsId,则不需要。
    • ,我设置了 hibernate.hbm2ddl.auto=none 然后再试一次。我必须将@JoinColumn(name = "id") 与@MapsId 一起使用,否则它会抱怨插入时找不到列'paper_setting_id'
    【解决方案3】:

    1)添加级联选项:

    @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @MapsId
    private PaperSetting paperSetting;
    

    2) 设置好之后,您可以在重新创建两个实体时仅保存 PaperSubjectType:

    PaperSetting paperSetting = new PaperSetting();
    paperSetting.setxx;
    PaperSubjectType paperSubjectType = new PaperSubjectType();
    paperSubjectType.setPaperSetting(paperSettingInDbNew);
    paperSubjectTypeRepository.save(paperSubjectType);
    

    【讨论】:

    • 1) 你的配置和我的一样。 2) 字段 'id' 没有默认值
    猜你喜欢
    • 2013-06-03
    • 2017-10-21
    • 1970-01-01
    • 2018-06-09
    • 2020-11-02
    • 1970-01-01
    • 2020-12-27
    • 2013-06-26
    • 1970-01-01
    相关资源
    最近更新 更多