javaenumsdatabase-driven

Enum with constant and also dynamic values from database


I have a current state where an enum MyType represent Type table with columns as:

ID
Name

And it's used to identify type using ID parameter with byId method:

public enum MyType {

FIRST_TYPE("First Type", 10),
SECOND_TYPE("Second Type", 20);
public static class Holder {
    static Map<Integer, MyType > idMap = new HashMap<>();
    private Holder() { }
}

private MyType(String name, Integer id) {
    this.name = name;
    this.id = id;
    Holder.idMap.put(id, this);
}

public String getName() {
    return name;
}

public static MyType byId(Integer id) {
    return Holder.idMap.get(id);
}

My new requirement is to support also values exists in Type table, I found answers for dynamic enum, but accept answer is not to do it

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

What will be a better solution for finding also values (mainly IDs) from database (for example ID 30)

 select ID from TYPE

Can I extends existing state instead of change it? can I add extra IDS from database using method?

EDIT

Even if I update as @StefanFischer suggested an interface which populate map with enum class and new database class, I still expect in code an enum return by byId method,

public interface MyType {
    public static class Holder {
        static Map<Integer, MyType> idMap = new HashMap<>();
        private Holder() { }
    }

    public default void add(MyType myType, Integer id) {
        Holder.idMap.put(id, myType);
    }


    public static MyType byId(Integer id) {
        return Holder.idMap.get(id);
    }
}

Solution

  • If I understand it correctly the requirements are:

    So a enum can not be extended dynamically, but we could switch to a class.

    So staying close to your code one could write something like:

    import java.util.HashMap;
    import java.util.Map;
    
    public class MyType {
    
        static Map<Integer, MyType> idMap = new HashMap<>();
        static {
            idMap.put(10, new MyType("First Type"));
            idMap.put(20, new MyType("Second Type"));
        }
    
        private final String name;
    
        private MyType(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public static MyType byId(Integer id) {
            return idMap.get(id);
        }
    
        public static void addType(String name, Integer id) {
            MyType lookup = byId(id);
            if(lookup != null) {
                if(!lookup.getName().equals(name)) {
                    System.out.println("conflicting redefinition for id " + id + ": '" + name + "' vs '" + lookup.name + "'");
                    //handle...
                }
            }
            idMap.put(id, new MyType(name));
        }
    }
    

    Test Data

    Let's assume we have the following in the database:

    stephan=# select * from Type;
     id |    name     
    ----+-------------
     30 | Third Type
     10 | First Type
     20 | Second Type
    (3 rows)
    

    So in the database we have the predefined types with id=10 and id=20 but also a type with id=30 that is not known per default to the application. But we can populate the types from the database.

    Test Case

    public static void main(String[] args) {
        try {
            Connection connection = createConnection();
            try (connection) {
                populateTypes(connection);
            }
    
            MyType type;
    
            type = MyType.byId(10);
            System.out.println(type.getName());
    
            type = MyType.byId(20);
            System.out.println(type.getName());
    
            type = MyType.byId(30);
            System.out.println(type.getName());
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    JDBC Example

    It doesn't matter what actual database technology is used to retrieve the values. Here an example for JDBC:

    private static void populateTypes(Connection connection)
            throws SQLException {
    
        String sql = "SELECT * FROM type";
        try (Statement st = connection.createStatement()) {
            try (ResultSet rs = st.executeQuery(sql)) {
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    MyType.addType(name, id);
                }
            }
        }
    }
    

    Demo Output

    First Type
    Second Type
    Third Type
    

    Is that what you are looking for?