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!

Reply via email to