Jooq: "Error while looking up Scala enum" from DSL.mostSpecific

Created on 3 Jan 2017  路  26Comments  路  Source: jOOQ/jOOQ

ScalaGenerator generates enum values using Scala case objects, which have their own subclass of the sealed trait that extends org.jooq.EnumType:

object MyEnum {

  val A : MyEnum = screener.mysql.jooq.enums.A
  val B : MyEnum = screener.mysql.jooq.enums.B

  def values : Array[MyEnum] = Array(
      A
    , B
  )

  def valueOf(s : String) : MyEnum = s match {
    case "A" => A
    case "B" => A
    case _ => throw new IllegalArgumentException()
  }
}

sealed trait MyEnum extends EnumType {
  override def getCatalog : Catalog = null
  override def getSchema : Schema = null
  override def getName : String = "..."
}

case object A extends MyEnum {
  override def getLiteral : String = "A"
}

case object B extends MyEnum {
  override def getLiteral : String = "B"
}

When attempting to insert a record containing a field with any enum value (i.e. MyEnum.A or MyEnum.B), then DSL.mostSpecific throws an exception like:

! java.lang.ClassNotFoundException: screener.mysql.jooq.enums.A$$
! at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_112]
! at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_112]
! at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) ~[na:1.8.0_112]
! at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_112]
! at org.jooq.impl.Tools.enums(Tools.java:3322) ~[jooq-3.9.0.jar:na]
! ... 81 common frames omitted
! Causing: org.jooq.exception.MappingException: Error while looking up Scala enum
! at org.jooq.impl.Tools.enums(Tools.java:3328) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.DefaultDataType.asEnumDataType(DefaultDataType.java:678) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:800) ~[jooq-3.9.0.jar:na]
! ... 79 common frames omitted
! Causing: org.jooq.exception.MappingException: Cannot create instance of class screener.mysql.jooq.enums.A$
! at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:804) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.DSL.mostSpecific(DSL.java:16875) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.DSL.val(DSL.java:16843) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.DSL.val(DSL.java:16811) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.Tools.field(Tools.java:1082) ~[jooq-3.9.0.jar:na]
! at org.jooq.impl.TableRecordImpl.addValue(TableRecordImpl.java:297) ~[jooq-3.9.0.jar:na]
...

This error appears to stem from either:

  1. a flaw in DSL.mostSpecific, which assumes that enum values have the same Class as the corresponding DataType.getType(), and therefore invokes DefaultDataType.getDataType with the enum value's Class:
            Class valueType = value.getClass();
            Class coercionType = dataType.getType();
            if(valueType != coercionType && coercionType.isAssignableFrom(valueType)) {
                return DefaultDataType.getDataType((SQLDialect)null, valueType, dataType);
            }
  1. or a deficiency in the Tools.enums method, which assumes that the type it will receive is the Class corresponding to the sealed trait, and therefore incorrectly assumes that it can load the companion object via Java reflection by appending "$":
            Class e = Thread.currentThread().getContextClassLoader().loadClass(type.getName() + "$");

This error was reproduced on 3.9.0, but there is some evidence that it has existed for some time: http://stackoverflow.com/questions/37464351/insert-scala-enum-through-jooq

Functionality C Scala All Editions Medium Wontfix Defect

Most helpful comment

I just hit this with trying to use enums with H2.

I'd like to propose a simpler solution. Whenever I need an actual enum in Scala, I just create it in Java. With the latest version of Java enums, where they can take arguments, you can define methods on them, etc., they're far superior in my opinion to anything Scala has specifically for enums and are easy to use from Scala code.

Is there any way you could add an enumDirectory to the target setting for the ScalaGenerator and if it's set, don't create the Scala enums and instead create Java enums in the appropriate package?

I'm going to try the suggested workaround of generating both Java and Scala code, but I suspect I'll need to create different packages for each, which is kind of a pain and I'm likely to accidentally use the Scala ones.

All 26 comments

Thank you very much for reporting this, and sorry that it hasn't been fixed yet. I remember having had a chat with one of the Scala compiler team members about this - to find a thorough solution that wouldn't depend as much on generated byte code as the current one. Then, the issue must've gotten off my radar.

Will investigate again

@lukaseder I took a second look at this, and it strikes me that, since you have control of the generated Scala code, you could solve this problem with the following tweaks:

  1. Mirror the values method onto the case objects generated for each enum value:
