【问题标题】:Populate an enum with values from database使用数据库中的值填充枚举
【发布时间】:2009-05-12 06:46:11
【问题描述】:

我有一个映射 String->Integer 的表。

我不想静态创建枚举,而是想用数据库中的值填充枚举。这可能吗?

所以,与其静态地删除这个:

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

我想动态创建这个枚举,因为数字 {0,1,2,3} 基本上是随机的(因为它们是由数据库的 AUTOINCREMENT 列自动生成的)。

【问题讨论】:

  • 除了 C#,不是 Java,我们都知道你可以用 Java 做更多事情:-)
  • Dynamic enum in C#的可能重复
  • 从概念上讲,我们大多数人都会明白,在运行时无法从数据库构建一组编译时枚举值。至少,您会遇到事先不知道的枚举常量。枚举常量充当类加载时可用的单个 Singleton,这很可能是我们正在寻找的便利。如果您解决了单例问题,那么值将作为内部化版本,直接== 比较将起作用。如果另外,我们确保单例在应用程序加载期间自动初始化,那么我们可以避免使用枚举。

标签: hashmap enumeration java


【解决方案1】:

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

话虽如此,您可能应该弄清楚您真正感兴趣的枚举的哪些方面。大概您不想在它们上使用switch 语句,因为这意味着静态代码而您不这样做不知道静态的值...代码中的任何其他引用也是如此。

如果您真的只是想要从StringInteger 的映射,您可以使用在执行时填充的Map<String, Integer>,然后就完成了。如果您想要 EnumSet 功能,以相同的效率重现它们会有些棘手,但通过一些努力可能是可行的。

因此,在进一步考虑实施之前,我建议您弄清楚您的真正需求是什么。

(编辑:我一直假设这个枚举是完全动态的,即您不知道名称,甚至不知道有多少值。如果名称集是固定的并且您只有 em> 需要从数据库中获取 ID,这是一个非常不同的问题 - 请参阅 Andreas' answer。)

【讨论】:

  • 如果要动态生成代码并在运行时编译,请看BeanShell。
【解决方案2】:

这有点棘手,因为这些值的填充发生在类加载时。因此,您将需要对数据库连接的静态访问。

尽管我很重视他的回答,但我认为 Jon Skeet 这次可能错了。

看看这个:

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

【讨论】:

  • 你假设你在编译时知道会有多少值,以及它们应该被调用什么。如果是这样的话,那很好 - 但我看不出问题中有任何暗示。 (我认为您的查询也应该选择 ID,而不仅仅是 1 :)
  • 一个信息丰富的回复,提醒我们枚举的面向对象方面 - 谢谢,Andreas。
  • (我知道回复晚了)我认为,您可以通过使用反射来填充值来制作这个 DRYer。所以你可以只使用 FIRST() 并在初始化代码中查找名称?
  • getFromDb 方法可能会泄漏连接吗?连接既没有关闭也没有返回到池中。此外,如果rs.close(); 之前抛出异常,statement.close(); 将不会再被调用,而statement.close(); 无论如何都会隐式完成。
【解决方案3】:

what Andreas did的基础上进行了改进,可以将数据库的内容加载到地图中,以减少所需的数据库连接数。

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

【讨论】:

  • 在这一点上提一下这个想法很好。然而,让我们清楚的是,这段代码在任何现实(即多线程)生产环境中都表现得非常糟糕。如果你的枚举在应用内被广泛使用,那么这个初始化代码注定会返回绝对不可靠的值。
【解决方案4】:

枚举不是动态的,所以简短的回答是你不能这样做。

还可以查看 Stack Overflow 问题Dynamic enum in C#

【讨论】:

    【解决方案5】:

    您需要在代码中复制数据库中的内容(反之亦然)。请参阅此question 以获得一些好的建议。

    【讨论】:

      【解决方案6】:

      在我所知道的所有语言中,枚举都是静态的。编译器可以对它们进行一些优化。因此,简短的回答是否定的,你不能。

      问题是为什么要以这种方式使用枚举。你能指望什么? 或者换句话说,为什么不使用集合呢?

      【讨论】:

      • 遗留代码,到处都在使用枚举,现在我们想从数据库中动态加载。如果您可以即时加载枚举,则可以节省大量的重构......
      猜你喜欢
      • 1970-01-01
      • 2018-06-21
      • 2021-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-03
      • 2013-04-15
      相关资源
      最近更新 更多