用Java动态创建类
我曾试图找到有关这方面的信息,但却空手而归: 我推测可以使用反射或代理在Java中动态创建一个类,但我不知道如何创建。我正在实现一个简单的数据库框架,其中我使用反射创建SQL查询。该方法获取数据库字段作为参数的对象,并基于该参数创建查询。但是,如果我还可以动态创建对象本身,那么它将非常有用,这样我就不需要为每个表都创建一个简单的数据包装器对象 动态类只需要简单的字段(用Java动态创建类,java,reflection,Java,Reflection,我曾试图找到有关这方面的信息,但却空手而归: 我推测可以使用反射或代理在Java中动态创建一个类,但我不知道如何创建。我正在实现一个简单的数据库框架,其中我使用反射创建SQL查询。该方法获取数据库字段作为参数的对象,并基于该参数创建查询。但是,如果我还可以动态创建对象本身,那么它将非常有用,这样我就不需要为每个表都创建一个简单的数据包装器对象 动态类只需要简单的字段(String,Integer,Double),例如 这可能吗?我该怎么做 编辑:我将这样使用: /** Creates an SQ
String
,Integer
,Double
),例如
这可能吗?我该怎么做
编辑:我将这样使用:
/** 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;
}
有许多不同的方法可以实现这一点(例如代理、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();
}
}
}
这是可能的,但(我相信)你需要类似或的东西
或者,您可以使用更强大的功能(如)。可以生成类(通过,,),但不应该这样做。为什么?
- 使用库的代码应该使用类型
,并使用反射获取所有字段-这不是一个好主意Object
- java是静态类型化语言,您希望引入动态类型化——这不是一个合适的地方
Object[]
,或者Map
(如果您希望命名它们),然后从那里获取数据-这将为您节省大量不必要的类生成的麻烦,而这些类的唯一目的是包含一些将通过反射获得的数据
相反,您可以使用预定义的类来保存数据,并将它们作为参数传递给查询方法。例如:
public <T> T executeQuery(Class<T> expectedResultClass,
String someArg, Object.. otherArgs) {..}
public T executeQuery(类expectedResultClass,
字符串someArg,Object..otherArgs){..}
因此,您可以对传递的expectedResultClass
使用反射来创建该类型的新对象,并用查询结果填充它
也就是说,我认为您可以使用现有的一些东西,比如ORM框架(Hibernate、EclipseLink)、spring的
JdbcTemplate
,等等。为每个表创建一个数据模型类需要几分钟,您可以使用类似Hibernate的ORM或编写自己的JDBC DAO轻松地将其映射到数据库。这比深入思考要容易得多
您可以创建一个实用程序来查询表的数据库结构,并为您创建数据模型类和DAO。或者,您可以用Java创建模型,并创建一个实用程序来创建数据库模式和DAO(使用反射和Java5注释来辅助)。不要忘记JavaFieldName通常与数据库列名不同。我知道反射的性能缺点,但对于我的小项目,我需要它,我创建了一个项目库,将JSON转换为Java,然后在JVM上下文中最终转换为.class 任何需要这种东西的人都可以查看my,它需要JDK来编译代码
顺便提一下,我建议使用这种方法,因为您生成的类看起来很简单。我找不到sun.tools.javac.Main或com.sun.tools.javac。我在哪里可以找到这些?请尝试:导入com.sun.tools.javac.Main并告诉我这是否有效现在有Java编译器API aka JSR-199(类来自
javax.tool.*
)。但我不认为这是一条正确的道路。@AmirAfghani无法使用它。这个导入(import com.sun.tools.javac.Main)返回的包不存在。我的想法是避免有大量只包含简单字段的类。动态创建类要简单得多。代码要少得多,与拥有所有这些类一样清晰。@Makis但是您将如何使用生成的类呢?在编写代码时,你不能对它们进行强制转换,也不能知道它们的字段是什么。正如我所写的,我将使用反射来确定该类的字段-这就是我需要的所有信息。我将查看每个字段的类型、名称和值。我将把这个例子添加到上面的问题中。@Makis检查我的更新-如果您将要使用的数据不需要任何预定义的表单,您可以只填充一个对象数组(object[]
),而不需要类生成和反射的所有麻烦如果您需要将字段名映射到值,为什么不使用HashMap呢?我不认为Java是实现这一点的合适工具,Groovy更适合IMO。就我个人而言,我不认为拥有映射到数据库模型的Java模型有什么问题。另外,在创建SQL查询时,请确保使用PreparedStatements来避免SQL注入,而不是构建SQL字符串。
public <T> T executeQuery(Class<T> expectedResultClass,
String someArg, Object.. otherArgs) {..}