Enum with constant and also dynamic values from database

让人想犯罪 __ 提交于 2019-12-02 23:46:48

问题


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);
    }
}

回答1:


If I understand it correctly the requirements are:

  • having a MyType.byId(Integer id) method that delivers some predefined values
  • it should be also extended dynamically from a Table Type from the database

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?




回答2:


A distinct non-answer: you are trying to force yourself down the wrong rabbit hole.

The whole point of Enums are to give you certain advantages at compile time. At runtime, it really wouldn't matter to the JVM if you have a class with some final static Whatever fields, or an Enum with different constants. Or if you use an EnumSet versus an ordinary Set.

You use enums because they allow you to write down your source code in more elegant ways.

Therefore the approach of generating enums at runtime doesn't make sense.

The idea of enums is that you write source code using them. But when your enums are generated for you, how exactly would you write source code exploiting them?! As mentioned already, enum classes are final by default. You can't extend or enhance them separately. Whatever you would want to have, it needs to be generated for you. Which again raises the question: how would you exploit something at compile time, that gets generated at runtime?

Therefore, from a conceptual point of view, the approach outlined in the other answer (to use a Map) is a much better design point than trying to turn enums into something that they aren't meant to be.




回答3:


enum represents a group of constants (unchangeable variables, like final variables). you can not define it on runtime.



来源:https://stackoverflow.com/questions/53628583/enum-with-constant-and-also-dynamic-values-from-database

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!