| Apache OpenJPA 1.2.0 | EclipseLink 1.1.0 | Hibernate 3.4.0.GA | |
|---|---|---|---|
| Single Table | FAIL | OK | OK |
| One-To-Many | FAIL | OK | OK |
| Many-To-Many | FAIL | OK | OK |
| Composite PK | FAIL | OK | FAIL |
Why or Why Not to Use Scala for JPA Entites
Writing JPA entities in scala has some advantages and some disadvantages. The major disadvantage at the time is that scala up to versions 2.7.x does not support nested annotations, so you cannot use any of the JPA annotations that represent collections of related annotations (like@JoinColumns or
@NamedQueries) or have a complex structure like
@SqlResultSetMapping.
But you can get quite far by designing you entity classes in a way
that will give you sensible table definitions with the default naming
conventions.
One of the main advantages -- apart from the general fact that scala
code can be very succinct -- becomes visible if you have to deal with
composite primary keys.
Using composite primary keys can be quite tedious in java, because you
have to implement an @IdClass for the key that is
serializable and overrides equals and
hashCode.
In java this amounts to approximately a whole page of boilerplate code
for every composite key.
But the astute reader might have noticed that the requirements for an
@IdClass sound awfully similar to the properties of a
scala case class, and indeed, they are.
In scala, all this boilerplate boils down to
Of course, if you have many composite keys, you will also most
probably have foreign key relationships on them for which you will
want to use @JoinColumns, so scala is a mixed blessing
here. But since scala 2.7 and up supports mixed language projects, you
can still write most of your code including all the
@IdClasses in scala and fall back to java only for those
entities that require nested annotations while you wait for
Lukas Rytz to fix named arguments,
or define the things you cannot express with annotations in an
orm.xml file.
Some useful tips on how to deal with this are to be
found on the
Lift JPA page.
The Tests
For this comparison I used four simple scenarios that I tested with the latest stable version of every JPA implementation I could find. The first test persists a single entity into a single table as a test of basic functionality: The second test adds a milestone entity and a simple on-to-many relationship from projects to milestones. The code added to the project entity class is (thetargetEntity is only
necessary to work around a bug in 2.7 that will be fixed in 2.8):
The third test gets rid of the milestones again and instead adds a
many-to-many relationship between projects and users. Both classes get
an attribute like this:
The fourth test models this many-to-many relationship explicitly, so
that we can add additional attributes like the role a user has within
a projekt to it. The changes to the user and project entity class are
trivial, instead of a many-to-many relationship to the other entity,
both have a one-to-many relationship to the role entity:
The interesting things happen in the role entity class, which has a
composite primary key as described above consisting of the user and
project primary key, as well as many-to-one relationships to both entities.
The trick here is to make the primary key fields immutable, otherwise
JPA will generate double mappings for the PK component and the
many-to-one relationship that represent the same relationship to the
other entity.
The Candidates
I found five free JPA implementions (thanks topersistence.xml.
The other three allow the deployment of unmodified classes, so I
restricted my tests to these.
The Results
The way the persistence
annotations
end up in the class files produced by the scala compiler seems to confuse
OpenJPA.
Technically there is nothing wrong with the way OpenJPA behaves here,
the
behaviour of JPA in the presence of confusing
annotations is undefined and the problems are stricly scala problems.
But still, all I ever got out of OpenJPA in any of my tests were exceptions like
Eclipselink and Hibernate performed the first three test without any
problems. The first difference appeared in
the test with the composite primary key. Eclipselink mastered this
test without problems. Hibernate first complained about the
@IdClass, unlike eclipselink it insists on a default
constructor for this class too, like for a proper entity class.
This is easily remedied, just change it to:
But with this change the real problems just start. You get a long
trace of nested exceptions, with the root cause beeing
This happens probably when trying to add the sixth parameter to this
four parameter query, which is the last one hibernate logs:
The Code
Here is a tar-archive with the code. To use it, you must download the JPA implementations, install scalatest and fix the paths in theant.properties file.
History
The first version contained@BeanProperty annotations on
all fields, which are nice in general, but unnecessary for JPA.