Jooq for scala, how to create Converter for Option[Long]

80 Views Asked by At

I guess there is some issue with scala Long and java primitive long

class JooqOptionalLongConverter extends Converter[Long, Option[Long]] {
  override def from(databaseObject: Long): Option[Long] =
    Option(databaseObject)

  override def to(userObject: Option[Long]): Long = userObject match {
    case None       => null //  an expression of type Null is ineligible for implicit conversion
    case None       => null
    case Some(long) => long
  }

  override def fromType(): Class[Long] = classOf[Long]

  override def toType: Class[Option[Long]] = classOf[Option[Long]]
}

It doesn't compile with exception

 an expression of type Null is ineligible for implicit conversion
    case None       => null

Same approach for Option[String] works fine, but String is object...

this doesn't compile too

  private val optionalLongType = org.jooq.impl.SQLDataType.BIGINT.asConvertedDataType(new JooqOptionalLongConverter)

with message

overloaded method value asConvertedDataType with alternatives:
  [U](x$1: org.jooq.Binding[_ >: Long, U])org.jooq.DataType[U] <and>
  [U](x$1: org.jooq.Converter[_ >: Long, U])org.jooq.DataType[U]
 cannot be applied to ...

Are there any workarounds?

1

There are 1 best solutions below

0
Gastón Schabas On

Short answer

As it was mentioned in the comments, replacing scala.Long used as the first type parameter in Converter for java.lang.Long should be enough.

class JooqOptionalLongConverter extends Converter[java.lang.Long, Option[Long]] {
  override def from(databaseObject: java.lang.Long): Option[Long] =
    Option(databaseObject)

  override def to(userObject: Option[Long]): java.lang.Long = userObject match {
    case None       => null
    case Some(long) => long
  }

  override def fromType(): Class[java.lang.Long] = classOf[java.lang.Long]

  override def toType: Class[Option[Long]] = classOf[Option[Long]]
}

It could be easy to get confused which Long belongs to each package. Scala let you rename a class when you are importing. Doing something like this, could help you to make it more readbale (this is a personal opinion).

import java.lang.{Long => JavaLong}

class JooqOptionalLongConverter extends Converter[JavaLong, Option[Long]] {
  // ... same for the other places where `java.lang.Long` is used
}

Detailed answer

I haven't worked with Jooq. Based on the docs where it give details about how Data type conversion works, I guess you are trying to convert a numeric nullable column from a table to an Option[Number] in scala. There is an open issue with the title Add support for Scala Option or Java Optional types.

That being said, the error shown by the compiler (error: an expression of type Null is ineligible for implicit conversion), is because how the Type System work in scala. There is a difference between what is a value class and what is a class

The following diagram shows how the type hierarchies is designed.

enter image description here

We are going to focus in a couple of them:

  • Any: is the root of the Scala class hierarchy. Every class in a Scala execution environment inherits directly or indirectly from this class.

  • AnyVal: is the root class of all value types, which describe values not implemented as objects in the underlying host system. Value classes are specified in Scala Language Specification.

  • AnyRef: is the root class of all reference types. All types except the value types descend from this class.

  • Null: is the type of the null literal. It is a subtype of every type except those of value classes. Value classes are subclasses of AnyVal, which includes primitive types such as Int, Boolean, and user-defined value classes.


As you can see, from the root you have a clear division between values (AnyVal) and references (AnyRef, which is analogue to java.lang.Object in Java). If you look at Null, you will see that it is a subtype of AnyRef but not a subtype of AnyVal.

So, when you are trying to do

  override def to(userObject: Option[Long]): Long = userObject match {
    case None       => null //  an expression of type Null is ineligible for implicit conversion
    case Some(long) => long
  }

It's the same to do

val double: Double = null
val   long: Long   = null
val    int: Int    = null
val  short: Short  = null

All of them will give you the same compilation error due to Null is not a subtype of AnyVal.