我想动态创建一个类文件。在这里...... 使用给定的ResultSet,使用getter和setter方法为ResultSet中存在的所有列动态提取我想要构建类文件的元数据。此外,我应该能够在以后的使用中使用我想要的类文件。 任何机构都可以建议我更好地实现这一点。此外,如果任何现有的jar文件可用于实现此功能,那将会很有帮助。
答案 0 :(得分:6)
也许Apache Beanutils可能符合您的要求?
请参阅Dynabeans
部分特别是:
3.3 ResultSetDynaClass(包装DynaBeans中的ResultSet)
DynaBean API的一个非常常见的用例是包装其他通常不作为JavaBeans出现的“东西”集合。最好的包装之一是当您要求JDBC驱动程序执行SQL SELECT语句时返回的java.sql.ResultSet。 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查询的结果表示为一系列DynaBeans,但一个重要的问题是底层ResultSet必须在应用程序处理行的整个时间段内保持打开状态。这阻碍了使用ResultSetDynaClass作为在模型 - 视图 - 控制器体系结构(例如Struts框架提供的体系结构)中从模型层向视图层传递信息的方法的能力,因为没有简单的机制来确保结果集最后关闭(如果您使用的是连接池,则返回其连接池)。
RowSetDynaClass类表示解决此问题的另一种方法。构造此类实例时,底层数据将复制到一组表示结果的内存中DynaBeans中。当然,这种技术的优点是,您可以立即关闭ResultSet(和相应的Statement),通常在您处理返回的实际数据之前。当然,缺点是您必须支付复制结果数据的性能和内存成本,结果数据必须完全适合可用的堆内存。对于许多环境(特别是在Web应用程序中),这种权衡通常非常有用。
作为额外的好处,RowSetDynaClass类被定义为实现java.io.Serializable,因此它(以及对应于结果的每一行的DynaBeans)可以方便地序列化和反序列化(只要基础列值也是可序列化的)。因此,RowSetDynaClass表示将SQL查询的结果传输到基于Java的远程客户端应用程序(例如applet)的非常方便的方法。
答案 1 :(得分:5)
事情是 - 从你的情况的声音,我明白你想在运行时创建这个类,基于你刚刚从数据库查询返回的ResultSet的内容。这一切都很好,可以用字节码操作完成。
然而,您认为您会从中获得什么好处?您的其他代码将无法调用此类上的任何方法(因为它们在编译时不存在),因此实际使用此生成的类的唯一方法是通过反射或通过其父类的方法或实现接口(我将假设它将扩展ResultSet)。您可以在没有字节码编织的情况下执行后者(查看dynamic proxies以获取接口的任意运行时实现),如果您正在执行前者,我不会看到如何使用类并机械地调用{{1通过反射的方法比调用getFoo
更好 - 它会更慢,更笨重和更少类型安全。
那么 - 你确定你真的想创建一个课程来实现你的目标吗?
答案 2 :(得分:4)
您可能希望查看BCEL,但我相信还有其他字节码操作库可用。
答案 3 :(得分: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'类型。
答案 4 :(得分:1)
我对它的工作方式感到困惑。我不认为这是可能的。 原因如下:
如果要在应用程序的其余部分中使用类代码,则需要一个接口(或大量使用反射),这意味着,您事先知道列类型 - 否定了生成类的目的。< / p>
生成的类可能在运行时与另一个类发生冲突。 如果为每个SQL调用创建一个新类,则可以使用不同的类来实现相同的目的。而这些甚至可能不会通过常规调用“等于”。 您必须从以前执行的语句中查找类。你松散了灵活性和/或用课程填满你的堆。
答案 5 :(得分:1)
我做过类似的事情。但我不会创建动态类。 我有一个名为Schema的对象,它将加载我需要的每个表的数据。 我有一个具有Schema类型的Table对象。每个Schema对象都有columns属性,而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();
但它可以轻松获得某些列名称的价值。 无论如何,原理很简单,我可以加载包含在表information_data中的数据,它也可以用于描述。
我能够动态加载任何表,因为表具有动态属性,结构没有硬编码。但是没有必要为每个表创建新类。
还有一些事情值得注意。每个表架构都加载了一次。表只引用了模式和模式引用了列。列引用了列类型等...
找到比它更好的用途本来是有趣的。我为数据库复制的单元案例做了这个。我没有兴趣为30个表中的每个表编写一个类,并插入/删除/更新和选择。这是我可以看到它创建有关sql动态的有用的唯一原因。如果您不需要了解有关表的任何信息,只想插入/删除垃圾。
如果我不得不重做我的代码,我会使用更多的关联数组 无论如何Goodluck
答案 6 :(得分:0)
我赞成dtsazza和Stroboskop的评论;在运行时生成一个新类可能不是你想要做的事情。
你还没有真正进入为什么你想要这样做,但听起来你正试图推出自己的Object-Relational mapper。这个问题比最初看起来要难得多。
您可能希望查看现有的解决方案,例如Hibernate(高级系统,为您管理大部分对象和查询)或iBatis,而不是自下而上构建您自己的系统。 (更低级别;它处理对象映射,但您仍然可以编写自己的SQL)。
答案 7 :(得分:0)
我发现在JSF中bean和map可以互换使用。因此,为了处理你不想构建一套完整的get / setter而只是创建ah:table的结果,可以更容易地为每一行创建一个带有地图的列表,其中键是列名(或者数字),值是列内容。
如果您以后发现它更加类型安全,那么您可以使用bean重新编写后端代码,并保持JSF代码不变。