Search code examples
hibernatejpa-2.0entity-relationshipself-reference

Self-referencing class in Hibernate or additional table



I have quite common problem, but no idea how to solve it in practice and explain my choice/solution.
I have a situation when there are two tables in the database: Account and Contact. They both represent the same data, because one person (Account) can have in contact list only those people, who also have accounts in the created system. So it is classic one to many relationship between table Account and Contact.
I chose to use such model, that there are two tables instead of one, self-referencing. I am not sure if I chose well.
Here is how these tables look like:

CREATE TABLE account (
    id INT NOT NULL AUTO_INCREMENT,
    login VARCHAR(20) NOT NULL,
    firstName VARCHAR(15) NOT NULL,
    lastName VARCHAR(40) NOT NULL,
    passwordHash CHAR(50) NOT NULL,
    PRIMARY KEY(id));
CREATE TABLE contact (
    ownerid INT NOT NULL,
    contactid INT NOT NULL,
    PRIMARY KEY (ownerid, contactid),
    FOREIGN KEY (ownerid) REFERENCES account(id),
    FOREIGN KEY (contactid) REFERENCES account(id));

My problem is that I don't know how to construct entity classes using annotations. I probably might have used some IDE wizards, but it is not the solution for me, because I could not even check whether everything is correct. I would rather prefer understand topic fully.
My aim is to have situation such that every Account possesses list of contacts. When I remove contact from this list, account is not removed from Account table.
I have read something about inverse and cascade, but unfortunately I don't feel this.

Please show me correct path to solve this problem.


Solution

  • The additional table is just a join table for a many-to-many relationship between Account (as a person/owner) and Account (as a contact/owned). A person owns many contacts, and a contact is owned by several persons.

    So you should have a unique entity with the following association:

    @Entity
    public class Account {
        // ...
    
        @ManyToMany
        @JoinTable(name = "contact",
                   joinColumns = @JoinColumn(name="ownerid"),
                   inverseJoinColumns = @JoinColumn(name="contactid")
        private Set<Account> contacts;
    }
    

    You can also make it bidirectional:

    @Entity
    public class Account {
        // ...
    
        @ManyToMany
        @JoinTable(name = "contact",
                   joinColumns = @JoinColumn(name="ownerid"),
                   inverseJoinColumns = @JoinColumn(name="contactid")
        private Set<Account> contacts;
    
        @ManyToMany(mappedBy = "contacts")
        private Set<Account> owners;
    }