case object A extends MyEnum {
  override def getLiteral : String = "A"

  def values = MyEnum.values
}

case object B extends MyEnum {
  override def getLiteral : String = "B"

  def values = MyEnum.values
}
  1. Adjust the Scala reflection code to correctly handle the synthetic classes generated for each case object:
    Class<?> companionOrValueClass = type.getName().endsWith("$") ? type : Thread.currentThread().getContextClassLoader().loadClass(type.getName() + "$");
    java.lang.reflect.Field module = companionClass.getField("MODULE$");
    Object companionOrValue = module.get(companionOrValueClass);
    return (EnumType[]) companionOrValueClass.getMethod("values").invoke(companionOrValue);

This solution, like the existing code, relies on the fact that Scala will generate a synthetic class ending in "$" for each object in the source code, and that the Scala object can be loaded via the static "MODULE$" field of that synthetic class in order to invoke its "values" method.

Since the "values" method is guaranteed (by code generation) to exist on both the companion object to the enum trait and on each of the case objects for the enum values, the above code should work for any JVM Class that is related to the enum that was loaded statically or dynamically by Java or Scala code:

// In Java:
class JavaEnumLoader {
    public static void main(String[] args) throws ClassNotFoundException {
        Tools.enums(MyEnum.class);
        Tools.enums(A.class);
        Tools.enums(B.class);
        for (MyEnum value : MyEnum$.MODULE$.values()) {
            Tools.enums(value.getClass());
        }
        String packageName = ...;
        Tools.enums(Class.forName(packageName + "MyEnum"));
        Tools.enums(Class.forName(packageName + "MyEnum$"));
        Tools.enums(Class.forName(packageName + "A"));
        Tools.enums(Class.forName(packageName + "A$"));
        Tools.enums(Class.forName(packageName + "B"));
        Tools.enums(Class.forName(packageName + "B$"));
    }
}

// In Scala:
object ScalaEnumLoader extends App {
  Tools.enums(classOf[MyEnum])
  Tools.enums(MyEnum.getClass)
  Tools.enums(A.getClass)
  Tools.enums(B.getClass)
  for (value <- MyEnum.values) {
    Tools.enums(value.getClass)
  }
}

We're running into this too - at the moment this makes it impossible (at the very least) to insert enum values via the DSL when using the ScalaGenerator.

As far as I'm aware, short of using a Scala shim (to use either macros or a runtimeMirror which relies on implicits and thus Scala code), the premier solution to the problem at hand is to use the $-suffix synthetic class as well as MODULE$.

The solution proposed by @robbytx above should be essentially complete (really it comes down to checking whether you already received a synthetic class and grabbing MODULE$).

Is there any chance this fix could form part of an upcoming release? In the meanwhile I think we're going to have to (at best) work around this at quite a lot of extra effort. :(

Workaround for now:

val field = COFFEE.SIZE
val enum = CoffeeEnum.single
val literal = enum.getLiteral
query.set(field.asInstanceOf[Field[Any]], DSL.cast(literal, field.getDataType).asInstanceOf[Field[Any]])

It would be nice to be able to unpick this (since this comes up a lot, in a database using enums). :)

Worth adding at this point that the above workaround doesn't work for building records and then later retrieving values from them.

jOOQ picks this up when setting to the DB later and issues a string cast, but if you try to retrieve the value from the record before pushing it to the DB, jOOQ will try to cast the value of type Cast to the type of the enum field, causing a ClassCastException.

I'm not sure there's a good middle-ground solution that solves this, other than transforming all record types just before setting to the DB to turn all setting of enum values into this cast from from above.

Thanks for your additional feedback, @tekacs. I'm afraid, the best workaround I can offer right now is to run two code generations: 1x Scala and 1x Java, and then use the Java enums instead of the Scala ones (e.g. generate both code sets in the same package, different output directory, and then delete Scala enums, and Java non-enum code).

Thanks and :+1: - for now we're using the workaround I mentioned for writes and doing a re-fetch of a clean record from the database for reads (since jOOQ does the right thing even with Scala enums when reading from the DB, given the nature of this bug).

Unfortunately using Java enums isn't really practical, for a variety of reasons (for one case objects each have their own type, where Java enums are erased to a single type AIUI), so the above two workarounds will have to do for now.

