如何动态创建类文件?

时间:2022-11-25 08:28:23

I want to create a class file dynamically. Here it goes... With the given ResultSet, extracting the metadata I want to build a class file dynamically with getter and setter methods for all the columns that exist in ResultSet. Also I should be able to use this class file generated where ever I want in my later use. Can any body suggest me a better way to implement this. Also if any existing jar files available to implement this, that would be helpful.

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

8 个解决方案

#1


6  

Perhaps Apache Beanutils might suit your requirements?

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

See the section on Dynabeans

请参阅Dynabeans部分

In particular:

尤其是:

3.3 ResultSetDynaClass (Wraps ResultSet in DynaBeans)

3.3 ResultSetDynaClass(在DynaBeans中包装ResultSet)

A very common use case for DynaBean APIs is to wrap other collections of "stuff" that do not normally present themselves as JavaBeans. One of the most common collections that would be nice to wrap is the java.sql.ResultSet that is returned when you ask a JDBC driver to perform a SQL SELECT statement. Commons BeanUtils offers a standard mechanism for making each row of the result set visible as a DynaBean, which you can utilize as shown in this example:

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 (Disconnected ResultSet as DynaBeans)

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

Although ResultSetDynaClass is a very useful technique for representing the results of an SQL query as a series of DynaBeans, an important problem is that the underlying ResultSet must remain open throughout the period of time that the rows are being processed by your application. This hinders the ability to use ResultSetDynaClass as a means of communicating information from the model layer to the view layer in a model-view-controller architecture such as that provided by the Struts Framework, because there is no easy mechanism to assure that the result set is finally closed (and the underlying Connection returned to its connection pool, if you are using one).

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

The RowSetDynaClass class represents a different approach to this problem. When you construct such an instance, the underlying data is copied into a set of in-memory DynaBeans that represent the result. The advantage of this technique, of course, is that you can immediately close the ResultSet (and the corresponding Statement), normally before you even process the actual data that was returned. The disadvantage, of course, is that you must pay the performance and memory costs of copying the result data, and the result data must fit entirely into available heap memory. For many environments (particularly in web applications), this tradeoff is usually quite beneficial.

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

As an additional benefit, the RowSetDynaClass class is defined to implement java.io.Serializable, so that it (and the DynaBeans that correspond to each row of the result) can be conveniently serialized and deserialized (as long as the underlying column values are also Serializable). Thus, RowSetDynaClass represents a very convenient way to transmit the results of an SQL query to a remote Java-based client application (such as an applet).

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

#2


5  

The thing is though - from the sounds of your situation, I understand that you want to create this class at runtime, based on the contents of a ResultSet that you just got back from a database query. This is all well and good, and can be done with bytecode manipulation.

事情是 - 从你的情况的声音,我明白你想在运行时创建这个类,基于你刚刚从数据库查询返回的ResultSet的内容。这一切都很好,可以用字节码操作完成。

However, what benefit do you perceive you will get from this? Your other code will not be able to call any methods on this class (because it did not exist when they were compiled), and consequently the only way to actually use this generated class would be either via reflection or via methods on its parent class or implemented interfaces (I'm going to assume it would extend ResultSet). You can do the latter without bytecode weaving (look at dynamic proxies for arbitrary runtime implementations of an interface), and if you're doing the former, I don't see how having a class and mechanically calling the getFoo method through reflection is better than just calling resultSet.getString("foo") - it will be slower, more clunky and less type-safe.

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

So - are you sure you really want to create a class to achieve your goal?

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

#3


4  

You might want to look at BCEL, although I believe there are other bytecode manipulation libraries available too.

你可能想看看BCEL,虽然我相信还有其他的字节码操作库。

#4


4  

If you're using Java 6 you can write your code and directly call the Java compiler:

如果您使用的是Java 6,则可以编写代码并直接调用Java编译器:

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

You will then have to load said class but you can do that easily enough with a class loader.

然后,您将必须加载所述类,但您可以使用类加载器轻松地完成此操作。

Sadly this is what you have to do in Java.

遗憾的是,这是你在Java中所要做的。

In C# you just use the 'var' type.

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

#5


1  

I'm confused to the way it's supposed to work. And i don't think it's possible. Here's why:

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

If you want to use the class code in the rest of your application, you need an interface (or heavy use of reflection) and that would mean, you know the column types beforehand - defeating the purpose of a generated class.

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

A generated class might * during runtime with another one. If you create a new class for each SQL call, you will have either different classes for the same purpose. And these would probably not even pass a regular call to "equals". You have to look up classes from previously executed statements. And you loose flexibility and/or fill your heap with classes.

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

#6


1  

I've done something probably similar. But I wouldn't create dynamic classes. I had an object called Schema that would load the data of each table I'd need. I had a Table object that would have a Schema type. Each Schema object would have columns attribute while While Table object had attribute with value and reference on Schema column attribute.

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

The Schema had everything you'd need to insert,select,delete,update data to the database.

Schema拥有您需要插入,选择,删除,更新数据到数据库所需的一切。

And I had a mediator that would handle connection between the database and Table object.

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

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

But It could have something to get value on certain column name easily. Anyway, the principle is easy, my could would load data contained in the table information_data it could probably work with a describe too.

但它可以轻松地获得某些列名称的价值。无论如何,原理很简单,我可以加载包含在表information_data中的数据,它也可以用于描述。

I was able to load anytable dynamically as table had dynamic attributes the structure wasn't hardcoded. But there is no real need to create new classes for each table.

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

There was also something that could be important to note. Each table schema were loaded once. Tables only had reference to schemas and schemas had reference to column. column had references to column type etc...

还有一些重要的事情需要注意。每个表模式都加载一次。表只引用了模式和模式引用了列。列引用了列类型等...

It could have been interesting to find a better use than it had. I made that for unit case on database replication. I had no real interest to code a class for each of the 30 tables and do insert/delete/updates and selects. That's the only reason I can see it usefull to create something dynamic about sql. If you don't need to know anything about the tables and only want to insert/delete junk into it.

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

If I had to redo my code, I'd used more associative array.
Anyway Goodluck

如果我不得不重做我的代码,我会使用更多的关联数组。无论如何Goodluck

#7


0  

I second the comments made by dtsazza and Stroboskop; generating a new class at run time is probably not what you want to do in this case.

我是dtsazza和Stroboskop的评论。在运行时生成一个新类可能不是你想要在这种情况下做的。

You haven't really gotten into why you want to do this, but it sounds like you are trying to roll your own Object-Relational mapper. That is a problem that's much harder to get right than it first seems.

你还没有真正理解为什么要这样做,但听起来你正试图推出自己的对象关系映射器。这是一个比最初看起来要难得多的问题。

Instead of building your own system from the down up, you might want to look into existing solutions like Hibernate (high-level system, manages most of you objects and queries for you) or iBatis (a bit more low-level; it handles object mapping, but you still get to write your own SQL).

您可能希望查看现有的解决方案,如Hibernate(高级系统,管理大多数对象和查询)或iBatis(更低级别;它处理对象),而不是从下层构建您自己的系统。映射,但你仍然可以编写自己的SQL)。

