To start with, accessing JPA entities from Scala is really no different that accessing them from Java. In either case you're getting a JPA EntityManager interface from somewhere (JNDI in my case) and using the persist, merge, etc. methods to control object persistence management. There are, however, a few things you can do with Scala to make it a little simpler to use and more Scala-ish.
The first issue I came to was that all of my JPA entities use Java collections for their collection interfaces. In my case they're all java.util.Sets, but it's nicer to have a Scala set to get access to all of the nice methods like exists, toList, etc. For that, I start out with an implicit definition:
implicit def utilSetToSet[A] (s : java.util.Set[A]) =
new SetWrapper[A] { override def underlying = s}
That way, when I access a set member of a given entity, I can automatically use it as a Scala set.
The next thing I've added to make things a little easier is to create a "using" method to automatically handle closing the entity manager and handling any exceptions thrown:
def usingEM[A](f : EntityManager => Can[A]) : Can[A] = {
val em = emf.createEntityManager()
try {
f(em)
} catch {
case e: Exception => Failure(e.getMessage(), Full(e), Nil)
} finally {
em.close()
}
}
I've been vacillating on this particular method, since in the past I usually have used a filter to open an EntityManager and then close it at the end. Doing it this way allows me to better handle errors via Scala's nice pattern matching, but it also makes the code more "baroque":
object entityName extends RequestVar[Can[String]](Empty)
def view (xhtml : NodeSeq) : NodeSeq = usingEM({em =>
val myEntity = em.createNamedQuery("findEntityByName")
.setParamater("name", entityName.is openOr "non-existant")
.getSingleResult()
Full(bind("me", xhtml, // blah blah))
}) match {
case Failure(_, e : NoResultException, _) => S.error("Could not find entity by name")
case Failure(msg, exception, _) => log(msg, exception); S.error("Error retrieving entity"); Text("")
case Full(x) => x
}
I'm not convinced that this is the right way to do this so I'm open to suggestions.
Derek