views:

942

answers:

3

How do I enable JTable icons and behaviors for sorting table rows by a column, without letting it use a comparison predicate to do the sorting? That is to say, how do I tell the table headers to show the arrow for ascending/descending sort order in the column being used, and get it to call appropriate methods when sort order/column change?

I am trying to create an (editable, filterable, sortable) JTable backed by an SQL query or view. The rows may not fit in memory, and may not map cleanly to java objects, so I want to do all sorting/filtering within SQL. I have already written the code for changing a query to accommodate sorting by column, filtering by values, and visible columns.

To use this, I am planning to write a JTableModel based on a ResultSet with TYPE_SCROLL_SENSITIVE, and CONCUR_UPDATABLE, so changes to the DB get propagated to the ResultSet. I will periodically (several times a second) force a refresh of the visible JTable from the ResultSet, so changes to the database become visible to the user. User changes to the table will be passed to the updateable ResultSet after validation.

I've looked a little bit at how sorting is done normally, but most implementations seems to rely on the JTable creating a javax.swing.RowSorter with a Comparator predicate, or on maintaining a sorted list of rows that fires events when changed. So, my questions:

ORM frameworks are NOT an answer to this question, because the data do not map well to entity objects. Also, the DBMS I am using is H2.

EDIT: Sortable JTable libraries based on applying Comparators or sorting predicates to row objects are also unsuitable, unfortunately. I do not believe I will be able to hold all objects in memory in order to perform sorting. This problem prevents me from using the SwingX JXTables, GlazedLists, or similar libraries. I wish I could, but I can't. Period.

** I will be dealing with many thousand rows, potentially millions, with numerous columns. Yes, I really DO need to use SQL to do the sorting and filtering.**

Questions: (in descending importance)

  1. How do I show indicators for which column is used to sort rows?
  2. How do I get the JTable to fire appropriate events when the column headers are LEFT-clicked to change sort order?
  3. Is there an easier way to force the JTable to update when the database changes?
  4. Is there a library that would make all this considerably easier (connecting DB queries or views and JTables)?
  5. Am I going to run into horrible, horrible problems when I design the system like this?
+1  A: 

In answer to 1 and 2, check out SwingX, which already includes a table class with built-in sorting (and filtering). You may be able to adapt this.

Am I going to run into horrible, horrible problems when I design the system like this?

From experience, yes. I worked on a project almost exactly the same as this, where someone had designed a JTable that supposedly 'magically' bound to a database table. This coupled display logic and database access together in one big horrible mess, which we replaced entirely with reflection-driven table models and separate record CRUD operations.

You say that ORM is not the answer...

  • If the format of the data doesn't change, then it's worth considering anyway. Your 'entity' classes need not represent real-world entities.

  • If (as I suspect) your entity format changes, it might be worth considering:

    • A flexible map-based Record class which stores records as key-value pairs;

    • Dynamically-built table models for your display logic, built by querying record keys, plugged into SwingX tables to get sort and filter for free;

    • A similarly-designed Repository class which encapsulates your database access separately from the table itself, responsible for loading and saving Records. This acts as an adapter between your updateable ResultSet and the view (although I'd check whether using a ResultSet this way is going to require an open database connection whilst data is visible...).

    This separation into 'a table that displays and sorts records' and 'a repository that manages the data' means:

    • You can reuse the table for non-database-bound data;
    • You can display database-bound records in things other than tables;
    • You won't go mad trying to build and test the thing :)
Dan Vinton
When I say that ORM is utterly unsuitable due to structure, I mean it, reflection is too bloody slow. AFAIK SwingX is tied to sort predicates on row objects, and is thus unsuitable.The table is describing resources with a set of tags, which the user may modify. The user can also add new tags. Furthermore, I *know* at this point that I will be adding additional data types for tags. There will be thousands or (potentially) millions of unique resources. Reflection will choke on performance, and an ORM will generate too many queries or eat RAM like popcorn.
BobMcGee
@BobMcGee - cool, then ditch the ORM and reflection. I still think that you should seriously consider separating the data access from the table though.I've written a JTable extension myself that had clickable column headers. I overrode the code that renders the header row to place JButtons there instead of labels, then added event handlers to receive the click, update the icon appropriately (up/down arrow) and create a custom Comparator to pass to a straight collection sort method on the backing data. All the sorting was wrapped in a SortableTableModel model extension class.
Dan Vinton
adrian.tarau
See posted question: both JXTable and the GlazedLists connections to it are awesome, but not particularly feasible for my needs. To use them, I either have to store primary key objects (longs, in this case) and use ~1 query per key to get properties for comparison OR store all the objects in memory. The first is unworkably slow. The latter is more complex: it works fine for small, simple rows, but rapidly eats RAM up. If you store 100K rows with 15 columns, you pay for java's objects. 8 bytes minimum per object, 24 PER STRING. I've been here before: there's a reason we use DBs.
BobMcGee
Hmmm....not sure what do you mean by " I do not believe I will be able to hold all objects in memory in order to perform sorting." You don't have to keep all the objects in memory and you don't have to use the default sorting model. The main reason to use JXTable is because it provides support for sorted columns and takes care of all visual details but you can still do whatever you want in the table model. You can do the same thing with JTable but you will have to implement yourself a functionality already implemented in JXTable.
adrian.tarau
A: 
banjollity
I don't need multiple sorting columns (yet). Is there a way I can use this to hook into external sorting (SQL) rather than operating on objects in memory?
BobMcGee
Well its a JTable subclass and uses a TableModel just like any other JTable. It's possible you can if you write your own customised TableModel
banjollity
+1  A: 

I have never used it myself but JIDE Data Grids provides a DatabaseTableModel that provides filtering and sorting support using SQL WHERE and ORDER BY.

Mark
Not free, but I should be able to dissect it for ideas at least. Best answer so far, and it doesn't involve "ignore your requirements" or "use reflection" (snicker) so you get credit. Thanks!
BobMcGee