package scalanate import scala.xml._ import java.util.Date import org.hibernate._ import org.hibernate.cfg._ class Event extends Hibernate { var id: Long = 0L hibernate(<id name="id" column="EVENT_ID"> <generator class="native"/> </id>) var title: String = null hibernate(<property name="title"/>) var date: Date = null hibernate(<property name="date" type="timestamp" column="EVENT_DATE"/>) var participants: java.util.Set = new java.util.HashSet hibernate(<set name="participants" table="PERSON_EVENT" inverse="true"> <key column="EVENT_ID"/> <many-to-many column="PERSON_ID" class="scalanate.Person"/> </set>) override def toString = "Event " + id + " " + title + " " + date hibernateReady } class Person extends Hibernate { var id: Long = 0L hibernate(<id name="id" column="PERSON_ID"> <generator class="native"/> </id>) var age: Int = 0 hibernate(<property name="age"/>) var name: String = null hibernate(<property name="name"/>) var events: java.util.Set = new java.util.HashSet hibernate(<set name="events" table="PERSON_EVENT"> <key column="PERSON_ID"/> <many-to-many column="EVENT_ID" class="scalanate.Event"/> </set>) override def toString = "Person " + id + " " + name + " " + age hibernateReady }
The magic is in the repeated calls to hibernate() and hibernateReady(), which together capture the XML mapping information that Hibernate needs. The fragments of XML should be familiar to anyone who's had to sling around a hibernate X.hbm.xml mapping file. Now, we can crank up a program and do some entity creation, saving, and querying...
object Main { def main(args: Array[String]) { println("main: " + args.mkString(",")); // Use throwaway instances to force Hibernate to // configure these classes. // new Person new Event Hibernate.config = <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class">org.h2.Driver</property> <property name="connection.url">jdbc:h2:mem:test_mem</property> <property name="connection.username">admin</property> <property name="connection.password"></property> <property name="dialect">org.hibernate.dialect.H2Dialect</property> <property name="current_session_context_class">thread</property> <!-- Emit generated sql for debugging --> <property name="show_sql">true</property> <!-- Auto-drop/recreate schema on startup --> <property name="hbm2ddl.auto">create</property> </session-factory> </hibernate-configuration> args(0) match { case "test0" => test0 case "test1" => test1 } } import Hibernate.withTxSession def test0 = withTxSession(session => { val e1 = new Event e1.title = "hi" e1.date = new Date session.save(e1) }) def test1 = { withTxSession(session => { val e1 = new Event e1.title = "hi" e1.date = new Date session.save(e1) val e2 = new Event e2.title = "bye" e2.date = new Date session.save(e2) val p1 = new Person p1.name = "p1" p1.age = 21 session.save(p1) }) println("test1.a") import scala.collection.jcl.MutableIterator.Wrapper withTxSession(session => { val p1 = session.createQuery("from Person").list.get(0).asInstanceOf[Person] println(p1) for (e <- new Wrapper[Event](session.createQuery("from Event").list.iterator)) { p1.events.add(e) e.participants.add(p1) } }) println("test1.b") withTxSession(session => { val p1 = session.createQuery("from Person").list.get(0).asInstanceOf[Person] println(p1) println(p1.events) for (e <- new Wrapper[Event](session.createQuery("from Event").list.iterator)) { println(e) println(e.participants) } }) println("test1.c") } }
And, to make the above work, you'll need these utility helpers.
object Hibernate { lazy val sessionFactory = configuration.buildSessionFactory lazy val configuration = if (config != null) (new Configuration).configure(configDoc).addDocument(mappingDoc) else (new Configuration).configure.addDocument(mappingDoc) var config: Node = null // App can to init this, otherwise, uses hibernate.cfg.xml. def configDoc = parseDoc(config) def mappingDoc = parseDoc(<hibernate-mapping default-access="field"> {ready.keys.map( cls => { val attrs = ready.getOrElse(cls, Map.empty) <class name={cls.getName} table={attrs.getOrElse("table", tableName(cls.getName))}> {mappings.getOrElse(cls, Text(""))} </class> } ) } </hibernate-mapping>) def tableName(className: String) = className.substring(className.lastIndexOf('.') + 1) def withTxSession(f: (Session) => Any) = { val s = sessionFactory.getCurrentSession val t = s.beginTransaction val r = f(s) t.commit r } // --------------------------------- val mappings = new scala.collection.mutable.HashMap[Class, NodeSeq] val ready = new scala.collection.mutable.HashMap[Class, Map[String, String]] def mappingAppend(cls: Class, x: => NodeSeq): Unit = if (!ready.contains(cls)) mappings += (cls -> NodeSeq.fromSeq(mappings.getOrElse(cls, Text("\n")) ++ x)) def mappingReady(cls: Class, m: => Map[String, String]): Unit = if (!ready.contains(cls)) ready += (cls -> m) // --------------------------------- lazy val parseDocFactory = javax.xml.parsers.DocumentBuilderFactory.newInstance def parseDoc(x: Node) = { import org.xml.sax.InputSource import java.io.StringReader parseDocFactory.newDocumentBuilder.parse(new InputSource(new StringReader(x.toString))) } } // --------------------------------- trait Hibernate { def hibernate(x: => NodeSeq): Unit = Hibernate.mappingAppend(this.getClass, x) def hibernateReady: Unit = Hibernate.mappingReady(this.getClass, Map.empty) def hibernateReady(m: => Map[String, String]): Unit = Hibernate.mappingReady(this.getClass, m) }
Above, you can see the Hibernate helper singleton object and companion Hibernate trait are awfully short. In all, I think, quite an interesting exercise in the DSL possibilities of Scala. Tags:
1 Comment
Add a Comment
© 2009 Created by Steve Yen on Ning. Create Your Own Social Network
You need to be a member of My Scala Base to add comments!
Join this social network