I've had some time to dig into the Scala language and to evaluate its
implications for the jOOQ API. In particular, I like how Scala wraps
the Java Collections API making Java Lists, Maps, etc available
natively to the Scala language through a single import. Intuitively,
I'd say, jOOQ should provide a similar conversion feature allowing to
enhance the jOOQ API for Scala users. Here's what I'm talking about:
The test program can import the org.jooq.scala.Conversions utility to
enrich the jOOQ API with overloaded operators
-------------------------------------------------------------------
import java.sql.DriverManager
import org.jooq._
import org.jooq.impl._
import org.jooq.test.hsqldb.generatedclasses.Tables._
import collection.JavaConversions._
// This is all you have to import in addition to regular jOOQ types
import org.jooq.scala.Conversions._
object Test {
def main(args: Array[String]): Unit = {
val c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
val f = new Factory(c, SQLDialect.H2);
// Check out the neat syntax enhancements
for (val r <- f
select(
T_BOOK.ID * T_BOOK.AUTHOR_ID,
T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4,
T_BOOK.TITLE || " abc" || " xy"
)
from T_BOOK
where (T_BOOK.ID === 3)
fetch) {
println(r)
}
}
}
-------------------------------------------------------------------
Now, the below shows what I've hacked together to get some basic
operator overloading working. Essentially, I'm wrapping the
org.jooq.Field interface with a new org.jooq.scala.Field trait that
adds such operators. The implementation thereof is delegated to a
org.jooq.impl.CustomField, which already provides access to all of
jOOQ's internals.
-------------------------------------------------------------------
package org.jooq.scala
import org.jooq._
import org.jooq.impl._
import org.jooq.impl.Factory._
object Conversions {
import org.{ jooq => j }
import j. { impl => ji }
case class JFieldWrapper[T](val underlying : j.Field[T])
extends ji.CustomField[T] (underlying.getName(),
underlying.getDataType())
with Field[T] {
def toSQL(context : RenderContext) = underlying.toSQL(context)
def bind(context : BindContext) = underlying.bind(context)
def *(value : Number) = underlying.mul(value)
def *(value : Field[_ <: Number]) = underlying.mul(value)
def +(value : Number) = underlying.add(value)
def +(value : Field[_ <: Number]) = underlying.add(value)
def ||(value : String) = underlying.concat(value)
//def ||(value : Field[_]) : underlying.concat(value)
def ===(value : T) : Condition = underlying.equal(value)
def ===(value : Field[T]) : Condition = underlying.equal(value)
}
case class SFieldWrapper[T](underlying : Field[T])
extends ji.CustomField[T] (underlying.getName(),
underlying.getDataType()) {
def toSQL(context : RenderContext) = underlying.toSQL(context)
def bind(context : BindContext) = underlying.bind(context)
}
implicit def asScalaField[T](f : j.Field[T]): Field[T] = f match {
case JFieldWrapper(f) => f
case _ => new JFieldWrapper(f)
}
implicit def asJavaField[T](f : Field[T]): j.Field[T] = f match {
case JFieldWrapper(f) => f
case _ => new SFieldWrapper(f)
}
trait Field[T] extends QueryPartInternal {
def *(value : Number) : Field[T]
def *(value : Field[_ <: Number]) : Field[T]
def +(value : Number) : Field[T]
def +(value : Field[_ <: Number]) : Field[T]
def ||(value : String) : Field[String]
//def ||(value : Field[_]) : Field[String]
def ===(value : T) : Condition
def ===(value : Field[T]) : Condition
}
}
-------------------------------------------------------------------
The feedback I'm looking for is this:
- Is this going to respond to real user needs for those of you working
with Scala, or interested in getting involved with Scala?
- Is this quick hack "correct" in the way a Scala developer expects
things to happen?
- What should be added? Improved?
The discussion is open, contributions are welcome!