Is there an implementation difference between the One-To-One and Many-To-One Entity Relationship?

Viewed 58

I am confused regarding the difference of OneToOne vs ManyToOne (not OneToMany).

Lets assume we have a Person class and a Department Class. When we say Many Persons can belong to one Department, we assume a ManyToOne relation and add a DeptID column in the Person class. This is what makes sense to me and works as said.

However, if we say that Person and Department has a OneToOne relation, this means one person has only one department and one department as only one person. However, this is where the problem comes. JPA allows two Person objects to share the same department nonetheless.

Person Class:

@Entity
@Table
public class Person {
    @Id
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    int id;
    @Column
    int age;
    @Column(length = 20)
    String name;

    @Column(length = 20, nullable = true)
    String cnic;

    @Column(nullable = true)
    boolean married;

    @OneToOne
    Department dep;
}

Department Class:

@Entity
public class Department {
    @Id
    int Id;
    @Column
    String name;
}

Making two Persons with same Department:

public void testOTO() {
        System.out.println("Testing OTO");
        Department dep = new Department(202, "CS");
        depdb.saveAndFlush(dep);

        Person p1 = new Person();
        Person p2 = new Person();
        p1.setDep(dep);
        p2.setDep(dep);

        db.saveAndFlush(p1);
        db.saveAndFlush(p2);
}

I wanted to ask, is the difference between OneToOne and ManyToOne just semantic or am I missing something?

2 Answers

JPA itself does not control if your one-to-one relationship is indeed one-to-one. But your database schema can. Just add a unique constraint to the foreign key column.

The reason JPA does not check this is, because it would be really expensive to do so on the application side. JPA would need to obtain a lock on the foreign key column for the whole table and then check that the value about to be inserted isn't there.

Database constraints on the other hand are mode for this. And while they still require locking, they only lock the index and they do it automatically.

I'm not sure if the popular JPA implementations generate the unique constraint when they generate the schema. If they don't, this might be because it would collide with certain scenarios where there exists two identical values for a short time. If you encounter such problems after creating a unique constraint you might want to check if your database supports deferred constraints.

The above answer is the correct.

You should always protect yourself by declaring the foreign key column to be unique on database level. This way the error would occur on database layer and then bubble up to you in your JPA layer. To do this you have to annotate the side that is the owner of the relationship which has the @OneToOne also with @JoinColumn(unique=true)

However as Hibernate mentions also to the documentation

When using a bidirectional @OneToOne association, Hibernate enforces the unique constraint upon fetching the child-side. If there are more than one children associated with the same parent, Hibernate will throw a org.hibernate.exception.ConstraintViolationException. Continuing the previous example, when adding another PhoneDetails, Hibernate validates the uniqueness constraint when reloading the Phone object.

So even if there is no unique column on database level, Hibernate will still validate this, when you load entities in peristence context as it will enforce the meaning of @OneToOne at this point.

So in case you did

    Department dep = depdb.findById(1L); //load what was previously stored

It would have crushed with ConstraintViolationException as it would have identified 2 Person with this single department which is wrong according to meaning of OneToOne.

But in case of entity persist, hibernate makes no check of this type of violation so it proceeds with duplicate references for the same entity from different parents although this should not be allowed.

Although it is mentioned in Hibernate doc if you inspect closely, that the create tables queries executed for bidirectional relationship for @OneToOne contain no unique constraint, in my opinion the hibernate documentation should be updated to explicitly state this so that developers would be clearly aware and apply the @JoinColumn(unique=true) or some other configuration when needed in order to be protected from this type of issue.

Related