代码之家  ›  专栏  ›  技术社区  ›  Makis

用Java动态创建类

  •  25
  • Makis  · 技术社区  · 15 年前

    我试图找到有关这方面的信息,但却空手而归:

    我认为有可能使用反射或代理在Java中动态创建一个类,但我不知道如何使用。我正在实现一个简单的数据库框架,在这里我使用反射创建SQL查询。方法以数据库字段作为参数获取对象,并基于该参数创建查询。但是,如果我也可以动态地创建对象本身,这样就不需要为每个表都有一个简单的数据包装器对象,这将非常有用。

    动态类只需要简单的字段( String ,请 Integer , Double ),例如

    public class Data {
      public Integer id;
      public String name;
    }
    

    这有可能吗?我该怎么做?

    编辑:这是我将如何使用它:

    /** Creates an SQL query for updating a row's values in the database.
     *
     * @param entity Table name.
     * @param toUpdate Fields and values to update. All of the fields will be
     * updated, so each field must have a meaningful value!
     * @param idFields Fields used to identify the row(s).
     * @param ids Id values for id fields. Values must be in the same order as
     * the fields.
     * @return
     */
    @Override
    public String updateItem(String entity, Object toUpdate, String[] idFields,
            String[] ids) {
        StringBuilder sb = new StringBuilder();
    
        sb.append("UPDATE ");
        sb.append(entity);
        sb.append("SET ");
    
        for (Field f: toUpdate.getClass().getDeclaredFields()) {
            String fieldName = f.getName();
            String value = new String();
            sb.append(fieldName);
            sb.append("=");
            sb.append(formatValue(f));
            sb.append(",");
        }
    
        /* Remove last comma */
        sb.deleteCharAt(sb.toString().length()-1);
    
        /* Add where clause */
        sb.append(createWhereClause(idFields, ids));
    
        return sb.toString();
    }
     /** Formats a value for an sql query.
     *
     * This function assumes that the field type is equivalent to the field
     * in the database. In practice this means that this field support two
     * types of fields: string (varchar) and numeric.
     *
     * A string type field will be escaped with single parenthesis (') because
     * SQL databases expect that. Numbers are returned as-is.
     *
     * If the field is null, a string containing "NULL" is returned instead.
     * 
     * @param f The field where the value is.
     * @return Formatted value.
     */
    String formatValue(Field f) {
        String retval = null;
        String type = f.getClass().getName();
        if (type.equals("String")) {
            try {
                String value = (String)f.get(f);
                if (value != null) {
                    retval = "'" + value + "'";
                } else {
                    retval = "NULL";
                }
            } catch (Exception e) {
                System.err.println("No such field: " + e.getMessage());
            }
        } else if (type.equals("Integer")) {
            try {
                Integer value = (Integer)f.get(f);
                if (value != null) {
                    retval = String.valueOf(value);
                } else {
                    retval = "NULL";
                }
            } catch (Exception e) {
                System.err.println("No such field: " + e.getMessage());
            }
        } else {
            try {
                String value = (String) f.get(f);
                if (value != null) {
                    retval = value;
                } else {
                    retval = "NULL";
                }
            } catch (Exception e) {
                System.err.println("No such field: " + e.getMessage());
            }
        }
        return retval;
    }
    
    5 回复  |  直到 8 年前
        1
  •  19
  •   Bozho    15 年前

    可以生成类(通过 cglib ,请 asm , javassist , bcel 但是你不应该那样做。为什么?

    • 使用库的代码应为预期类型 Object 利用反射得到所有的场-不是个好主意
    • Java是静态类型的语言,你想引入动态类型——它不是地方。

    如果您只希望数据是未定义的格式,那么您可以在数组中返回它,比如 Object[] Map<String, Object> 如果您希望命名它们,并从中获取它们——它将为您节省大量不必要的类生成的麻烦,而这些类生成的唯一目的是包含一些将通过反射获得的数据。

    您可以改为使用预定义的类来保存数据,并将它们作为参数传递给查询方法。例如:

     public <T> T executeQuery(Class<T> expectedResultClass, 
          String someArg, Object.. otherArgs) {..}
    

    因此,您可以使用对传递的 expectedResultClass 创建该类型的新对象并用查询结果填充它。

    也就是说,我认为您可以使用现有的东西,比如ORM框架(Hibernate、EclipseLink)、Spring JdbcTemplate 等。

        2
  •  25
  •   Amir Afghani    15 年前

    实现这一点有许多不同的方法(例如代理、ASM),但最简单的方法是,当原型制作时可以从以下方法开始:

    import java.io.*;
    import java.util.*;
    import java.lang.reflect.*;
    
    public class MakeTodayClass {
      Date today = new Date();
      String todayMillis = Long.toString(today.getTime());
      String todayClass = "z_" + todayMillis;
      String todaySource = todayClass + ".java";
    
      public static void main (String args[]){
        MakeTodayClass mtc = new MakeTodayClass();
        mtc.createIt();
        if (mtc.compileIt()) {
           System.out.println("Running " + mtc.todayClass + ":\n\n");
           mtc.runIt();
           }
        else
           System.out.println(mtc.todaySource + " is bad.");
        }
    
      public void createIt() {
        try {
          FileWriter aWriter = new FileWriter(todaySource, true);
          aWriter.write("public class "+ todayClass + "{");
          aWriter.write(" public void doit() {");
          aWriter.write(" System.out.println(\""+todayMillis+"\");");
          aWriter.write(" }}\n");
          aWriter.flush();      
          aWriter.close();
          }
        catch(Exception e){
          e.printStackTrace();
          }
        }
    
      public boolean compileIt() {
        String [] source = { new String(todaySource)};
        ByteArrayOutputStream baos= new ByteArrayOutputStream();
    
        new sun.tools.javac.Main(baos,source[0]).compile(source);
        // if using JDK >= 1.3 then use
        //   public static int com.sun.tools.javac.Main.compile(source);    
        return (baos.toString().indexOf("error")==-1);
        }
    
      public void runIt() {
        try {
          Class params[] = {};
          Object paramsObj[] = {};
          Class thisClass = Class.forName(todayClass);
          Object iClass = thisClass.newInstance();
          Method thisMethod = thisClass.getDeclaredMethod("doit", params);
          thisMethod.invoke(iClass, paramsObj);
          }
        catch (Exception e) {
          e.printStackTrace();
          }
        }
    }
    
        3
  •  3
  •   JeeBee    15 年前

    为每个表创建一个数据模型类需要几分钟的时间,您可以使用类似hibernate的ORM或编写自己的JDBCDAOS轻松映射到数据库。这比深入思考要容易得多。

    您可以创建一个实用程序来查询表的数据库结构,并为您创建数据模型类和DAO。或者,您可以在Java中创建模型,并创建一个实用工具来创建数据库模式和DAO(使用反射和Java 5注释来辅助)。别忘了javaFieldNames通常与数据库的列名称不同。

        4
  •  0
  •   Bill K    15 年前

    我也不会尝试这个。类既是数据又是代码,您计划将哪种代码与动态数据关联起来?

    你可能想要一个收藏——或者冬眠。

    你可以用这个系列玩很多把戏,让它做你想做的。您可以使用确保其类型或不为空的数据将对象包装在元对象中,而不是将对象直接放置到集合中。您可以将整个集合包装在一个类中,该类强制实现类型安全性、完整性和关系。我甚至让我的集合能够接受“验证器”类来验证分配的数据。

    验证、结构和初始条目可以来自数据库、XML或代码。

        5
  •  0
  •   Graham Russell Drew Wills    9 年前

    这是可能的,但(我相信)你需要 ASM BCEL .

    或者,你可以使用一些更有力量的东西(比如 Groovy )