views:

1063

answers:

8

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.

+4  A: 

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

Jon Skeet
I've used BCEL. It's very simple to use. Recommended.
zvikico
+6  A: 

Perhaps Apache Beanutils might suit your requirements?

See the section on Dynabeans

In particular:

3.3 ResultSetDynaClass (Wraps ResultSet in DynaBeans)

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:

  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)

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).

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.

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).

toolkit
+3  A: 

If you're using Java 6 you can write your code and 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();

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.

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

cletus
+4  A: 

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.

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.

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

Andrzej Doyle
+1  A: 

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 clash 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.

Stroboskop
+1  A: 

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.

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

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

   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.

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.

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

Sybiam
A: 

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.

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).

fred-o
A: 

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.

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.

Thorbjørn Ravn Andersen