|Apache OpenJPA 1.2.0||EclipseLink 1.1.0||Hibernate 3.4.0.GA|
Why or Why Not to Use Scala for JPA EntitesWriting 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
@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
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
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
Some useful tips on how to deal with this are to be found on the Lift JPA page.
The TestsFor 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 (the
targetEntityis 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 CandidatesI found five free JPA implementions (thanks to hints from the mailing lists): the DataNucleus bytecode enhancer has problems with scalac generated bytecode.) Ebean seems to require programmaic configuration, so I could not test it by just switching out the
persistence.xml. The other three allow the deployment of unmodified classes, so I restricted my tests to these.
The way the persistence annotations
end up in the class files produced by the scala compiler seems to confuse
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 CodeHere is a tar-archive with the code. To use it, you must download the JPA implementations, install scalatest and fix the paths in the
HistoryThe first version contained
@BeanPropertyannotations on all fields, which are nice in general, but unnecessary for JPA.