【问题标题】:How can I create a class file dynamically?如何动态创建类文件?
【发布时间】:2010-10-08 01:51:30
【问题描述】:

我想动态创建一个类文件。来了…… 使用给定的 ResultSet,提取元数据,我想使用 getter 和 setter 方法为 ResultSet 中存在的所有列动态构建一个类文件。此外,我应该能够在以后使用的任何地方使用生成的这个类文件。 任何机构都可以建议我更好的方法来实现这一点。此外,如果有任何现有的 jar 文件可用于实现这一点,那将很有帮助。

【问题讨论】:

  • 您可能需要考虑稍微不同地实现它。使用与 ResultSet 的字段匹配的参数调用方法。 IIRC

标签: java class dynamic class-design javabeans


【解决方案1】:

也许Apache Beanutils 可能符合您的要求?

参见Dynabeans部分

特别是:

3.3 ResultSetDynaClass(在DynaBeans中包装ResultSet)

DynaBean API 的一个非常常见的用例是包装其他通常不会以 JavaBeans 形式出现的“东西”集合。最好包装的最常见的集合之一是 java.sql.ResultSet,当您要求 JDBC 驱动程序执行 SQL SELECT 语句时,它会返回。 Commons BeanUtils 提供了一种标准机制,可以使结果集的每一行都作为 DynaBean 可见,您可以使用它,如下例所示:

  Connection conn = ...;
  Statement stmt = conn.createStatement();
  ResultSet rs = stmt.executeQuery
    ("select account_id, name from customers");
  Iterator rows = (new ResultSetDynaClass(rs)).iterator();
  while (rows.hasNext()) {
    DynaBean row = (DynaBean) rows.next();
    System.out.println("Account number is " +
                       row.get("account_id") +
                       " and name is " + row.get("name"));
  }
  rs.close();
  stmt.close();

3.4 RowSetDynaClass(断开连接的ResultSet作为DynaBeans)

虽然 ResultSetDynaClass 是一种非常有用的技术,可以将 SQL 查询的结果表示为一系列 DynaBean,但一个重要的问题是底层 ResultSet 必须在应用程序处理行的整个时间段内保持打开状态。这阻碍了使用 ResultSetDynaClass 作为在模型-视图-控制器架构(例如 Struts 框架提供的架构)中将信息从模型层传递到视图层的能力,因为没有简单的机制来确保结果集最终关闭(如果您使用的是连接池,则底层连接返回到其连接池)。

RowSetDynaClass 类代表了解决此问题的不同方法。当您构造这样一个实例时,底层数据被复制到一组表示结果的内存中 DynaBeans。当然,这种技术的优点是您可以立即关闭 ResultSet(和相应的 Statement),通常在您处理返回的实际数据之前。当然,缺点是您必须支付复制结果数据的性能和内存成本,并且结果数据必须完全适合可用的堆内存。对于许多环境(尤其是在 Web 应用程序中),这种权衡通常是非常有益的。

作为一个额外的好处,定义了 RowSetDynaClass 类来实现 java.io.Serializable,这样它(以及与结果的每一行对应的 DynaBeans)可以方便地进行序列化和反序列化(只要底层列值也是可序列化的)。因此,RowSetDynaClass 代表了一种将 SQL 查询结果传输到远程基于 Java 的客户端应用程序(例如小程序)的非常方便的方法。

