views:

1688

answers:

5

I'm seeking an efficient way to display an SQL table queried via Hibernate in a JTable.

    Query q = em.createNamedQuery("Files.findAll");
    List rl = q.getResultList();

It would probably be preferable to use the List returned by that (In this case, that would make a list of Files objects (where Files is an internal class, not java.io.File)), but I won't be picky as long as it is neat.

I have one answer I worked up below, but that doesn't work very well. I'd going to end up having to write a TableModel for it if I keep going down this path.

+2  A: 

There are a lots and lots of ways to do this, but are you looking for something that would automatically figure out the columns or what? If you used the java reflection pieces you can read the Hibernate annotations to find out the column names and populate the JTable that way...

Otherwise this is just a straight forward piece of code that a. creates a JTable and TableModel, and b. populates the display with the database data.

EDIT: I think this example may cover walking the annotation tree and processing them. The specifics are the AnnotationProcessorFactory part iirc.

EDIT 2: I also found this library which is built to help lookup annotations at runtime. One of their examples is looking up Entity classes in hibernate to build a resource list - I believe you could do something similar to find classes that that implement @column, or @basic etc. This should allow you via reflection to pretty easily do it, but as I said java's standard library already provides the ability to walk the annotation tree to find out the column names - at which point creating the JTable from that should be very easy to do in a programmatic way.

EDIT 3: This code is all that and a bag of chips! From here you should easily be able to walk the list of maps and pull out all of the info you want, the value, its class type, the field name for the column headers... Note that it isn't particularly safe.. I've dropped out all of the error code I did while testing to keep it short...

List<Map> createTable(List queryResults) {
    List<Map> r = new LinkedList<Map>();
    for (Object o : queryResults) {
         r.add(entityMap(o));
    }
    return r;
}

Map entityMap(Object obj) throws Throwable {
    Map m = new HashMap();
    for (Field field : getFields(obj.getClass())) {
        Method method = getMethod(field);
        Object value = method.invoke(obj);
        m.put(field, value);
    }
    return m;
}

List<Field> getFields(Class<?> clazz) {
    List<Field> fields = new LinkedList<Field>();

    for (Field field : clazz.getDeclaredFields()) {
        Column col = field.getAnnotation(Column.class);
        if (col != null)
            fields.add(field);
    }
    return fields;
}

Method getMethod(Field field) throws NoSuchMethodException {
    Class<?> clazz = field.getDeclaringClass();
    String name = "get" + uppercase(field.getName());
    Method method = clazz.getMethod(name);
    return method;
}

String uppercase(String str) {
    return str.substring(0,1).toUpperCase() + str.substring(1);
}
Petriborg
It'd be nice to have it automatically figure out the columns. I did note that I can do List.toArray() and then attach toArray() to the Files class as well. Seems messy allocation-wise. A reflection setup may be wise.
Autocracy
A: 

Well, here's what I ended up doing for now:

//client-side class
public String[][] getFilesArray() {
    List<Files> files = remote.getFiles();
    String[][] x = new String[files.size()][];
    for (int i = 0; i < files.size(); i++) {
        x[i] = files.get(i).getStringArray();
    }
    return x;
}


//DAO class
public String[] getStringArray() {
    return new String[] {
        fileid.toString(),
        name,
        DateFormat.getInstance().format(timestamp),
        status,
        hash
    };
}

public static String[] getColumnNames() {
    return new String[] {
        "Id",
        "Name",
        "Timestamp",
        "Status",
        "Hash"
    };
}
Autocracy
+1  A: 

Did you take a look at the org.hibernate.metadata classes. These provide you metadata information about classes and collections. You can also make calls to SessionFactory.getClassMetadata(Class) to get the metadata information for the class in question.

+1  A: 

In the answer below I expect that your HQL returns not a list of objects, but a list of arrays of necessary properties that you wish to show in JTable (i.e. that you're using so calling report queries).

In that case you can write simple TableModelAdapter that will be used as a TableModel for JTable.

public class TableModelAdapter extends AbstractTableModel{

    private List<Object[]> list;

    public TableModelAdapter(List<Object[]> aList){
     list = aList;
    }
    public int getColumnCount() {
     if (list == null){
      return 0;
     }
     if (list.size() == 0){
      return 0;
     }
     return list.get(0).length;
    }

    public int getRowCount() {
     if (list == null){
      return 0;
     }
     return list.size();
    }

    public Object getValueAt(int row, int column) {
     if (list == null){
      return null;
     }
     return list.get(row)[column];
    }
}

If you have to return list of objects we can change the example and path throw properties via reflection instead of array.

FoxyBOA
Briefly, everything right now is returned as an object. Having strongly avoided reflection until now, I ask how you would do that.
Autocracy
The secret is in Hibernate report query. It works only with HQL and construct for you not a whole object, but an array of requested properties. And if you ask a .list() of them, you'll get List<Object[]> which was used in an example.
FoxyBOA
A: 

JIDE Data Grids provides a HibernateTableModel that could provide the functionality you are looking for if you are happy to buy a third party library.

Mark