Thursday, May 17, 2012

One-to-one relationship using EclipseLink

Best Practice in JPA series:
Part 1 – JPA Caching in EclipseLink
Part 2 – One-to-one relationship using EclipseLink

Here are some tips on how to implement high performance one to one relationship using EclipseLink 2.3.2.

Identify owning side (side with foreign key) and inverse side (side with mappedBy attribute in @OneToOne annotation). I will use Owning entity and Inverse entity as examples in following tips.

If you define private Inverse inverse; in Owning entity and you're satisfied with inverse_id as column name for foreign key, you don't need to specify @JoinColumn(name = "inverse_id") on it. However, you need to specify @OneToOne(cascade = CascadeType.ALL) on it, so that any operations on Inverse entity can be performed from Owning entity. You also need to specify @OneToOne(mappedBy = "inverse") on private Owning owning; in Inverse entity.

To avoid N + 1 select problem, specify @BatchFetch(BatchFetchType.EXISTS) on owning property in Inverse entity. You can also use @BatchFetch(BatchFetchType.IN) or @BatchFetch(BatchFetchType.JOIN). Following SQL statements will be used respectively for better performance.

SELECT t0.* FROM INVERSE t0 WHERE EXISTS (SELECT t1.ID FROM OWNING t1 WHERE (t0.ID = t1.INVERSE_ID))

SELECT * FROM INVERSE WHERE (ID IN (?,?))

SELECT t0.* FROM INVERSE t0, OWNING t1 WHERE (t0.ID = t1.INVERSE_ID)

If both Owning entity loads Inverse entity eagerly and Inverse entity loads Owning entity eagerly, following SQL statement will still be executed N times. Otherwise N + 1 problem is solved.

SELECT * FROM OWNING WHERE (INVERSE_ID = ?)

fetch = FetchType.LAZY can be set in @OneToOne annotation on owning side and / or inverse side. But it's better to use it on inverse side, because when owning object loads inverse object eagerly (FetchType.EAGER is default in one-to-one relation) by INVERSE_ID, the result can be used to populate inverse entity cache.

If you want lazy fetch take effect outside of a Java EE 5/6 application server, VM argument -javaagent:/home/jerry/.m2/repository/org/eclipse/persistence/eclipselink/2.3.2/eclipselink-2.3.2.jar needs to be set. Note that full absolute path is used here. See Using EclipseLink JPA Weaving for more details.