Java Tips Weblog

  • Blog Stats

    • 2,569,644 hits
  • Categories

  • Archives

Bean Table Model

Posted by Rob Camick on November 27, 2008

The BeanTableModel is a concrete implementation of the RowTableModel introduced in a previous entry. It is used to display a bean in a row of a table. The model uses reflection to determine which properties of the bean should be displayed in the table.

The model will search the bean for all getter methods, (ie. method name starts with “get” or “is”) that require zero parameters to invoke the method. When a method described above is found, it will then check for a setter method (ie. method name starts with “set”) of the same name that requires a single parameter. This parameter must be of the same type returned by the getter method. These getter/setter methods will be used in the implementation the getValueAt() and setValueAt() methods of the model.

The method name will be used to create the column name for each method. Basically the prefix will be removed and a space will be inserted before each capital character of the method name. The return type of the getter methods will be used to set the column class of each column. Also, the default editability of the column can be set based on the presence of a setter method or not.

If we assume the existence of a simple bean, lets call it Person, with the following method signatures:

  • String getFirstName()
  • setFirstName(String firstName)
  • String getLastName()
  • setLastName(String lastName)
  • setBirthdate(Date)
  • int getAge()

…then the model would contain 3 columns:

  • First Name – of class String and is editable
  • Last Name – of class String and is editable
  • Age – of class Integer and is not editable

You could manually set the First Name and Last Name columns to be non editable, but you cannot make the Age editable. To create a table using the Person bean your code would be:

BeanTableModel model = new BeanTableModel(Person.class);
JTable table = new JTable(model);

Unfortunately we need to use the Person.class as a parameter because reflection is used to find the getter/setter methods and Generics do not provide this information even though the appropriate type was used to create the class.

The above constructor allows us to find all the getter/setter methods defined in the specified class. However, many beans extend from a class other than Object, in which case you may want to see all the inherited properties in the model as well. In this case you will need to use the constructor that allows you to specify an ancestor class as well. Maybe something like this:

BeanTableModel model = new BeanTableModel(JButton.class, Component.class);
JTable table = new JTable(model);

Of course if you do execute the above code you will find that the table model contains 100 columns of data. Chances are you are not interested in all the columns, so you will have two choices:

  • remove columns from the table view by using the removeColumn(…) method of JTable. This is a convenience method for the remove(…) method of TableColumnModel. For example to remove the Age column you could use:
    table.removeColumn( table.getColumn( "Age" ) );
  • create a custom TableModel for the bean to only disply the columns you wish to see

I have included JButtonTableModel as a quick demo of how you might create such a custom model. The code is executable and contains code to create a table using the BeanTableModel or the JButtonTableModel so you can see the difference. Now the choice is up to you. In some situations you may want to use the BeanTableModel for its convenience. In other cases the small amount of work to create a custom model will pay off in the long run.

Note: as has been mentioned in the comments below the table column ordering cannot be controlled when using the BeanTableModel. Check out the new Table Column Reordering entry for a simple solution to reorder columns after the table has been created. The solution was based on the suggestion provided below. Thanks Andre.

Get The Code

BeanTableModel.java
RowTableModel.java
JButtonTableModel.java

See Also

Row Table Model
Table Column Reordering

18 Responses to “Bean Table Model”

  1. Andre Uhres said

    Is there any means to influence column order when coding the bean? Example: we have a bean with fields “name” and “phone” and the JTable should show the corresponding columns in the same order (first column = “name”, second column = “phone”). Of course we can do a moveColumn on the ColumnModel after having set the model. However, I think it would be nice if we could state the initial ordering within the bean itself.

    • Rob Camick said

      Not that I know of. The API for the getMethods() method of the Class class says: “The elements in the array returned are not sorted and are not in any particular order”.

      Like you the only solution I could think of is to manually order the columns using the moveColumn() method. I have thought about creating a static helper method that would take a table and an array of columns names as parameters and then invoke the moveColumn() method to order each column.

  2. Andre Uhres said

    This seems to work well:

    /**
    * Typical call example:
    * BeanTableDemo.reorderColumnsInTable(Person.ORDER, table);
    * the first argument being an array of column names
    */
    public static void reorderColumnsInTable(final String[] order, final JTable table) {
      TableColumnModel model = table.getColumnModel();
      for (int newIndex = 0; newIndex < order.length; newIndex++) {
        String columnName = order[newIndex];
        int index = model.getColumnIndex(columnName);
        if (index != newIndex) {
          model.moveColumn(index, newIndex);
        }
      }
    }

  3. Rob Camick said

    I removed your other attempt at formatting the code. I’ve also noted that the code tags don’t seem to work. For all my postings (and the code you posted above) I manually enter the HTML space entity code “@nbsp;”.

    Anyway, thanks for the code. I’ll look at including it in my RXTable class in a few days.

  4. Anonymous said

    The “Typical call example” above should read:
    BeanTableModel.reorderColumnsInTable(Person.ORDER, table);
    (In fact I thought about including the method in the BeanTableModel and not in BeanTableDemo, which is only my frame class.)

  5. janny said

    Hi, thx for this useful code. I use it in my JTables and it works perfectly.
    I’m quite new to Swing devel. and I have problems trying to get back a Bean from the table after a column sorting (I use swingx JXTable component that comes with default column sorting features). For example, following code returns the wrong Person after the table order has changed:

    int sel = table.getSelectedRow();
    PersonTableModel pertm = (PersonTableModel) table.getModel();
    Person per = pertm.getRow(sel); //<- wrong Person

    Can you point me in the right direction to fix this?
    Thx

    • Rob Camick said

      Yes, the selected index represents the row in the table view, not the row from the TableModel. When using JTable you would use:

      int sel = tbl.convertRowIndexToModel(tbl.getSelectedRow());

      I assume JXTable works the same way.

  6. Francisco said

    Hi Camick,

    This is very useful, I was looking for something like this. Thank you!

  7. kelmer44 said

    There is a bug in this function:

    public void setColumnClass(int column, Class columnClass)
    {
    	columnClasses[column] = columnClass;
    	fireTableRowsUpdated(0, getColumnCount() - 1);
    }
    

    Correct me if I’m wrong, but it should read

    fireTableRowsUpdated(0, getRowCount()-1);
    
  8. Rob Camick said

    That makes sense. The source code has been updated. Thanks.

  9. candicecold said

    can we flip this whole thing such that rowheaders and multiple row values are added instead of columns

    nice blog and lot of useful stuff to learn.

  10. candicecold said

    Can we print the values veritcally in jable rather than horizontal without the row headers , basically two columns with header values “Name” and “Description” with name columns having the name of the headers and description column having the values. Just a thought. I mean creating another method in beansTablemodel to buffer the data . set a data design and then show it that way

  11. Anonymous said

    Obviously this is quite an old post now but still very useful! Regarding the idea of being able to stop fields from the bean appearing in the table, one solution is to create an interface containing all getters necessary for your table structure, ensure that your bean implements the interface and then create the table using the interface e.g. BeanTableModel model = new BeanTableModel(Set rows, MyButtonInterface.class);
    JTable table = new JTable(model);
    This solution allows the table to still be constructed of whatever class you wish and allows you to control which rows appear.

  12. Iverson Chan said

    It’s awesome and very very useful. Thanks.

Leave a reply to Rob Camick Cancel reply