From: david.karr on
Quite often databases will have columns that are stored as integers,
but represent enumerated values. In object-relational mapping, it's a
good idea to translate that integer value to the enumerated value it
represents.

The built-in "ordinal" value of an enum is almost useless. The integer
values for an enum always need to be controlled, and can't change if
you reorder things.

So you at least have to implement one custom field in the enum, which
I'll call "columnValue".

Somewhere you have to have code that translates those integer values
into the enumerated type value. The best place to do that is within
the enumerated type itself. Ideally, I'd like to do this in a way
that doesn't repeat the integer values, and is reasonably efficient.

A simple-minded implementation might look like this:

public static enum SomeType {
Foo(101),
Bar(100),
Gork(4001);

private int columnValue;

public final int getColumnValue() { return columnValue; }
public final void setColumnValue(int columnValue)
{ this.columnValue = columnValue; }

public SomeType getEnum(int columnValue) {
switch (columnValue) {
case 101: return Foo;
case 100: return Bar;
case 4001: return Gork;
default: return null;
}
}

SomeType(int columnValue) {
this.columnValue = columnValue;
}
}

Can someone think of a better way to do this, that doesn't repeat the
column values?
From: Peter Duniho on
david.karr wrote:
> Quite often databases will have columns that are stored as integers,
> but represent enumerated values. In object-relational mapping, it's a
> good idea to translate that integer value to the enumerated value it
> represents.
>
> The built-in "ordinal" value of an enum is almost useless. The integer
> values for an enum always need to be controlled, and can't change if
> you reorder things. [...]

I guess I don't really get this.

I see two choices: the enum values never change, or they may change.

You seem to be saying that a situation where they may never change is
bad. That the code needs to be able to allow them to change. But you
still want to store integers in the database.

So now you have a completely different set of integers that can never
change.

How is that better than just not allowing the enum values to ever change?

On the other hand, if you simply want to keep the enum implementation
completely separate from the database implementation, I would think that
putting the mapping into the enum type itself doesn't make much sense
either. A mapping in a completely separate class would seem more
appropriate. You could even use different versions of the class if the
mapping needed to change at some point, selecting the version according
to the version of the database you're accessing.

As for the code you posted, I don't understand its purpose. You seem to
be using the exact same numerical values for the integers and enum
values. Why have the switch statement at all? Why not return an enum
instance set to the passed-in "columnValue"?

I'm also a little confused by the "setColumnValue()" method, as it
implies to me that there's the expectation of a mutable enum instance.
I would normally expect an enum instance to be immutable. But that's
probably just an artifact of my relative unfamiliarity with Java's
implementation of enums.

Pete
From: EJP on
david.karr wrote:
> Can someone think of a better way to do this, that doesn't repeat the
> column values?

Have each enum value enter itself on construction into a Map<Integer,
YourEnum> with its own ColumnValue as the key.

Like Peter I cannot see the point of setColumnValue(), and the Map would
of course require the columnValue to be constant.
From: markspace on
david.karr wrote:

> The built-in "ordinal" value of an enum is almost useless. The integer
> values for an enum always need to be controlled, and can't change if
> you reorder things.


I wouldn't say that. I think enums and ordinal() have their uses, just
not the use you were hoping for.

In general, I think trying to make enums model any kind of external or
customer data is a bad idea. Customer data always changes, and enums
don't, without a lot of hassle.


> Can someone think of a better way to do this, that doesn't repeat the
> column values?


Map<Integer,String>.

Seriously. Set the strings and integers in a data or config file, or
better yet read them from the scheme when needed. Then you'll always
match the database, and you never have to go through the work of trying
to figure out what to do if and when someone starts to modify the schema.

public class DatabaseEnumeration {

String table;
String column;
Map<Integer, String> intToEnum;
Map<String, Integer> enumToInt;

...
}

Now you have the beginnings of a flexible class that can handle any
table's enumerations and doesn't have all the unpleasant side effects
associated with enums.

You do loose some code constructs, like case statements, which you'll
have to emulate with if-else chains, but that's not a lot to give up imo.
From: david.karr on
On Dec 18, 4:25 pm, EJP <esmond.not.p...(a)not.bigpond.com> wrote:
> david.karr wrote:
> > Can someone think of a better way to do this, that doesn't repeat the
> > column values?
>
> Have each enum value enter itself on construction into a Map<Integer,
> YourEnum> with its own ColumnValue as the key.
>
> Like Peter I cannot see the point of setColumnValue(), and the Map would
> of course require the columnValue to be constant.

Yes, you're right, there's no need for "setColumnValue()".

Concerning the Map, I had already thought of that. That's the obvious
way to do it. Now, how would you do it? I would assume you'd define
a static Map in the enum type and have the constructor put itself into
the map. The problem is, it doesn't appear to be possible to do
that. It doesn't compile. What might work is implementing some sort
of "super-map" that holds all the mappings for all enum types you
implement, so the key would have to concatenate the class and the
custom ordinal.