#8


0  

I have found that in JSF beans and maps can be used interchangably. Hence for handling results where you don't want to build a complete set of get/setters but just create a h:table, it is much easier to create a list with a map for each line, where the key is the column name (or number) and the value is the column content.

我发现在JSF中bean和map可以互换使用。因此,为了处理你不想构建一套完整的get / setter而只是创建ah:table的结果,创建一个包含每行的映射的列表要容易得多,其中键是列名(或者数字),值是列内容。

If you find it relevant later to make it more typesafe, you can then rework the backend code with beans, and keep your JSF-code unchanged.

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

#1


6  

Perhaps Apache Beanutils might suit your requirements?

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

See the section on Dynabeans

请参阅Dynabeans部分

In particular:

尤其是:

3.3 ResultSetDynaClass (Wraps ResultSet in DynaBeans)

3.3 ResultSetDynaClass(在DynaBeans中包装ResultSet)

A very common use case for DynaBean APIs is to wrap other collections of "stuff" that do not normally present themselves as JavaBeans. One of the most common collections that would be nice to wrap is the java.sql.ResultSet that is returned when you ask a JDBC driver to perform a SQL SELECT statement. Commons BeanUtils offers a standard mechanism for making each row of the result set visible as a DynaBean, which you can utilize as shown in this example:

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 (Disconnected ResultSet as DynaBeans)

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

Although ResultSetDynaClass is a very useful technique for representing the results of an SQL query as a series of DynaBeans, an important problem is that the underlying ResultSet must remain open throughout the period of time that the rows are being processed by your application. This hinders the ability to use ResultSetDynaClass as a means of communicating information from the model layer to the view layer in a model-view-controller architecture such as that provided by the Struts Framework, because there is no easy mechanism to assure that the result set is finally closed (and the underlying Connection returned to its connection pool, if you are using one).

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

The RowSetDynaClass class represents a different approach to this problem. When you construct such an instance, the underlying data is copied into a set of in-memory DynaBeans that represent the result. The advantage of this technique, of course, is that you can immediately close the ResultSet (and the corresponding Statement), normally before you even process the actual data that was returned. The disadvantage, of course, is that you must pay the performance and memory costs of copying the result data, and the result data must fit entirely into available heap memory. For many environments (particularly in web applications), this tradeoff is usually quite beneficial.

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

As an additional benefit, the RowSetDynaClass class is defined to implement java.io.Serializable, so that it (and the DynaBeans that correspond to each row of the result) can be conveniently serialized and deserialized (as long as the underlying column values are also Serializable). Thus, RowSetDynaClass represents a very convenient way to transmit the results of an SQL query to a remote Java-based client application (such as an applet).

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

#2


5  

The thing is though - from the sounds of your situation, I understand that you want to create this class at runtime, based on the contents of a ResultSet that you just got back from a database query. This is all well and good, and can be done with bytecode manipulation.

