Ruben Laguna’s blog

JTable and JPA Pagination Through Custom TableModel

I really want to talk about JTable, Beans Binding and JPA pagination but I think I need to write about JTable and JPA pagination first. So I will take the Beans binding stuff in another post.

By the way, choose wisely your JPA Provider/DB Provider combination, as some combinations will not give you any real paginations at all. For example, neither OpenJPA, Hibernate or TopLink/EclipseLink seems to support Apache Derby pagination (OFFSET/FETCH). The example here uses Derby and TopLink which is a bad example because the JPA pagination doesn’t get translated to SQL command for pagination. So if you really want proper pagination you should use other combination like Hibernate JPA/HSQLDB.

To get a JTable showing data from a paginated JPA Query you need to create a Custom TableModel for the JTable like this one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<span class='line'>class JPAPaginationTableModel extends AbstractTableModel {
</span><span class='line'>
</span><span class='line'>    private final EntityManager manager;
</span><span class='line'>    private  int startPosition;
</span><span class='line'>    private  List&lt;Customers> theList;
</span><span class='line'>    private int counter=0;
</span><span class='line'>
</span><span class='line'>    JPAPaginationTableModel(EntityManager manager) {
</span><span class='line'>        this.manager = manager;
</span><span class='line'>        this.startPosition = 0;
</span><span class='line'>        this.theList = getItems(startPosition, startPosition+100);
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    public int getRowCount() {
</span><span class='line'>        return ((Long) manager.createQuery("SELECT COUNT(c) FROM Customers c").getSingleResult()).intValue();
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    public int getColumnCount() {
</span><span class='line'>        return 3;
</span><span class='line'>    }
</span><span class='line'>
</span><span class='line'>    public Object getValueAt(int rowIndex, int columnIndex) {
</span><span class='line'>
</span><span class='line'>        if((rowIndex >= startPosition) && (rowIndex&lt;(startPosition+100))){
</span><span class='line'>
</span><span class='line'>        } else
</span><span class='line'>        {
</span><span class='line'>            this.theList = getItems(rowIndex, rowIndex+100);
</span><span class='line'>            this.startPosition=rowIndex;
</span><span class='line'>        }
</span><span class='line'>        Customers c = theList.get(rowIndex-startPosition);
</span><span class='line'>
</span><span class='line'>        Object toReturn = null;
</span><span class='line'>        switch (columnIndex) {
</span><span class='line'>            case 0:
</span><span class='line'>                toReturn = c.getId();
</span><span class='line'>                break;
</span><span class='line'>            case 1:
</span><span class='line'>                toReturn = c.getFirstName();
</span><span class='line'>                break;
</span><span class='line'>            case 2:
</span><span class='line'>                toReturn = c.getLastName();
</span><span class='line'>                break;
</span><span class='line'>            default:
</span><span class='line'>                toReturn = c.getId();
</span><span class='line'>
</span><span class='line'>        }
</span><span class='line'>        return toReturn;
</span><span class='line'>    }
</span><span class='line'>    private List&lt;Customers> getItems(int from, int to) {
</span><span class='line'>        System.out.println("number of requests to the database "+counter++);
</span><span class='line'>        Query query = manager.createQuery("SELECT c FROM Customers c").setMaxResults(to-from).setFirstResult(from);
</span><span class='line'>
</span><span class='line'>        //add the cache
</span><span class='line'>        List&lt;Customers> resultList = query.getResultList();
</span><span class='line'>        return resultList;
</span><span class='line'>    }
</span><span class='line'>}</span>

The getValueAt in this custom TableModel make use from a paginated (setMaxResults) JPA query that take 100 rows each time. The database contains 30000 rows but with this TableModel you won’t need to retrieve all of them to display the JTable. Only the rows that need to be shown will be retrieved.

To fully understand this custom TableModel I think it’s better to illustrate a full application so I’m going how to describe how to create an example application with netbeans:


  1. First we need a database, I used Java DB. Go to Netbeans ⇒ Services. Right click on Java DB and select Start server.

  2. p.

  3. Create a database. database name “namesdb” username and password “nbuser”.
  4. Connect to the newly created database
  5. Execute command
  6. download the following sql script to create a table and insert 30000 rows. And execute it in sql command window
  7. Now we have a table with 30000 entries that we want to show in a JTable
  8. Let’s create a project in netbeans. New project ⇒ Java ⇒ Java Application
  9. new project

  10. Let’s create the JPA Entity class for the database. Select the project and click on New ⇒
  11. create entity classes for database

  12. In the wizard select the connection and the customers table
  13. entity classes wizard

  14. Create a Persistence Unit also and specify TopLink as provider
  15. create persistence unit

  16. Now you have created the entity classes for Customers
  17. Now we need to add the Java DB / Derby drivers to the project so TopLink is able to find the drive to connect to the database. Right-click on the project Properties ⇒ Libraries ⇒ Add Library and select or create a library for derby
  18. add derby library

    derby library

  19. Now we can create our GUI
  20. New ⇒ JPanel Form. Type JTablePaginationExample as class name
  21. jpanel form

  22. Drop a JTable into the form from the Palette

  23. Adding JTable
  24. Now let’s add a custom TableModel to this JTable. Switch to the Source view and add the following
  25. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <span class='line'>private TableModel getTableModel() {
    </span><span class='line'>        EntityManager manager = Persistence.createEntityManagerFactory("JTablePaginationJPAPU").createEntityManager();
    </span><span class='line'>
    </span><span class='line'>        return new JPAPaginationTableModel(manager);
    </span><span class='line'>    }
    </span><span class='line'>
    </span><span class='line'>   public static void main(String args[]) {
    </span><span class='line'>        java.awt.EventQueue.invokeLater(new Runnable() {
    </span><span class='line'>
    </span><span class='line'>            public void run() {
    </span><span class='line'>                JFrame frame = new JFrame("JTable with custom TableModel and JPA");
    </span><span class='line'>                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    </span><span class='line'>                JComponent panel = new JTablePaginationExample();
    </span><span class='line'>                frame.add(panel);
    </span><span class='line'>                frame.pack();
    </span><span class='line'>                frame.setVisible(true);
    </span><span class='line'>            }
    </span><span class='line'>        });
    </span><span class='line'>    }</span>

    and at the end of the file add a new class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <span class='line'>class JPAPaginationTableModel extends AbstractTableModel {
    </span><span class='line'>
    </span><span class='line'>    private final EntityManager manager;
    </span><span class='line'>    private int startPosition;
    </span><span class='line'>    private List&lt;Customers> theList;
    </span><span class='line'>    private int counter = 0;
    </span><span class='line'>
    </span><span class='line'>    JPAPaginationTableModel(EntityManager manager) {
    </span><span class='line'>        this.manager = manager;
    </span><span class='line'>        this.startPosition = 0;
    </span><span class='line'>        this.theList = getItems(startPosition, startPosition + 100);
    </span><span class='line'>    }
    </span><span class='line'>
    </span><span class='line'>    public int getRowCount() {
    </span><span class='line'>        return ((Long) manager.createQuery("SELECT COUNT(c) FROM Customers c").getSingleResult()).intValue();
    </span><span class='line'>    }
    </span><span class='line'>
    </span><span class='line'>    public int getColumnCount() {
    </span><span class='line'>        return 3;
    </span><span class='line'>    }
    </span><span class='line'>
    </span><span class='line'>    public Object getValueAt(int rowIndex, int columnIndex) {
    </span><span class='line'>
    </span><span class='line'>        if ((rowIndex >= startPosition) && (rowIndex &lt; (startPosition + 100))) {
    </span><span class='line'>        } else {
    </span><span class='line'>            this.theList = getItems(rowIndex, rowIndex + 100);
    </span><span class='line'>            this.startPosition = rowIndex;
    </span><span class='line'>        }
    </span><span class='line'>        Customers c = theList.get(rowIndex - startPosition);
    </span><span class='line'>
    </span><span class='line'>        Object toReturn = null;
    </span><span class='line'>        switch (columnIndex) {
    </span><span class='line'>            case 0:
    </span><span class='line'>                toReturn = c.getId();
    </span><span class='line'>                break;
    </span><span class='line'>            case 1:
    </span><span class='line'>                toReturn = c.getFirstName();
    </span><span class='line'>                break;
    </span><span class='line'>            case 2:
    </span><span class='line'>                toReturn = c.getLastName();
    </span><span class='line'>                break;
    </span><span class='line'>            default:
    </span><span class='line'>                toReturn = c.getId();
    </span><span class='line'>
    </span><span class='line'>        }
    </span><span class='line'>        return toReturn;
    </span><span class='line'>    }
    </span><span class='line'>
    </span><span class='line'>    private List&lt;Customers> getItems(int from, int to) {
    </span><span class='line'>        System.out.println("numer of requests to the database " + counter++);
    </span><span class='line'>        Query query = manager.createQuery("SELECT c FROM Customers c").setMaxResults(to - from).setFirstResult(from);
    </span><span class='line'>
    </span><span class='line'>        //add the cache
    </span><span class='line'>        List&lt;Customers> resultList = query.getResultList();
    </span><span class='line'>        return resultList;
    </span><span class='line'>    }
    </span><span class='line'>}</span>

  26. Now you can run it. Select JTablePaginationExample and “Run File”
  27. JTablePaginationExample

  28. You can download the full source code of JTablePaginationExample.java
  29. Links

    Comments

    Copyright © 2015 - Ruben Laguna - Powered by Octopress