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.

Saturday, April 14, 2012

Wildcard Host doesn't work in MySQL 5.5.22

I haven't used MySQL for a while. After reinstalled my laptop with Ubuntu 12.04 Beta 2, I decided to use MySQL instead of PostgreSQL to continue my JPA study.

sudo apt-get install mysql-server

I used MySQL Administrator long time ago and now it's replaced with powerful MySQL Workbench. If you have difficulties in installing it, please follow this tutorial.

Everything seems fine, just like when I developed myTunes 6 years ago, except that % for any host doesn't work any more. So if you get

ERROR 1045 (28000): Access denied for user 'user'@'localhost' (using password: YES)

Update Host column from % to localhost for your user account and restart MySQL will solve it.

Update: you can also remove anonymous accounts by executing delete from user where user=''; (Thank you, Dan)