0

Let's say 2 entities are given: master and dependant.

They are defined in DB usually like dependants.master_id -> masters.id, i.e. the dependant entity holds the reference to the main entity.

In JPA one2one BiDirectional association in this case usually looks like:

class Master {
    @OneToOne(mappedBy="master")
    Dependant dependant
}
class Dependant {
    @OneToOne
    @JoinColumn("master_id")
    Master master
}

And this approach causes the necessity to deal with both sides of relation like:

Master master = new Master();
Dependant dependant = new Dependant();
dependant.setMaster(master);
master.setDependant(dependant);
repository.save(master);

instead of more intuitive and closer to business logic one like:

Master master = new Master();
Dependant dependant = new Dependant();
master.setDependant(dependant);
repository.save(master);

Are there any common workarounds for this? I mean I don't want to mess with supporting the association from the dependant side.

fyrkov
  • 2,245
  • 16
  • 41
  • Might be the title is not very good and clear. Any suggestions for the title would be nice – fyrkov Mar 04 '19 at 17:04
  • Possible duplicate of [JPA @JoinColumn annotation with One To One relationship](https://stackoverflow.com/questions/38054178/jpa-joincolumn-annotation-with-one-to-one-relationship) – billdoor Mar 04 '19 at 17:11

2 Answers2

2

One workaround might be using @PrePersist. For both entities you could implement methods like:

Master

@PrePersist
private void prePersist() {
    if(null == getDependant().getMaster()) {
        getDependant().setMaster(this);
    }
}

Dependant

@PrePersist
private void prePersist() {
    if(null == getMaster().getDependant()) {
        getMaster().setDependant(this);
    }
}

Or maybe just omitting the null checks.

pirho
  • 11,565
  • 12
  • 43
  • 70
  • Isn't it the case that `@PrePersist` hooks are invoked only in conjunction with the "persist" lifecycle event of an entity object? That would serve the OP's specific example case, but not various others that they might routinely encounter. – John Bollinger Mar 04 '19 at 17:32
  • @JohnBollinger You might be right. Depends on what OP needs. Could also implement getters/setters so that this stuff is handled already then. – pirho Mar 04 '19 at 17:35
  • Yes, I was thinking about something along those lines. – John Bollinger Mar 04 '19 at 17:40
1

You have multiple options, but all depend on setting the relationship correctly on the owning side, which in your case is Dependant. The best choice for you depends on your needs and usage pattern. For example, another answer discusses defining a @PrePersist method, which can be pretty clean if the association between Master and Dependent is established when and only when masters are first persisted.

Given that you are using field-level access, you could also consider making Master.setDependant() do the work for you:

class Master {
    // ...

    @OneToOne(mappedBy="master")
    private Dependant dependant;

    public void setDependant(Dependant dep) {
        if (dep != null) {
            dep.setMaster(this);
        } else if (dependant != null) {
            // leaves a dangling dependant ...
            dependant.setMaster(null);
        }
        dependant = dep;
    }

    // ...
}

That will ensure that when you assign a Dependant to a Master, the reciprocal relationship is automatically set up on the Dependant side. You will then want to ensure that persistence operations are specified to cascade correctly from Master to Dependant.

Note, however, that this is no silver bullet. It can easily yet surprisingly fail if you assign a detached or removed Dependant to a Master. It will likely also fail if you try to replace a Master's Dependant without explicitly removing the original one, if any. There are other ways to break it, too.

Inasmuch as managing the relationship in all but very simple ways requires care and attention to detail, I'd suggest that you bite the bullet and do it manually, everywhere, if indeed you need anything more than creating the relationship when you persist a new Master and traversing existing relationships.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157