28

I have a table which maps String->Integer.

Rather than create an enum statically, I want to populate the enum with values from a database. Is this possible ?

So, rather than delcaring this statically:

public enum Size { SMALL(0), MEDIUM(1), LARGE(2), SUPERSIZE(3) };

I want to create this enum dynamically since the numbers {0,1,2,3} are basically random (because they are autogenerated by the database's AUTOINCREMENT column).

Jacques René Mesrine
  • 46,127
  • 27
  • 66
  • 104
  • Duplicate: http://stackoverflow.com/questions/725043/dynamic-enum-in-c – aleemb May 12 '09 at 06:55
  • 4
    Except that was for C#, not Java, and we all know you can do more with Java :-) – paxdiablo May 12 '09 at 06:58
  • 1
    Possible duplicate of [Dynamic enum in C#](https://stackoverflow.com/questions/725043/dynamic-enum-in-c-sharp) – StayOnTarget Apr 24 '18 at 12:42
  • 1
    Conceptually, most of us will understand that a compile-time set of Enum values cannot be built from Database at run-time. In the very least you will get stuck with enum constants not being known beforehand. Enum constants act as individual Singletons available at class load time, and that is the convenience we are looking for most likely. If you resolve the singleton issue, then values act as internalized version, and a direct `==` comparison will work. If additionally, we make sure that the singletons are auto-initialized during application load time, then we can avoid having an enum. – YoYo Jun 17 '19 at 15:12

6 Answers6

37

No. Enums are always fixed at compile-time. The only way you could do this would be to dyamically generate the relevant bytecode.

Having said that, you should probably work out which aspects of an enum you're actually interested in. Presumably you weren't wanting to use a switch statement over them, as that would mean static code and you don't know the values statically... likewise any other references in the code.

If you really just want a map from String to Integer, you can just use a Map<String, Integer> which you populate at execution time, and you're done. If you want the EnumSet features, they would be somewhat trickier to reproduce with the same efficiency, but it may be feasible with some effort.

So, before going any further in terms of thinking about implementation, I suggest you work out what your real requirements are.

(EDIT: I've been assuming that this enum is fully dynamic, i.e. that you don't know the names or even how many values there are. If the set of names is fixed and you only need to fetch the ID from the database, that's a very different matter - see Andreas' answer.)

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
28

This is a bit tricky, since the population of those values happens at class-load time. So you will need a static access to a database connection.

As much as I value his answers, I think Jon Skeet may be wrong this time.

Take a look at this:

public enum DbEnum {
    FIRST(getFromDb("FIRST")), SECOND(getFromDb("second"));

    private static int getFromDb(String s) {
        PreparedStatement statement = null;
        ResultSet rs = null;
        try {
            Connection c = ConnectionFactory.getInstance().getConnection();
            statement = c.prepareStatement("select id from Test where name=?");
            statement.setString(1, s);
            rs = statement.executeQuery();
            return rs.getInt(1);

        }
        catch (SQLException e) {
           throw new RuntimeException("error loading enum value for "+s,e);
        }
        finally {
            try {
                rs.close();
                statement.close();
            } catch (SQLException e) {
                //ignore
            }
        }
        throw new IllegalStateException("have no database");
    }

    final int value;

    DbEnum(int value) {
        this.value = value;
    }
}
Andreas Petersson
  • 16,248
  • 11
  • 59
  • 91
  • 7
    You're assuming that you know at *compile-time* how many values there will be, and what they should be called. If that's the case, then that's fine - but I don't see anything in the question which suggests that. (I think your query should be selecting the ID as well, rather than just 1 :) – Jon Skeet May 12 '09 at 07:26
  • 2
    An informative response that reminds us about the object oriented aspects of enums - thanks, Andreas. – Kevin Day May 13 '09 at 05:12
  • (Late reply, I know) You could make this DRYer by using reflection to populate the values, I think. So you could just have FIRST() and look the name up in initialization code? – Rich Sep 13 '15 at 21:16
  • Might the `getFromDb` method leak connections? The connection is neither closed nor returned to a pool. Moreover, `statement.close();` won't be invoked anymore if there's an exception thrown before by `rs.close();` which would be done implicitely by `statement.close();` anyway. – MrSnrub Jul 02 '18 at 23:47
6

Improving on what Andreas did, you can load the contents of the database into a map to reduce the number of database connections needed.

public enum DbEnum {
    FIRST(getFromDb("FIRST")),
    SECOND(getFromDb("second"));

    private Map<String,Integer> map;
    private static int getFromDB(String s)
    {
        if (map == null)
        {
           map = new HashMap<String,Integer>();
           // Continue with database code but get everything and
           // then populate the map with key-value pairs.
           return map.get(s);
        }
        else {
            return map.get(s); }
    }
}
Community
  • 1
  • 1
Tigertron
  • 628
  • 6
  • 6
  • It's good to mention this idea at this point. However let it be clear that this code performs horribly in any realistic (that's to say multi-thread) production environment. If your enum is used widely inside the app, this initialisation code is doomed to return values that are absolutely unreliable. – Marco Faustinelli Jul 11 '16 at 08:42
3

Enums are not dynamic, so the short answer is that you can't do it.

Also have a look at Stack Overflow question Dynamic enum in C#.

Community
  • 1
  • 1
aleemb
  • 31,265
  • 19
  • 98
  • 114
1

You need to replicate in code what is in the database (or vice-versa). See this question for some good advices.

Community
  • 1
  • 1
kgiannakakis
  • 103,016
  • 27
  • 158
  • 194
0

In all the languages I know enums are static. The compiler can make some optimizations on them. Therefore the short answer is no, you can't.

The question is why you want to use an enum in this way. What do you expect? Or in other words why not use a collection instead?

Luixv
  • 8,590
  • 21
  • 84
  • 121
  • Legacy code, where there's an enum used all over the place which now we want to dynamically load from the database. Saves a fair amount of refactoring if you can just load the enum on the fly... – Brian Knoblauch Jun 08 '16 at 17:55