Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persist OneToOne relation with SpringData JPA

I have the next two entities with a OneToOne relation between them:

@Entity
@Table(name = "tasks")
public class Task {
    @OneToOne(mappedBy = "task", cascade = CascadeType.PERSIST)
    private Tracker tracker;

    /* More code */
}

@Entity
@Table(name = "trackers")
public class Tracker {
    @OneToOne
    @JoinColumn(name = "trk_task", unique = true)
    private Task task;

    /* More code */
}

I'm trying to run this code:

Task task = taskService.findDispatchableTask();
if (task != null) {
    Tracker tracker = trackerService.findIdleTracker();
    if (tracker != null) {
        task.setTracker(tracker);
        task.setStatus(TaskStatus.DISPATCHED);
        taskService.save(task);
    }
}

But I get this error:

ERROR org.hibernate.AssertionFailure  - an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session) 
org.hibernate.AssertionFailure: non-transient entity has a null id

I can "solve" it changing my code to:

Task task = taskService.findDispatchableTask();
if (task != null) {
    Tracker tracker = trackerService.findIdleTracker();
    if (tracker != null) {
        tracker.setTask(task);
        trackerService.save(tracker);
        task.setTracker(tracker);
        task.setStatus(TaskStatus.DISPATCHED);
        taskService.save(task);
    }
}

My question is, Which is the proper way to persist a OneToOne relation? In my code, Why do I have save both parts of the relation to make it work?

like image 490
David Moreno García Avatar asked May 04 '13 23:05

David Moreno García


People also ask

How do you create a one-to-one relationship in Spring boot JPA?

In Spring Data JPA, a one-to-one relationship between two entities is declared using the @OneToOne annotation. It accepts the following parameters: fetch — Defines a strategy for fetching data from the database. By default, it is EAGER indicating that the data must be eagerly fetched.

How does JPA define one-to-one relationships?

For a One-to-One relationship in JPA, each entity instance is related to a single instance of another entity. It means each row of one entity is referred to one and only one row of another entity.

How do you write one to many relationship in JPA?

Define Data Model for JPA One to Many mapping – @Entity annotation indicates that the class is a persistent Java class. – @Table annotation provides the table that maps this entity. – @Id annotation is for the primary key. – @GeneratedValue annotation is used to define generation strategy for the primary key.

What is JPA one-to-one mapping?

The One-To-One mapping represents a single-valued association where an instance of one entity is associated with an instance of another entity. In this type of association one instance of source entity can be mapped atmost one instance of target entity.


1 Answers

Here we go again.

Every bidirectional association has two sides : the owner side, and the inverse side. The inverse side is the one which has the mappedBy attribute. The owner side is the other one. JPA/Hibernate only cares about the owner side. So if you just initialize the inverse side, the association won't be persisted.

It's a good practice, generally, to initialize both sides of the association. First because it makes sure the owner side is initialized, and second because it makes the graph of entities coherent, for your own good.

Also note that if you're working inside a transaction (and you should), all the entities returned by your queries are attached entities. The changes applied to the entities are automatically made persistent when the transaction is committed (or before). There is no need to save the entities explicitely like you're doing.

like image 116
JB Nizet Avatar answered Sep 23 '22 08:09

JB Nizet