Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting a @OneToOne child entity with @MapsId throws "error:detached entity passed to persist" in Hibernate

I read https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/. I tried suggestion config like(using spring data JPA,hibernate 5.0 as vendor ):

public class PaperSubjectType{
    @Id
    private Long id;

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

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

first I tried the example:

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

paperSubjectTypeRepository.save(paperSubjectType);

error:detached entity passed to persist:PaperSetting. it seems hibernate take PaperSetting as detached when cascade

2 if I want to create both PaperSubjectType and PaperSetting together,do I need to do this:

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

or I should use bidirectional in this situation? thank you!

like image 330
yuxh Avatar asked Dec 06 '17 08:12

yuxh


2 Answers

I tried it Hibernate 5.2 and it works like a charm.

Assuming you have these entities:

@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;
    }
}

And this data access logic:

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 );
} );

The test passes just fine in Hibernate ORM.

Maybe it was a bug in 5.0 that got fixed, so you are better of upgrading.

like image 57
Vlad Mihalcea Avatar answered Nov 12 '22 00:11

Vlad Mihalcea


I think you may have forgotten to wrap the logic in a @Transactional block

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

paperSubjectTypeRepository.save(paperSubjectType);

without that crudRepository.findOne() will open it's own short lived transaction so when you get the return of findOne() the entity is already detached, hence the error

like image 31
Zeromus Avatar answered Nov 11 '22 23:11

Zeromus