[#1773] Add a new jOOQ-Scala project and jooq-scala artefactId, to

contain jOOQ extensions in the Scala language - Added more operators
This commit is contained in:
Lukas Eder 2012-09-01 10:49:01 +02:00
parent 4f5593d8b4
commit cd8894e5d1
6 changed files with 293 additions and 45 deletions

View File

@ -154,6 +154,13 @@
<type>jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_2.9.0</artifactId>
<version>1.8</version>
<type>jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,59 +1,201 @@
/**
* Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.jooq.scala
import org.jooq._
import org.jooq.impl._
import org.jooq.impl.Factory._
/**
* jOOQ type conversions used to enhance the jOOQ Java API with Scala Traits
* <p>
* Import this object and all of its attributes to profit from an enhanced jOOQ
* API in Scala client code. Here is an example:
* <code><pre>
* import java.sql.DriverManager
* import org.jooq._
* import org.jooq.impl._
* import org.jooq.scala.example.h2.Tables._
* import collection.JavaConversions._
* 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);
*
* 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)
* }
* }
* }
* </pre></code>
*
* @author Lukas Eder
*/
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] {
/**
* A Scala-esque representation of {@link org.jooq.Field}, implementing
* overloaded operators for common jOOQ operations
*/
case class JFieldWrapper[T](val underlying: Field[T])
extends CustomField[T] (underlying.getName(), underlying.getDataType())
with SField[T] {
// ------------------------------------------------------------------------
// QueryPart API
// ------------------------------------------------------------------------
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 bind (context : BindContext) = underlying.bind(context)
// ------------------------------------------------------------------------
// Arithmetic operations
// ------------------------------------------------------------------------
def +(value : Number) = underlying.add(value)
def +(value : Field[_ <: Number]) = underlying.add(value)
def ||(value : String) = underlying.concat(value)
def -(value : Number) = underlying.sub(value)
def -(value : Field[_ <: Number]) = underlying.sub(value)
def *(value : Number) = underlying.mul(value)
def *(value : Field[_ <: Number]) = underlying.mul(value)
def /(value : Number) = underlying.div(value)
def /(value : Field[_ <: Number]) = underlying.div(value)
def %(value : Number) = underlying.mod(value)
def %(value : Field[_ <: Number]) = underlying.mod(value)
def ||(value : String) = underlying.concat(value)
//def ||(value : Field[_]) : underlying.concat(value)
def ===(value : T) : Condition = underlying.equal(value)
// ------------------------------------------------------------------------
// Comparison predicates
// ------------------------------------------------------------------------
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)
def !==(value : T) : Condition = underlying.notEqual(value)
def !==(value : Field[T]) : Condition = underlying.notEqual(value)
def <>(value : T) : Condition = underlying.notEqual(value)
def <>(value : Field[T]) : Condition = underlying.notEqual(value)
def >(value : T) : Condition = underlying.greaterThan(value)
def >(value : Field[T]) : Condition = underlying.greaterThan(value)
def >=(value : T) : Condition = underlying.greaterOrEqual(value)
def >=(value : Field[T]) : Condition = underlying.greaterOrEqual(value)
def <(value : T) : Condition = underlying.lessThan(value)
def <(value : Field[T]) : Condition = underlying.lessThan(value)
def <=(value : T) : Condition = underlying.lessOrEqual(value)
def <=(value : Field[T]) : Condition = underlying.lessOrEqual(value)
}
implicit def asScalaField[T](f : j.Field[T]): Field[T] = f match {
/**
* A Scala-esque representation of {@link org.jooq.Field}, adding overloaded
* operators for common jOOQ operations
*/
trait SField[T] extends QueryPartInternal {
// ------------------------------------------------------------------------
// Arithmetic operations
// ------------------------------------------------------------------------
def +(value : Number) : Field[T]
def +(value : Field[_ <: Number]) : Field[T]
def -(value : Number) : Field[T]
def -(value : Field[_ <: Number]) : Field[T]
def *(value : Number) : Field[T]
def *(value : Field[_ <: Number]) : Field[T]
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]
// ------------------------------------------------------------------------
// Comparison predicates
// ------------------------------------------------------------------------
def ===(value : T) : Condition
def ===(value : Field[T]) : Condition
def !==(value : T) : Condition
def !==(value : Field[T]) : Condition
def <>(value : T) : Condition
def <>(value : Field[T]) : Condition
def >(value : T) : Condition
def >(value : Field[T]) : Condition
def >=(value : T) : Condition
def >=(value : Field[T]) : Condition
def <(value : T) : Condition
def <(value : Field[T]) : Condition
def <=(value : T) : Condition
def <=(value : Field[T]) : Condition
}
implicit def asScalaField[T](f : Field[T]): SField[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
}
}

