Question

I'm starting on a Scala application which uses Hibernate (JPA) on the back end. In order to load an object, I use this line of code:

val addr = s.load(classOf[Address], addr_id).asInstanceOf[Address];

Needless to say, that's a little painful. I wrote a helper class which looks like this:

import org.hibernate.Session

class DataLoader(s: Session) {
  def loadAddress(id: Long): Address = {
    return s.load(classOf[Address], id).asInstanceOf[Address];
  }
  ...
}

So, now I can do this:

val dl = new DataLoader(s)
val addr = dl loadAddress(addr_id)

Here's the question: How do I write a generic parametrized method which can load any object using this same pattern? i.e

val addr = dl load[Address](addr_id)

(or something along those lines.)

I'm new to Scala so please forgive anything here that's especially hideous.

Was it helpful?

Solution

Here it is:

import org.hibernate.Session
class DataLoader(s: Session) {
  def load[A](id: Long)(implicit m: Manifest[A]): A = {
    return s.load(m.erasure, id).asInstanceOf[A];
  }
}

EDIT -- Or, to ensure that any casting error -- as a result of hibernate returning the wrong object -- will happen inside load, like this:

import org.hibernate.Session
class DataLoader(s: Session) {
  def load[A](id: Long)(implicit m: Manifest[A]): A = {
    return m.erasure.asInstanceOf[Class[A]].cast(s.load(m.erasure, id));
  }
}

On Scala 2.8 you can also write it like this:

import org.hibernate.Session
class DataLoader(s: Session) {
  def load[A : Manifest](id: Long): A = {
    return s.load(manifest[A].erasure, id).asInstanceOf[A];
  }
}

You can combine it with an implicit session as well, as suggested by Chris:

def load[A](id: Long)(implicit m: Manifest[A], s: org.hibernate.Session): A = {
  return s.load(m.erasure, id).asInstanceOf[A];
}

Note that you can't combine context view notation (A : Manifest) with additional implicit parameters.

OTHER TIPS

One method would be to do take advantage of the java.lang.Class.cast method in order to do something like:

def load[A](clazz: Class[A], id: Long)(implicit s: Session) : A 
       = clazz.cast(s.load(clazz, id))

Then the usage is as follows:

implicit val s = ...//get hibernate session
val addr = load(classOf[Address], 1)

It's not hugely different from what you have already but in order to access the class instance, you need to pass it in.

I'm pretty confident that you cannot safely do what you want with Manifests because they cannot supply the parametrized Class[Address] at compile-time which you need in order for the cast to work (they can only supply the erasure of Class[_]). I don't see any other mechanism for doing a cast from a Manifest

You can of course use asInstanceOf[A] instead of cast but this gets erased at compile-time to isInstanceOf[Object] and is therefore useless in terms of compile-time type checking (and hence unadvisable).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top