We'll just have to wait for the fix for this (though there's a lot on the project board for the next jOOQ version!) :smile:

@lukaseder Given that this affects us pretty heavily (we use a lot of enums), would you be amenable to a pull request implementing the solution suggested by @robbytx at all?

I actually started to work on that, but I got stuck updating AfterMigrationTestScala.scala because the H2 DB has only recently added ENUM support, and jOOQ doesn't yet have support for it.

@tekacs:

@lukaseder Given that this affects us pretty heavily (we use a lot of enums), would you be amenable to a pull request implementing the solution suggested by @robbytx at all?

I'm sorry for the delay. I'm not sure if this is ready for a PR yet. The current Scala enum types are insufficiently supported throughout jOOQ's internals, which relies on the JVM's reflection methods to look up such enums, e.g. Class.getEnumConstants(). This obviously doesn't work with the current Scala enum emulation.

We'll have to think about something more robust, language agnostic.

If I can restate the problem:

  • In Scala codegen, an ENUM column maps to (1) sealed trait data type, (2) case objects extending the type for each possible value, and (3) a companion object for the sealed trait, containing a values that contains each of the companion objects.
  • Per http://docs.scala-lang.org/tutorials/tour/singleton-objects.html#notes-for-java-programmers, the supported method for access methods in (3) is to invoke like X$.MODULE$.values().
  • Actually, since Java 8 supports static methods in interfaces, Scala now even generates static forwarder methods on the interface, so you can invoke X.values() from Java. See https://github.com/scala/scala/pull/5131.
  • The problem is that Tools.enums(Class<? extends E> type) may be passed the Class from either (1) MyEnum.class or (2) A$.class, and the current implementation works for (1) the sealed trait, but not (2) the case objects.

To solve this problem, my proposal was to add a forwarder method on each case object that simply delegates to the existing values method on the companion object, and then add a slight modification to the existing implementation of Tools.enums so that it would skip the step of appending "$" to type.getName() in the case where it was invoked with a case object's class (which was already suffixed with "$").

While this solution isn't super elegant, it does permit the Tools.enums method to return the correct result whether it is given the class of the sealed trait or a case object.

If you're willing to sacrifice Scala 2.11 compatibility (at least for enums), then an even easier solution (which similarly requires mirroring the values method on each case object) is to simply invoke the values method via reflection. Update: The values method is static on the sealed trait MyEnum.class, but non-static on the case objects A$.class. So you either need to invoke it via A$.MODULE$, or you can call it statically via the generated class for each case object A.class (which you would reach by removing the trailing "$" suffix from type.getName()).

If you like, you could even introduce a Java interface that declares the existence of this method, that every case object and the companion object (of the sealed trait) would implement:

public interface HasEnumValues<T extends EnumType> {
    T[] values();
}

If you don't want to update codegen for some reason, then to implement Tools.enums correctly for case object classes, then I think you'll need to walk up the class hierarchy to sealed trait's class, and then you can use the existing code to invoke the values method from the companion object.

The primary limitation here for a generic solution is that Tools.enums is trying to invoke a static method in a polymorphic way by using the class definition.

For Java enums, this seems easy because java.lang.Class has special support via the getEnumConstants method. Interestingly, the current implementation of that method simply invokes the (static) values method that is generated by the Java compiler for each enum. Unfortunately, it only does so after confirming that java.lang.Class.isEnum is true.

With Scala, there's also (scala.Enumeration)[https://www.scala-lang.org/api/current/scala/Enumeration.html], which is implemented as a singleton object, and as such, should automatically permit static invocation of the values method. However, this pattern suffers from some limitations -- chief among them is the inability for values to extend additional traits like org.jooq.EnumType so that option is off the table.

