Close

Java Swing - JTable Lazy Pagination with JPA backend and @DisplayAs annotation to map columns/rows via reflection

[Last Updated: Jul 4, 2018]

This example is an improvement of the last example. We are going to use JPA as backend to load pagination data lazily along with an annotation based approach to map columns of the TableModel via reflection. This is merger of the last example and this example.

In the example, we just need to use DisplayableObjectTableModel with our JTable in the main class and @DisplayAs in our JavaBean/entity class. The rest of the classes are same as the two examples.

Main class

public class PaginationExampleMain {
  public static void main(String[] args) {
      JFrame frame = createFrame();
      ObjectTableModel<Employee> objectDataModel = new DisplayableObjectTableModel<>(Employee.class);
      JTable table = new JTable(objectDataModel);
      table.setAutoCreateRowSorter(true);
      PaginationDataProvider<Employee> dataProvider = createDataProvider(objectDataModel);
      PaginatedTableDecorator<Employee> paginatedDecorator = PaginatedTableDecorator.decorate(table,
              dataProvider, new int[]{5, 10, 20, 50, 75, 100}, 10);
      frame.add(paginatedDecorator.getContentPanel());
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
  }

  private static PaginationDataProvider<Employee> createDataProvider(
          ObjectTableModel<Employee> objectDataModel) {

      return new EmployeeLazyDataProvider(objectDataModel);
  }

  private static JFrame createFrame() {
      JFrame frame = new JFrame("JTable Pagination example");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(new Dimension(600, 300));
      return frame;
  }
}

DisplayableObjectTableModel

This class is same as from this example:

public class DisplayableObjectTableModel<T> extends ObjectTableModel<T> {
    private Map<Integer, ColumnInfo> columnInfoMap;

    public DisplayableObjectTableModel(Class<T> tClass) {
        init(tClass);
    }

    private void init(Class<T> tClass) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(tClass);
            this.columnInfoMap = new HashMap<>();
            for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
                Method m = pd.getReadMethod();
                DisplayAs displayAs = m.getAnnotation(DisplayAs.class);
                if (displayAs == null) {
                    continue;
                }
                ColumnInfo columnInfo = new ColumnInfo();
                columnInfo.displayName = displayAs.value();
                columnInfo.index = displayAs.index();
                columnInfo.method = m;
                columnInfo.propertyName = pd.getName();
                columnInfoMap.put(columnInfo.index, columnInfo);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object getValueAt(T t, int columnIndex) {
        try {
            return columnInfoMap.get(columnIndex)
                    .method.invoke(t);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getColumnCount() {
        return columnInfoMap.size();
    }

    @Override
    public String getColumnName(int column) {
        ColumnInfo columnInfo = columnInfoMap.get(column);
        if (columnInfo == null) {
            throw new RuntimeException("No column found for index " + column);
        }
        return columnInfo.displayName;
    }

    @Override
    public String getFieldName(int column) {
        ColumnInfo columnInfo = columnInfoMap.get(column);
        return columnInfo.propertyName;
    }

    public Class<?> getColumnClass(int columnIndex) {
        ColumnInfo columnInfo = columnInfoMap.get(columnIndex);
        return columnInfo.method.getReturnType();
    }

    private static class ColumnInfo {
        private Method method;
        private int index;
        private String displayName;
        public String propertyName;
    }
}

The pagination decorator

There's no change with the decorator from the previous two examples.

PaginationDataProvider implementation

There's no change with the data provider from the last example.

The JPA entity

Other than JPA annotations, we are also going to use our @DisplayAs annotation in our entity

@Entity
public class Employee {
  @Id
  @SequenceGenerator(name = "eSGen", sequenceName = "employeeSeq",
          initialValue = 50)
  @GeneratedValue(generator = "eSGen")
  private long id;
  private String name;
  private String phoneNumber;
  private String address;

  @DisplayAs(value = "Id", index = 0)
  public long getId() {
      return id;
  }

  public void setId(long id) {
      this.id = id;
  }

  @DisplayAs(value = "Employee Name", index = 1)
  public String getName() {
      return name;
  }

  public void setName(String name) {
      this.name = name;
  }

  @DisplayAs(value = "Work Phone", index = 2)
  public String getPhoneNumber() {
      return phoneNumber;
  }

  public void setPhoneNumber(String phoneNumber) {
      this.phoneNumber = phoneNumber;
  }

  @DisplayAs(value = "Address", index = 3)
  public String getAddress() {
      return address;
  }

  public void setAddress(String address) {
      this.address = address;
  }
}

Output

Example Project

Dependencies and Technologies Used:

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • datafactory 0.8: Library to generate data for testing.
  • JDK 1.8
  • Maven 3.3.9

JTable lazy Pagination with @DisplayAs to map columns/row Select All Download
  • table-lazy-pagination-with-display-as-annotation
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • PaginationExampleMain.java
          • resources
            • META-INF

    See Also