【讨论】:

    【解决方案2】:

    问题是 - 从你的情况来看,我知道你想在运行时创建这个类,基于你刚刚从数据库查询中返回的 ResultSet 的内容。这一切都很好,可以通过字节码操作来完成。

    但是,您认为您会从中获得什么好处?您的其他代码将无法调用此类上的任何方法(因为它们在编译时不存在),因此实际使用此生成的类的唯一方法是通过反射或通过其父类上的方法或实现的接口(我假设它会扩展 ResultSet)。您可以在没有字节码编织的情况下执行后者(查看dynamic proxies 以获取接口的任意运行时实现),如果您正在执行前者,我不知道如何拥有一个类并通过机械方式调用getFoo 方法反射比仅仅调用resultSet.getString("foo") 更好——它会更慢、更笨重并且不那么安全。

    那么 - 你确定你真的想创建一个类来实现你的目标吗?

    【讨论】:

      【解决方案3】:

      您可能想查看BCEL,尽管我相信还有其他可用的字节码操作库。

      【讨论】:

      • 我用过 BCEL。使用起来非常简单。推荐。
      【解决方案4】:

      如果您使用的是 Java 6,您可以编写代码和directly call the Java compiler

         Files[] files1 = ... ; // input for first compilation task
         Files[] files2 = ... ; // input for second compilation task
      
         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
         StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
      
         Iterable<? extends JavaFileObject> compilationUnits1 =
             fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files1));
         compiler.getTask(null, fileManager, null, null, null, compilationUnits1).call();
      
         Iterable<? extends JavaFileObject> compilationUnits2 =
             fileManager.getJavaFileObjects(files2); // use alternative method
         // reuse the same file manager to allow caching of jar files
         compiler.getTask(null, fileManager, null, null, null, compilationUnits2).call();
      
         fileManager.close();
      

      然后您必须加载该类,但您可以使用类加载器轻松完成。

      遗憾的是,这是您在 Java 中必须做的事情。

      在 C# 中,您只需使用 'var' 类型。

      【讨论】:

        【解决方案5】:

        我对它的工作方式感到困惑。而且我认为这是不可能的。 原因如下:

        如果您想在应用程序的其余部分使用类代码,则需要一个接口(或大量使用反射),这意味着您事先知道列类型 - 破坏了生成类的目的。

        生成的类可能在运行时与另一个类发生冲突。 如果您为每个 SQL 调用创建一个新类,您将拥有用于相同目的的不同类。这些可能甚至不会通过对“等于”的常规调用。 您必须从以前执行的语句中查找类。而且你失去了灵活性和/或用类填满了你的堆。

        【讨论】:

          【解决方案6】:

          我做过类似的事情。但我不会创建动态类。 我有一个名为 Schema 的对象,它可以加载我需要的每个表的数据。 我有一个具有 Schema 类型的 Table 对象。每个 Schema 对象都将具有列属性,而 Table 对象具有具有值的属性和对 Schema 列属性的引用。

          Schema 拥有您在数据库中插入、选择、删除、更新数据所需的一切。

          我有一个调解器来处理数据库和 Table 对象之间的连接。

             Table t = new Table('Dog');   
             t.randomValue(); // needed for the purpose of my project   
             t.save();    
             Table u = Table.get(t);
             u.delete();
          

          但它可以很容易地获得某些列名的价值。 无论如何,原理很简单,我可以加载 table information_data 中包含的数据,它可能也可以与 describe 一起使用。

          我能够动态加载任何表,因为表具有动态属性,结构没有硬编码。但是没有真正需要为每个表创建新的类。

          还有一些值得注意的地方。每个表模式都加载一次。表只引用了模式,而模式引用了列。列引用了列类型等...

          找到比它更好的用途可能会很有趣。我为数据库复制的单元案例做了这个。我没有真正的兴趣为 30 个表中的每一个编写一个类并进行插入/删除/更新和选择。这是我认为创建有关 sql 的动态内容很有用的唯一原因。如果您不需要了解有关表格的任何信息,而只想在其中插入/删除垃圾。

          如果我必须重做我的代码,我会使用更多的关联数组。
          总之祝你好运

          【讨论】:

            【解决方案7】:

            我支持 dtsazza 和 Stroboskop 制作的 cmets;在这种情况下,在运行时生成一个新类可能不是您想要做的。

            您还没有真正了解为什么要这样做,但听起来您正在尝试推出自己的Object-Relational mapper。这是一个比最初看起来更难解决的问题。

            与其自下而上构建自己的系统,不如研究现有的解决方案,例如Hibernate(高级系统,为您管理大部分对象和查询)或iBatis(更多低级;它处理对象映射,但您仍然可以编写自己的 SQL)。

            【讨论】:

              【解决方案8】:

              我发现在 JSF 中 bean 和映射可以互换使用。因此,对于处理您不想构建完整的 get/setter 集而只创建 ah:table 的结果,为每一行创建一个带有映射的列表要容易得多,其中键是列名(或number),值为列内容。

              如果您以后发现它相关以使其更加类型安全,那么您可以使用 bean 重新编写后端代码,并保持您的 JSF 代码不变。

              【讨论】:

                猜你喜欢
                • 2018-11-01
                • 1970-01-01
                • 1970-01-01
                • 2013-11-03
                • 2019-11-12
                • 2019-02-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多