When using the more powerful (and idiomatic?) sealed trait + case objects approach, there's no getting around the fact that there are multiple java.lang.Class involved. So, having the ability to invoke a particular static method across multiple classes via runtime reflection is going to require one or both of:

  1. Requiring the source code to adhere to some convention (as I've proposed).
  2. Being able to navigate from a given class to a target class that is known to contain the static method.

Obviously, it would be ideal if the Scala language or runtime could offer some support for either of those.

For the first requirement, I now think a Java interface / Scala trait that both the sealed trait and companion object each implement is ideal:

object MyEnum extends HasEnumValues[MyEnum] {

  val A : MyEnum = mypackage.enums.A
  val B : MyEnum = mypackage.enums.B

  def values : Array[MyEnum] = Array(
      A
    , B
  )

  ...
}

sealed trait MyEnum extends EnumType with HasEnumValues[MyEnum] {
  override def values = MyEnum.values
  ...
}

case object A extends MyEnum {
  override def getLiteral : String = "A"
}

case object B extends MyEnum {
  override def getLiteral : String = "B"
}

For the second requirement, it could be slightly cleaner to utilize scala-reflect's mirrors, but that would require an additional dependency beyond scala-library, which is probably undesirable for such a narrow use case.

Alternatively, you could walk the class hierarchy as I alluded to in my previous comment, using something like:

    Preconditions.checkState(EnumType.class.isAssignableFrom(type));
    while(true) {
        Class<?> parentType = type;
        for (Class<?> someInterface : type.getInterfaces()) {
            if (EnumType.class.isAssignableFrom(someInterface)) {
                parentType = someInterface;
                break;
            }
        }
        if (parentType == type) {
            break; // found parent type
        }
        type = parentType;
    }

    // then invoke ``type.values()`` via reflection (if Scala 2.12) or ``type$.MODULE$.values()`` (for Scala 2.11 and earlier)

IMHO, walking the class hierarchy to find the sealed trait seem more fraught than requiring codegen to mirror the values method onto the sealed trait. It seems likely that some users might construct intermediate subclasses of EnumType or of the sealed trait that would frustrate whatever algorithm was used for identifying the exact Class that contained the values method.

By contrast, if we add the "values" method to the sealed trait, then it is automatically inherited by each case object, and therefore is automatically available via the MODULE$ instance, or (again in Scala 2.12) statically on the unsuffixed class.

After further thought, I think the following algorithm is generic enough to support any combination of:

  • Scala pre / post-2.12
  • jOOQ with / without codegen mirroring of the values method

It has the added benefit of also supporting:

  • Java enums
  • Any subtype of EnumType that contains a static values method
  • Any subtype of EnumType that has a companion class ending in "$" that contains a static values method
  • Any subtype of EnumType that has a companion class ending in "$" that contains a non-static values method and a singleton instance in a MODULE$ field.
    public static EnumType[] enums(Class<?> type) {
        try {
            for (Class<?> enumType = type;
                 enumType != null && EnumType.class.isAssignableFrom(enumType) && enumType != EnumType.class;
                 enumType = findParentEnumType(enumType)) {

                Method method = findValuesMethod(enumType);
                if (method != null) {
                    EnumType[] values = invoke(method, enumType);
                    if (values != null) {
                        return values;
                    }
                }
                Class<?> companionClass = findCompanionClass(enumType);
                if (companionClass != null) {
                    method = findValuesMethod(companionClass);
                    if (method != null) {
                        EnumType[] values = invoke(method, companionClass);
                        if (values != null) {
                            return values;
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new MappingException("Error while locating values method for " + type, e);
        }
        throw new MappingException("Could not locate values method for " + type);
    }

    private static Method findValuesMethod(Class<?> type) {
        try {
            return type.getMethod("values");
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    private static Class<?> findCompanionClass(Class<?> type) {
        if (!type.getName().endsWith("$")) {
            try {
                return Thread.currentThread().getContextClassLoader().loadClass(type.getName() + "$");
            } catch (ClassNotFoundException e) {
                // type does not have companion object
            }
        }
        return null;
    }

    private static EnumType[] invoke(Method valuesMethod, Class<?> type) throws IllegalAccessException, InvocationTargetException {
        if (Modifier.isStatic(valuesMethod.getModifiers())) {
            return (EnumType[]) valuesMethod.invoke(null);
        }
        if (type.getName().endsWith("$")) {
            try {
                Field moduleField = type.getField("MODULE$");
                Preconditions.checkState(Modifier.isStatic(moduleField.getModifiers()));
                return (EnumType[]) valuesMethod.invoke(moduleField.get(null));
            } catch (NoSuchFieldException e) {
                // type is missing MODULE$ field ... maybe it's not a Scala companion class?
            }
        }
        return null;
    }

    private static Class<?> findParentEnumType(Class<?> type) {
        Class<?> parentType = null;
        for (Class<?> someInterface : type.getInterfaces()) {
            if (EnumType.class.isAssignableFrom(someInterface)) {
                if (parentType == null || someInterface.isAssignableFrom(parentType)) {
                    parentType = someInterface;
                }
            }
        }
        return parentType;
    }

While I'd still like to advocate for the solution I proposed above, I've developed an alternative workaround using a custom org.jooq.Converter:

import org.jooq.EnumType
import org.jooq.impl.AbstractConverter

import scala.reflect.ClassTag

/**
  * jOOQ Converter for enum types generated by [[org.jooq.codegen.ScalaGenerator]],
  *   to workaround jOOQ's default (mis)handling of Scala enums (https://github.com/jOOQ/jOOQ/issues/5758)
  *
  * @param valueOf reference to the `valueOf` method from the generated companion object of the enum type
  * @tparam Enum the sealed trait generated for the particular enum type
  */
class ScalaEnumConverter[Enum >: Null <: EnumType : ClassTag](valueOf: String => Enum)(implicit classTag: ClassTag[Enum])
  extends AbstractConverter[String, Enum](classOf[String], classTag.runtimeClass.asInstanceOf[Class[Enum]]) {

  override def from(databaseObject: String) = Option(databaseObject).map(valueOf).orNull

  override def to(userObject: Enum) = Option(userObject).map(_.getLiteral).orNull
}

Since mapping this converter with a <forcedType> simultaneously disables jOOQ's generation of the enum types from the database schema, I use a custom generator class instead:

import org.jooq.Name
import org.jooq.codegen.ScalaGenerator
import org.jooq.impl.DefaultDataType
import org.jooq.meta.{Database, DefaultDataTypeDefinition, SchemaDefinition}

/**
  * Overrides jOOQ's default handling for generated [[org.jooq.EnumType]] to force use of [[ScalaEnumConverter]] instead.
  */
class ScalaGeneratorWithEnumConverter extends ScalaGenerator {

  val converterName = classOf[ScalaEnumConverter[_]].getName

  override def getTypeReference(db: Database, schema: SchemaDefinition, t: String, p: Int, s: Int, l: Int, n: Boolean, i: Boolean, d: String, u: Name) = {
    if (db.getEnum(schema, u) != null) {
      new StringBuilder()
        .append(getJavaTypeReference(db, new DefaultDataTypeDefinition(db, schema, DefaultDataType.getDataType(db.getDialect, classOf[String]).getTypeName, l, p, s, n, d, null.asInstanceOf[Name])))
        .append(".asConvertedDataType(new ").append(converterName).append("(")
        .append(getStrategy.getFullJavaClassName(db.getEnum(schema, u)))
        .append(".valueOf))")
        .toString
    } else {
      super.getTypeReference(db, schema, t, p, s, l, n, i, d, u)
    }
  }
}

With this, you can simply replace org.jooq.codegen.ScalaGenerator with the fully-qualified name of the ScalaGeneratorWithEnumConverter class, and all enum columns will automatically use the converter, which should workaround the limitation of the Tools.enums method.

N.B. The ScalaGeneratorWithEnumConverter class must be compiled in advance and present on the classpath when code generation executes. With Maven, you can compile these in a separate module, and then add that module as a dependency of the jooq-codegen-maven plugin. Meanwhile, the ScalaEnumConverter class must be present on the classpath of your application at runtime, but since does not depend on the jooq-codegen or jooq-meta modules, you can mark those dependencies as <optional>true</optional>, so they will be omitted from your application's classpath.

Is there any progress on this issue? It has been open for a really long time and is a blocker for using enums with Scala

Is there any progress on this issue? It has been open for a really long time and is a blocker for using enums with Scala

Thank you for your comment, @ollyw. We have much older issues, but that obviously doesn't help you now :)

The reason why we haven't moved anywhere in this area is that this is much more complex to solve than it may appear.

In order to fix this thoroughly, we will need:

  • A good understanding of the moving target that are enumeration types in Scala (to my understanding, yesterday's approach is no longer what people do today, nor does it match what Dotty is going to do).
  • In fact, Dotty could finally offer a thorough solution to this, see https://dotty.epfl.ch/docs/reference/enums/enums.html, when we can generate Java compatible enums like enum Color extends java.lang.Enum[Color] { case Red, Green, Blue }
  • In any case, we need much better infrastructure internally to model enum types, rather than relying on java.lang.Enum (see #7025)
  • Once we tackle that, we should think of what an enum type really is on a database level, and how we can possibly use synergies with other types, such as SET or MULTISET types that some vendors support

This is a lot of work for a relatively narrow area of added value, which is why we could not have prioritised this in the recent past. Some workarounds have been documented in this issue.

Thanks for the super quick response @lukaseder. I totally get that there are a whole lot of priorities to juggle. The irony is that the enum types work fine when embedded in a postgres array field. It was really impressive the generator worked for that. Then in a more simple use case of a column without an array it falls over. I'll try the workaround above.

When testing the suggested workaround, using the ScalaGeneratorWithEnumConverter, errors occur at runtime when inserting into the db. The error is:
SQL [insert into ..... my_db_enum" = ? returning "my_table".*]; ERROR: column "my_db_enum" is of type myschema.my_db_enum_type but expression is of type character varying [info] Hint: You will need to rewrite or cast the expression
It seems that the binding is incorrect as there is no cast from string to db enum type in the SQL. This could be because somehow it is not picking up the correct binder. The function getTypeReference in ScalaGeneratorWithEnumConverter does appear to be getting/using the correct SQLDialect, in my case Postgres.

Any hints would be very welcome

@ollyw I've been using the workaround with MySQL, which automatically converts strings to the appropriate enum type.

Looking at the native jOOQ EnumConverter, there is a branch where the converter will return a number rather than a string. Potentially, that's what Postgres is expecting instead of a string? If so, you'll need to change ScalaEnumConverter to convert to/from a number instead of a string, and change ScalaGeneratorWithEnumConverter to use something other than [ScalaEnumType].valueOf.

What I would recommend doing is to generate the code once, and then try to tweak it and ScalaEnumConverter so that you get it working. Then you can revise ScalaGeneratorWithEnumConverter to generate the right code for your types. Let us know if you get it working.

I just hit this with trying to use enums with H2.

I'd like to propose a simpler solution. Whenever I need an actual enum in Scala, I just create it in Java. With the latest version of Java enums, where they can take arguments, you can define methods on them, etc., they're far superior in my opinion to anything Scala has specifically for enums and are easy to use from Scala code.

Is there any way you could add an enumDirectory to the target setting for the ScalaGenerator and if it's set, don't create the Scala enums and instead create Java enums in the appropriate package?

I'm going to try the suggested workaround of generating both Java and Scala code, but I suspect I'll need to create different packages for each, which is kind of a pain and I'm likely to accidentally use the Scala ones.

@toddobryan Thanks for the suggestion. That's a surprisingly simple and pragmatic idea - hard to see why this wasn't considered so far! Kotlin also binds their enum types to java.lang.Enum behind the scenes (adding some sugar), so this would make for a much more consistent solution.

I'll try to think of the best approach to solve this. I'm more than happy to replace the existing enums with Java ones for good, without the option of choosing (and the maintenance effort that is involved with maintaining both options).

So, having investigated this more thoroughly now, I think I'll go ahead with https://github.com/jOOQ/jOOQ/issues/10998, i.e. letting the ScalaGenerator generate Java enums by default. The existing logic will be maintained as deprecated for those who dont' want to upgrade this immediately, but fixes are no longer applied.

Dotty will have its own enum type, which can be made Java compatible by extending java.lang.Enum, so, the suggestion of generating Java enums seems forward compatible with dotty.

Closing this as won't fix.

@toddobryan Do your Java enums have references back into Scala code? It seems that this might cause issues at compile time for some setups, as the PostgreSQL enum types need to reference the schema, which is generated as Scala code

I guess this kind of tweak is acceptable to projects that bet on the Scala language strategically: https://github.com/jOOQ/jOOQ/issues/10998#issuecomment-731122562

How can we achieve your tweak using SBT ?
I'm still facing the issue I'll have to write plain sql...

@Simon-Bru The fix will ship in jOOQ 3.15 with https://github.com/jOOQ/jOOQ/issues/10998

@toddobryan Do your Java enums have references back into Scala code? It seems that this might cause issues at compile time for some setups, as the PostgreSQL enum types need to reference the schema, which is generated as Scala code

Sorry. I've been really busy with work and haven't had a chance to do fun stuff recently. In my case, I was using H2 and they didn't refer back, but I'd probably want PostgreSQL eventually, so I'm not sure how that would work. I'll try to take a look over the holidays and report back.

Was this page helpful?
0 / 5 - 0 ratings