title: Accessing hidden JTable model columns after user sort a column
body: <p>I have a <code>JTable</code> called <code>transactionList</code>, each row represents a <code>Transaction</code> where the first column holds the actual <code>Transaction</code> object and each subsequent column displays some data about the <code>Transaction</code>.</p>
  
  <p>I don't want the <code>Transaction</code> object appearing to the user so I remove it from the <code>ColumnModel</code>:</p>
  
  <pre><code>TableColumnModel cm = transactionList.getColumnModel();
  cm.removeColumn(cm.getColumn(0));
  transactionList.setColumnModel(cm);
  </code></pre>
  
  <p>This works great and I can then retrieve the selected <code>Transaction</code> using:</p>
  
  <pre><code>(Transaction) transactionList.getModel().getValueAt(transactionList.getSelectedRow(), 0))
  </code></pre>
  
  <p>The problem begins when the user sorts a column in the table, then the selected row does not match up correctly. I solve this by changing the above line so that we get the selected row from the table directly and not from the model:</p>
  
  <pre><code>(Transaction) transactionList.getValueAt(transactionList.getSelectedRow(), 0));
  </code></pre>
  
  <p>But now the 0 column is not my hidden <code>Transaction</code> object but simply its first field.</p>
  
  <hr>
  
  <p><strong>I will try to explain in another way as well.</strong></p>
  
  <p>Without allowing sorting of the JTable columns, both examples works:</p>
  
  <p>1) Display Transaction object in first column (don't remove from ColumnModel)</p>
  
  <p>Can retrieve Transaction object with:</p>
  
  <pre><code>transactionList.getValueAt(transactionList.getSelectedRow(), 0));
  </code></pre>
  
  <p>2) Display Transaction object in first column (remove from ColumnModel)</p>
  
  <p>Can retrieve Transaction object with:</p>
  
  <pre><code>transactionList.getModel().getValueAt(transactionList.getSelectedRow(), 0))
  </code></pre>
  
  <p>If I now allow sorting of the columns, #1 still works. However, method #2 now passes the JTable.getSelectedRow() to TableModel.getValueAt(). These indexes are no longer equal though, the problem is that the JTable and ColumnModel don't contain the Transaction object (only the TableModel does).</p>
  
  <pre><code>transactionList.getColumnCount(); //return 5
  transactionList.getColumnModel().getColumnCount(); //return 5
  transactionList.getModel().getColumnCount(); //returns 6
  </code></pre>
  
  <p>A possible solution I see is to sort the TableModel along with the JTable when the user clicks a column header. Is this viable? Are there better ways of achieving this goal (having a "hidden" object attached to a JTable row)?</p>
  
  <hr>
  
  <p><strong>TableModel</strong></p>
  
  <pre><code>public class TransactionTableModel extends AbstractTableModel {
      String[] columnNames = { "&lt;Transaction_Object&gt;", "Date", "Name",
              "Hours", "Amount", "Notes" };
  
      public TransactionTableModel() {
      }
      public String getColumnName(int col) {
          return columnNames[col].toString();
      }
  
      public Class&lt;?&gt; getColumnClass(int col) {
          switch (col) {
          case 1:
              return Calendar.class;
          case 2:
              return String.class;
          }
      }
  
      public int getRowCount() {
          return DB.getInstance().getTransactions().size();
      }
  
      public int getColumnCount() {
          return columnNames.length;
      }
  
      public Object getValueAt(int row, int col) {
          Transaction t = null;
          t = DB.getInstance().getTransactionsChronological().get(row);
          switch (col) {
          case 0:
              return t;
          case 1:
              return t.getDate();
          case 2:
              return t.getStudent.getName();
          }
      }
  }
  </code></pre>
  
  <hr>
  
  <p><strong>MCVE</strong></p>
  
  <p>There is a hidden first column which has matching values to the second. With an unsorted JTable the button prints the correct value, but after sorting a column by clicking on the header the models are out of sync.</p>
  
  <pre><code>import java.awt.*;
  import java.awt.event.*;
  import javax.swing.*;
  import javax.swing.table.*;
  
  public class SwingTesting {
  
      JFrame frame;
      TablePane tablePane;
  
      public SwingTesting() {
          tablePane = new TablePane();
  
          frame = new JFrame();
          frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
  
          JButton test = new JButton("Print hidden item");
          test.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                  printHiddenItem();
              }
          });
  
          frame.add(tablePane);
          frame.add(test, BorderLayout.SOUTH);
          frame.pack();
          frame.setVisible(true);
      }
  
      class TablePane extends JPanel {
  
          private final JTable table;
          private final TableModel tableModel;
          private final ListSelectionModel listSelectionModel;
  
          public TablePane() {
              table = new JTable();
              tableModel = createTableModel();
              table.setModel(tableModel);
              table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
              table.setAutoCreateRowSorter(true);
              table.add(table.getTableHeader(), BorderLayout.PAGE_START);
              table.setFillsViewportHeight(true);
  
              listSelectionModel = table.getSelectionModel();
              table.setSelectionModel(listSelectionModel);
  
              this.add(new JScrollPane(table));
  
              testMethod();
          }
  
          private TableModel createTableModel() {
              DefaultTableModel model = new DefaultTableModel(new Object[] {
                      "First", "Second", "Third" }, 0) {
              };
  
              addTableData(model);
              return model;
          }
  
          private void addTableData(DefaultTableModel model) {
              model.addRow(new Object[] { "ONE", "ONE", "2007" });
              model.addRow(new Object[] { "TWO", "TWO", "2012" });
              model.addRow(new Object[] { "THREE", "THREE", "2009" });
              model.addRow(new Object[] { "FOUR", "FOUR", "2005" });
              model.addRow(new Object[] { "FIVE", "FIVE", "2001" });
          }
  
  
          private void testMethod() {
              TableColumnModel cm = table.getColumnModel();
              cm.removeColumn(cm.getColumn(0));
              table.setColumnModel(cm);
          }
  
      }
  
      public void printHiddenItem() {
          System.out.println(tablePane.table.getModel().getValueAt(tablePane.table.getSelectedRow(), 0));
      }
  
      public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  new SwingTesting();
  
              }
          });
      }
  }
  </code></pre>