View File

@ -1,8 +1,46 @@
/**
* Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com
* All rights reserved.
*
* This software is licensed to you under the Apache License, Version 2.0
* (the "License"); You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* . Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* . Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* . Neither the name "jOOQ" nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import collection.JavaConversions._
import java.sql.DriverManager
import org.jooq._
import org.jooq.impl._
import org.jooq.scala.example.h2.Tables._
import collection.JavaConversions._
import org.jooq.scala.Conversions._
object Test {
@ -10,14 +48,13 @@ object Test {
val c = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
val f = new Factory(c, SQLDialect.H2);
for (
val r <- f
for (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
where (T_BOOK.ID <> 3) fetch
) {
println(r)

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<settings>
<renderSchema>false</renderSchema>
<renderNameStyle>LOWER</renderNameStyle>
<renderKeywordStyle>UPPER</renderKeywordStyle>
<renderFormatted>false</renderFormatted>
<statementType>PREPARED_STATEMENT</statementType>
<executeLogging>true</executeLogging>
<executeWithOptimisticLocking>false</executeWithOptimisticLocking>
<attachRecords>true</attachRecords>
</settings>

View File

@ -0,0 +1,45 @@
package org.jooq.scala.test
import collection.JavaConversions._
import org.scalatest.FunSuite
import org.jooq._
import org.jooq.impl._
import org.jooq.scala.example.h2.Tables._
import org.jooq.scala.Conversions._
import org.jooq.conf.Settings
import javax.xml.bind.JAXB
import org.jooq.conf.SettingsTools
class OperatorOverloadingTest extends FunSuite {
test("arithmetic") {
val add1 = T_BOOK.ID + T_BOOK.AUTHOR_ID
val add2 = T_BOOK.ID + 2
val sub1 = T_BOOK.ID - T_BOOK.AUTHOR_ID
val sub2 = T_BOOK.ID - 2
val mul1 = T_BOOK.ID * T_BOOK.AUTHOR_ID
val mul2 = T_BOOK.ID * 2
val div1 = T_BOOK.ID / T_BOOK.AUTHOR_ID
val div2 = T_BOOK.ID / 2
val mod1 = T_BOOK.ID % T_BOOK.AUTHOR_ID
val mod2 = T_BOOK.ID % 2
assert("(t_book.id + t_book.author_id)" == add1.toString(), add1.toString())
assert("(t_book.id + 2)" == add2.toString(), add2.toString())
assert("(t_book.id - t_book.author_id)" == sub1.toString(), sub1.toString())
assert("(t_book.id - 2)" == sub2.toString(), sub2.toString())
assert("(t_book.id * t_book.author_id)" == mul1.toString(), mul1.toString())
assert("(t_book.id * 2)" == mul2.toString(), mul2.toString())
assert("(t_book.id / t_book.author_id)" == div1.toString(), div1.toString())
assert("(t_book.id / 2)" == div2.toString(), div2.toString())
assert("mod(t_book.id, t_book.author_id)" == mod1.toString(), mod1.toString())
assert("mod(t_book.id, 2)" == mod2.toString(), mod2.toString())
// Check for the correct application of operator precedence
val combined1 = T_BOOK.ID + T_BOOK.AUTHOR_ID * 2
val combined2 = T_BOOK.ID * T_BOOK.AUTHOR_ID + 2
assert("(t_book.id + (t_book.author_id * 2))" == combined1.toString(), combined1.toString())
assert("((t_book.id * t_book.author_id) + 2)" == combined2.toString(), combined2.toString())
}
}

View File

@ -119,6 +119,7 @@
<!--<module>jOOQ-codegen-maven-example</module>-->
<module>jOOQ-console</module>
<module>jOOQ-meta</module>
<module>jOOQ-scala</module>
</modules>
<dependencyManagement>
@ -153,6 +154,11 @@
<artifactId>jooq-meta</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-scala</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>