1

My database contains 3 tables: person, document and peson_document. Person and Document have many-to-many relationship and are joined with the person_document table which contains addition columns. This is my mapping:

class Person {
    @Cascade(CascadeType.ALL)
    @OneToMany(mappedBy = "compositePK.person", orphanRemoval = true)
    Set<PersonDocument> personDocuments;
}

class Document {
    @OneToMany(mappedBy = "compositePK.document")
    Set<PersonDocument> personDocuments;
}

class PersonDocument {

    @EmbeddedId
    private CompositePK compositePK;

    @Column(name = "person_origin_id")
    private String personID;

    @ManyToOne(fetch = FetchType.LAZY)
    private Provider provider;    

    @Embeddable
    public static class CompositePK implements Serializable {

        @ManyToOne(fetch = FetchType.LAZY)
        private Person person;

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "documents_guid")
        private Document document;
    }

In my code I'm trying save this entities like this:

Set<PersonDocument> pds = new HashSet<>();
Document doc = new Document();
//set doc, set person to new PersonDocument and add once on pds set.
person.getPersonDocuments().addAll(pds);
personRepository.save(person);

The problem is that hibernate doesn't save it and throws exception: insert or update on table violates foreign key constraint - because in document table there is no record. So why hibernate don't persist document before saving person_document and how solve this problem?

Kadzhaev Marat
  • 1,278
  • 1
  • 19
  • 33

1 Answers1

3

Try this:

public class Person {
    @OneToMany(mappedBy = "person", orphanRemoval = true, cascade= CascadeType.ALL)
    Set<PersonDocument> personDocuments;
}


class Document {
    @OneToMany(mappedBy = "document")
    Set<PersonDocument> personDocuments;
}


public class PersonDocument {

    @EmbeddedId
    private CompositePK compositePK;

    @MapsId("person")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "person_origin_id")
    private Person person;

    @MapsId("document")
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "documents_guid")
    private Document document;

    @ManyToOne(fetch = FetchType.LAZY)
    private Provider provider;    
}


@Embeddable
public class CompositePK implements Serializable {

    //these fields should have the same type as the ID field of the corresponding entities
    //assuming Long but you have ommitted the ID fields

    private Long person;

    private Long document;

    //implement equals() and hashcode() as per the JPA spec
}


//you must always set both side of the relationship
Person person = new Person();
Document document = new Document();
PersonDocument pd = new PersonDocument();
pd.setPerson(person);
pd.setDocument(document);
person.getPersonDocuments.add(pd);//assumes initialized
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • Up vote from me. Personally I always replace my `ManyToMany` with 2 `OneToMany`. It gives you much more control, for instance with cascading, and it allows you to move a document from one person to another by changing the reference on the person document. In my opinion `ManyToMany` should never have been implemented, it causes a lot more problems than it solves. – Klaus Groenbaek Apr 21 '17 at 22:48
  • Alan, thank you for answer, but now I have another problem - when I'm trying to save `PersonDocument` I'm getting exception: `could not set a field value by reflection setter of model.entity.PersonDocument$CompositePK.document`, can't understand why is so? – Kadzhaev Marat Apr 24 '17 at 10:09
  • Is it in its own class definition file? You original code had it as a nested class within PersonDocument. As the JPA spec notes "The entity[and embeddable] class must be a top-level class." JPA spec sections 2.1 and 2.5 – Alan Hay Apr 24 '17 at 10:17
  • @AlanHay, I changed my code and now it in its own class definition, but I still get the same exception: `could not set a field value by reflection setter of model.entity.PersonDocumentPK.document` – Kadzhaev Marat Apr 24 '17 at 10:22
  • Add get/set methods? – Alan Hay Apr 24 '17 at 10:23
  • @AlanHay, yes, using lombok – Kadzhaev Marat Apr 24 '17 at 10:23
  • Sorry, dunno. There's plenty of discussion about the issue. Start here: http://stackoverflow.com/questions/23736422/jpa-composite-key-with-manytoone-getting-org-hibernate-propertyaccessexception – Alan Hay Apr 24 '17 at 10:26