I’m almost done developing a new version of an old webapp in Scala, using Wicket and JPA. Overall my productivity has increased with Scala once I got over the learning hump – I am loathed to go back to Java now, even though Scala IDE support is rudimentary at best (nothing “mvn scala:cc” can’t fix though).
However, I’ve found a couple of major gotchas when mixing JPA and Scala, which are worth pointing out:
Scala does not support nested annotations
Some things in JPA rely on nested annotations for full configurability, most notably Many-to-Many relationship mapping when mapping join-tables and join-columns. This does not stop you from using @ManyToMany, but it forces you into a certain direction, the workaround for this is to do something like the following:
@ManyToMany{val mappedBy=”users”}
@BeanProperty
var projects: java.util.List[Projects] = new java.util.Vector[Projects]
—-
Other side of relationship:
@ManyToMany{val targetEntity = classOf[User]}
@BeanProperty
var users: java.util.List[User] = new java.util.Vector[User]
(yes, I’m aware Vector is an antiquated class, but, it worked after much pain).
However, if you are mapping onto a legacy data-model where you do not have the luxury of letting JPA drive your data model definition, you are out of luck until Scala 2.8 comes out with support for nested annotations.
Scala Enumerations are NOT Java enums
I happily created Scala enumerations assuming they got mapped to Java enums when compiled. Big mistake when I started seeing DataTruncationError’s from MySQL once I started the app, for what I assumed would have been mapped into a simple tinyint field with a numeric value between 0-4.
Scala enumerations are not Java enums, never will be, so you won’t be able to rely on JPA’s or Hibernates enum support.
There are two ways around this issue: one is using Hibernate’s UserType user-defined customizable data types.
This seemed like a lot of work to me, so I went down an easier route instead:
I simply made the enum var’s into function definitions and created a new var field of an Int type, then made the enum functions map to and from that field to the correct enumeration value.
Some JPA aspects loose type-safety in Scala
In Java, you can do both of the following and get the correct type without any hassle:
List[User] users = entityManager.createQuery(“FROM User”).getResultList();
User user = entityManager.find(User.class, “someusername”);
This is one of the few cases where Scala actually ends up being a bit more verbose than Java and Scala’s type inference falls somewhat flat: I though you would be able to do something akin to:
var users: List[User] =entityManager.createQuery(“FROM User”).getResultList
Nope, doesn’t work, compilation error. Basically, from my (still somewhat limited) understanding of Scala, you will have to rely on the .asInstanceOf[SomeType] cast-function on each object as you iterate through the collection.
The last issue is a minor one in my opinion, but the two issues I mentioned first stole quite a few hours from me, so hopefully me writing this post might save someone a little frustration and hair-tearing.
August 10, 2009 at 12:59 am
Nested annotations will be supported in Scala 2.8.
You can still write java enums in java, mixed compilation is supported since 2.7.2
August 10, 2009 at 1:35 pm
import java.util.List
import javax.persistence.Query
// pimp it
class RichQuery(query : Query) {
def getTypedList[T] = query.getResultList.asInstanceOf[List[T]]
}
object RichQuery {
implicit def richQuery(q : Query) = new RichQuery(q)
}
// and use it
import RichQuery._
val q : Query = …
val l = q.getTypedList[User]