Hi Lukas,
>> Let's be practical.
> You're probably right.
but
> I'd like to re-use the existing EnumType
> interface and its getLiteral() attribute
So the real question is: do you want jOOQ to influence the global
design of an application or do you want jOOQ to help interfacing with
the database without being too intrusive in the application that is
using it? I can tell you that enumerations propagate far from the
simple database layer.
If you want to influence the application design, then of course you
can make up the rules.
If you want to help users simplify their database access, then re-use
their enums. If that means the generator should create a converter,
where users can define the rules for the bridge, then why not.
In practice, people build int enums the same way throughout an
application so generating a bridge would be simple. These enums tend
to have simple logic and once used in a program that rule cannot
change (use of the intValue() method to switch/case, or null checks if
toInteger(), etc.). I bet no jOOQ user has ever named that method
getLiteral(): they are not library makers but library users :)
Personaly, my team would never migrate to a scheme that mandates
changing the enumeration logic. This is too risky for our application
for various reasons. The most tolerable intrusion would be renaming
the method that returns the int value and/or implement an interface,
though "getLiteral" would not mean much to anyone which complexifies
the code.
Let me expand on a hypothetical generator-based solution, which I
think could cover all enum types (String, int, Integer, Date if you
like, etc.).
1. Say we have this xx.SomeEnum declaration in a codebase:
public enum SomeEnum {
public intValue() {...}
public static SomeEnum getEnum(int x) {...}
}
2. Then specify to the generator the logic for each enum:
enum: xx.SomeEnum
valueType: int
valueGetter: intValue
enumLookup: getEnum
sqlDataType: SMALLINT
3. I declare for the generator the column mapping of that enum:
TableX.ColumnY: xx.SomeEnum
4. The generator builds a central lookup class to convert the value
to/from an object.
This would be used by jOOQ when using/retrieving a value to/from the DB.
public class EnumLookup {
private final Map<Enum, EnumMapper<>> map = new HashMap<>();
public EnumLookup() {
map.put(SomeEnum.class, new EnumMapper<SomeEnum, Integer> {
public SomeEnum toEnum(Integer x) {
return SomeEnum.getEnum(x); // Autobox
}
public Integer fromEnum(SomeEnum e) {
return e == null? null: e.intValue(); // Autobox
}
public SQLDataType getDataType(SomeEnum e) {
return SQLDataType.SMALLINT;
}
}
}
public <T extends Enum<T>> EnumMapper<T, Object> getMapper(Class<T> clazz) {
return map.get(clazz);
}
}
Using the EnumMapper interface:
interface EnumMapper<T extends Enum<T>, U> {
public T toEnum(U x);
public Integer fromEnum(T e);
public SQLDataType getDataType(T e);
}
5. Use SomeEnum in field signatures
public final org.jooq.TableField<test.generated.tables.records.SomeRecord,
SomeEnum> MY_ENUM = createField("MyEnum",
SQLDataType.createEnumType(SomeEnum.class), this);
6. Use the lookup when marchalling/unmarshalling the enum.
This work for any type of literal, whether they are object or
primitive type. Bonus: we have a central place where we see all enum
declarations and how they get translated.
Other things could get added to the generator description like whether
a null enumeration is allowed (when an object literal is used).
> Other options than making EnumType<T> generic are likely to introduce
> incompatibilities for my MySQL and Postgres users, where true enums
> (as opposed to synthetic ones) already exist.
I am sure the mapper solves these kind of issues, by boxing/unboxing
(basically adapting) primitive to objects.
> Besides, there can still
> be static lookup methods outside of the enum, as implemented for Pay
> in MySQLFactory.
Maybe my naive generator descriptor should be enhanced to support
defining a helper class from which to look up if not directly
available on the enum itself. I believe this can be solved with the
above solution.
> Enum.valueOf() works in similar ways. It maintains a global dictionary
> that is maintained in java.lang.Class.enumConstantDirectory. I can get
> some inspiration there...
Yes, and EnumLookup (or rather, the mapper if it is turned into an
abstract class), could aggregate the values in a map at the first call
to avoid going many times to potentially avoid calling many times
badly implemented implementations of getEnum(x).
Please let me know what you think!
-Christopher