事情是 - 从你的情况的声音,我明白你想在运行时创建这个类,基于你刚刚从数据库查询返回的ResultSet的内容。这一切都很好,可以用字节码操作完成。

However, what benefit do you perceive you will get from this? Your other code will not be able to call any methods on this class (because it did not exist when they were compiled), and consequently the only way to actually use this generated class would be either via reflection or via methods on its parent class or implemented interfaces (I'm going to assume it would extend ResultSet). You can do the latter without bytecode weaving (look at dynamic proxies for arbitrary runtime implementations of an interface), and if you're doing the former, I don't see how having a class and mechanically calling the getFoo method through reflection is better than just calling resultSet.getString("foo") - it will be slower, more clunky and less type-safe.

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

So - are you sure you really want to create a class to achieve your goal?

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

#3


4  

You might want to look at BCEL, although I believe there are other bytecode manipulation libraries available too.

你可能想看看BCEL,虽然我相信还有其他的字节码操作库。

#4


4  

If you're using Java 6 you can write your code and directly call the Java compiler:

如果您使用的是Java 6,则可以编写代码并直接调用Java编译器:

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

You will then have to load said class but you can do that easily enough with a class loader.

然后,您将必须加载所述类,但您可以使用类加载器轻松地完成此操作。

Sadly this is what you have to do in Java.

遗憾的是,这是你在Java中所要做的。

In C# you just use the 'var' type.

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

#5


1  

I'm confused to the way it's supposed to work. And i don't think it's possible. Here's why:

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

If you want to use the class code in the rest of your application, you need an interface (or heavy use of reflection) and that would mean, you know the column types beforehand - defeating the purpose of a generated class.

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

A generated class might * during runtime with another one. If you create a new class for each SQL call, you will have either different classes for the same purpose. And these would probably not even pass a regular call to "equals". You have to look up classes from previously executed statements. And you loose flexibility and/or fill your heap with classes.

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

#6


1  

I've done something probably similar. But I wouldn't create dynamic classes. I had an object called Schema that would load the data of each table I'd need. I had a Table object that would have a Schema type. Each Schema object would have columns attribute while While Table object had attribute with value and reference on Schema column attribute.

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

The Schema had everything you'd need to insert,select,delete,update data to the database.

Schema拥有您需要插入,选择,删除,更新数据到数据库所需的一切。

And I had a mediator that would handle connection between the database and Table object.

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

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

But It could have something to get value on certain column name easily. Anyway, the principle is easy, my could would load data contained in the table information_data it could probably work with a describe too.

但它可以轻松地获得某些列名称的价值。无论如何,原理很简单,我可以加载包含在表information_data中的数据,它也可以用于描述。

I was able to load anytable dynamically as table had dynamic attributes the structure wasn't hardcoded. But there is no real need to create new classes for each table.

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

There was also something that could be important to note. Each table schema were loaded once. Tables only had reference to schemas and schemas had reference to column. column had references to column type etc...

还有一些重要的事情需要注意。每个表模式都加载一次。表只引用了模式和模式引用了列。列引用了列类型等...

It could have been interesting to find a better use than it had. I made that for unit case on database replication. I had no real interest to code a class for each of the 30 tables and do insert/delete/updates and selects. That's the only reason I can see it usefull to create something dynamic about sql. If you don't need to know anything about the tables and only want to insert/delete junk into it.

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

If I had to redo my code, I'd used more associative array.
Anyway Goodluck

如果我不得不重做我的代码,我会使用更多的关联数组。无论如何Goodluck

#7


0  

I second the comments made by dtsazza and Stroboskop; generating a new class at run time is probably not what you want to do in this case.

我是dtsazza和Stroboskop的评论。在运行时生成一个新类可能不是你想要在这种情况下做的。

You haven't really gotten into why you want to do this, but it sounds like you are trying to roll your own Object-Relational mapper. That is a problem that's much harder to get right than it first seems.

你还没有真正理解为什么要这样做,但听起来你正试图推出自己的对象关系映射器。这是一个比最初看起来要难得多的问题。

Instead of building your own system from the down up, you might want to look into existing solutions like Hibernate (high-level system, manages most of you objects and queries for you) or iBatis (a bit more low-level; it handles object mapping, but you still get to write your own SQL).

您可能希望查看现有的解决方案,如Hibernate(高级系统,管理大多数对象和查询)或iBatis(更低级别;它处理对象),而不是从下层构建您自己的系统。映射,但你仍然可以编写自己的SQL)。

#8


0  

I have found that in JSF beans and maps can be used interchangably. Hence for handling results where you don't want to build a complete set of get/setters but just create a h:table, it is much easier to create a list with a map for each line, where the key is the column name (or number) and the value is the column content.

我发现在JSF中bean和map可以互换使用。因此,为了处理你不想构建一套完整的get / setter而只是创建ah:table的结果,创建一个包含每行的映射的列表要容易得多,其中键是列名(或者数字),值是列内容。

If you find it relevant later to make it more typesafe, you can then rework the backend code with beans, and keep your JSF-code unchanged.

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