【问题标题】:Enum with constant and also dynamic values from database具有来自数据库的常量和动态值的枚举
【发布时间】:2018-12-05 09:03:32
【问题描述】:

我有一个当前状态,其中 enum MyType 表示类型表,其列为:

ID
Name

它用于通过byId方法使用ID参数来识别类型:

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

我的新要求是支持类型表中也存在值,我发现answers 用于动态枚举,但接受答案不是这样做

没有。枚举始终在编译时固定。您可以做到这一点的唯一方法是动态生成相关的字节码。

对于从数据库(例如 ID 30)中查找值(主要是 ID)有什么更好的解决方案

 select ID from TYPE

我可以扩展现有状态而不是改变它吗?我可以使用方法从数据库中添加额外的 IDS 吗?

编辑

即使我更新为@StefanFischer 建议使用枚举类和新数据库类填充映射的接口,我仍然希望在代码中通过byId 方法返回枚举,

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

【问题讨论】:

  • 枚举不适合动态值。您应该使用 this 的类和对象来表示数据库条目。
  • @markusw 你能建议添加扩展现有状态而不是改变它的类吗?
  • 您不能扩展枚举。实际上它只是不适合这个任务。您应该放弃此解决方案并创建一个提供完全相同接口和名称的类。因此客户端代码不需要被触及。
  • 我猜“问题”是关于这个的:“我的新要求是支持类型表中也存在值”但我不明白。这是否意味着你想在某处创建一个“exists(...)”方法来检查类型表中是否存在一个类型?这种方法会有一些限制吗?
  • 这个问题显然受到XY problem的影响。请先查看您的问题,然后找到合适的技术解决方案。你反过来做:你坚持一个解决方案,无论多么不适合这个问题。鉴于作者拥有 26k 的声誉,这令人难以置信。枚举在设计上不是动态的,这是它们的主要功能之一(不是不必要的限制)。

标签: java enums database-driven


【解决方案1】:

如果我理解正确,要求是:

  • 具有传递一些预定义值的 MyType.byId(Integer id) 方法
  • 还应该从数据库中的表Type动态扩展

所以一个枚举不能动态扩展,但我们可以切换到一个类。

因此,与您的代码保持密切联系可以编写如下内容:

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

测试数据

假设我们在数据库中有以下内容:

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

因此,在数据库中,我们有 id=10 和 id=20 的预定义类型,还有一个 id=30 的类型,默认情况下应用程序不知道。但是我们可以从数据库中填充类型。

测试用例

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 示例

使用什么实际的数据库技术来检索这些值并不重要。这里以 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);
            }
        }
    }
}

演示输出

First Type
Second Type
Third Type

这就是你要找的吗?

【讨论】:

  • MyType 不能保留 Enum 吗?
  • 不,枚举必须在编译时知道。如果要使用 DB 条目动态扩展类型,则不能使用枚举。枚举中是否有您感兴趣的特定功能?
  • 我想用动态值扩展现有枚举
  • 不可能。枚举是公共​​的、静态的,最重要的是,默认情况下是 final,因此没有扩展,也不会更改枚举本身的值,强烈建议不要将字段值用作动态存储。想要枚举中的动态值是一种代码味道,并且强烈表明您需要一个成熟的类。
  • 一个很好的答案,感谢您为更“概念性”的答案留出一些空间......朝着同一个方向前进!
【解决方案2】:

一个明显的非答案:你试图强迫自己进入错误的兔子洞。

枚举的全部点是在编译时为您提供某些优势。在运行时,如果你有一个带有一些 final static Whatever 字段的类,或者一个带有不同常量的 Enum,那么对于 JVM 来说真的没关系。或者,如果您使用 EnumSet 而不是普通的 Set

您使用枚举是因为它们允许您以更优雅的方式写下您的源代码

因此,在运行时生成枚举的方法没有意义。

枚举的想法是您使用它们编写源代码代码。但是,当您的枚举为您生成时,您将如何编写利用它们的源代码?!如前所述,枚举类默认是最终的。您不能单独扩展或增强它们。无论您想拥有什么,它都需要为您生成。这再次提出了一个问题:您将如何在编译时利用在运行时生成的东西?

因此,从概念的角度来看,另一个答案中概述的方法(使用地图)比试图将枚举变成它们不是的东西要好得多本来就是。

【讨论】:

    【解决方案3】:

    enum 表示一组常量(不可更改的变量,如 final 变量)。你不能在运行时定义它。

    【讨论】:

      猜你喜欢
      • 2014-07-28
      • 1970-01-01
      • 2021-04-11
      • 1970-01-01
      • 2020-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多