Reputation: 6459
I'm trying to figure out a cleaner way of saving a join table back to the database using only the entity id of the two joined tables. I can make my code work as is, but I feel like there should be a cleaner manner.
Imagine I have an entity which represents a simple sql join table, so something like this:
@Entity
@Table(name = "FOOBAR")
public class FooBar{
@ManyToOne(fetch = FetchType.Lazy)
@JoinColumn(name = "FOO_ID)
public Foo foo;
@ManyToOne(fetch = FetchType.Lazy)
@JoinColumn(name = "BAR_ID)
public Bar bar;
public FooBar(Foo foo, Bar bar){
this.foo=foo;
this.bar=bar;
}
}
I have a method which takes a Foo id and a Bar id, and wants to pass a FooBar object to my save method to save the join.
A naive implementation would query the database to find the Foo and Bar objects based off of their ids, pass those into the FooBar constructor, and then pass the new FooBar to the save method.
However, this is silly, the only things my join table needs is the foo and bar ids, not the full DB objects. I have everything I need already without hitting the DB to fetch Foo and Bar.
In fact I could simply construct a Foo object and bar object using their a public ID, ignoring all the other fields of the objects, create a FooBar object from these two 'empty' foo and bar objects, and pass that to save and it would work just fine without extra DB hits.
However, it somehow feels a little unclean to do this. I'm creating two 'dummy' objects this way. My foo and bar objects may have some nullable = false columns which my DB contract promises will never be null, so it feels wrong constructing a foo object that does have null fields for these columns; as if I'm breaking the contract for these objects. I don't want a constructor for either object that accepts only an ID because; I want to force them to be constructed in a manner that abides by my contract any time someone constructs them. I would prefer not to have to knowingly create objects that violate my promise.
I'm wondering if there is a way through JPA, or some quickly written abstraction I could do, to build my FooBar object using only the foo and bar ids in a way that is clean and communicative and does not violate any of the Foo/Bar constraints. After all since both Foo and Bar are lazy loaded when I first get a FooBar object all it really contains are the foo and bar ids until I call the getter; so I contain as much information already as a FooBar object from the DB would have.
can I create a FooBar object with only their ID and have that object tied to the DB, so it would lazy load the Foo and Bar objects if the getter were called just like a FooBar I get from the DB does?
Upvotes: 3
Views: 4214
Reputation: 88796
Having a class for a join table is overkill.
JPA has the ManyToMany
annotation, which creates the relationship for you.
You basically need to choose one of the entities to "own" the relationship. This is more or less which class defines the table being used for the join.
In this case, I'll choose Foo
as the one that owns the relationship.
In Foo
, you can do this:
@ManyToMany
@JoinTable(name="FOOBAR", joinColumns=@JoinColumn(name="FOO_ID"),
inverseJoinColumns=@JoinColumn(name="BAR_ID"))
public Set<Bar> bars;
and in Bar
:
@ManyToMany(mappedBy="bars")
public Set<Foo> foos;
Note, although I used Set
, you can use List
instead.
Note: ManyToMany
is Lazy by default... and I suggest leaving it that way.
Upvotes: 3
Reputation: 2724
The entityManager#getReference seems to be what you are looking for. It creates and returns an object that can be used wherever a reference to an entity is required and can (but in your case, will not have to) have all its state fetched lazily.
Upvotes: 1