Search code examples
javadatabaseswingjtablepojo

how to create a JTable with POJOs with a single table model


I’m trying to create a GUI to display the content of a database. I’m using a JTable for that. I want a constructor that takes a list of POJOs and eventually column titles. I want also my JTable to be able to return the selected POJO (for example when I doubleclick on a row). Do I really need to create a table model for each POJO or is there a better way?

I’ve made some searchs on similar topics, but I didn’t find any answer that uses a single table model for all POJOs and none of them has a method that returns a POJO.

Here is the description of my database tables and POJOs:

Database tables:

  • students (id, firstname, lastname, birthdate, grade_id, phonenumber)
  • grades (id, grade)
  • authors (id, firstname, lastname, nationality_id)
  • nationalities (id, nationality)
  • books (id, title, author_id, student_id)

POJOs:

  • Grade (int id, String grade)
  • Student (int id, String firstname, String lastname, Grade grade)
  • Author (int id, String firstname, String lastname, Nationality nationality)
  • Nationality (int id, String nationality)
  • Book (int id, String title, Author author, Student student)

Solution

  • Here is as example of solution using reflection and annotations.

    public class Worker {
    
        @Updatable(false)
        @Order(1)
        @ColumnName("ID")
        private int id; 
    
        @Updatable(true)
        @Order(2)
        @ColumnName("FIRST NAME")
        private String firstName; 
    
        @Updatable(true)
        @Order(3)
        @ColumnName("LAST NAME")
        private String lastName; 
    
        public Worker() {
        }
    
        public Worker(int id, String firstName, String lastName) {
            this.id = id;
            this.firstName = firstName;
            this.lastName = lastName;
        }
    
        @Order(1)
        public int getId() {
            return id;
        }
    
        @Order(1)
        public void setId(int id) {
            this.id = id;
        }
    
        @Order(2)
        public String getFirstName() {
            return firstName;
        }
    
        @Order(2)
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        @Order(3)
        public String getLastName() {
            return lastName;
        }
    
        @Order(3)
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    }
    

    -

    @Retention(RetentionPolicy.RUNTIME)
    public @interface Order {
        int value(); 
    }
    

    -

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface ColumnName {
        String value(); 
    }
    

    -

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Updatable {
        boolean value(); 
    }
    

    -

    public class ComparatorReflection<T extends AccessibleObject & Member> implements Comparator<T>{
    
        @Override
        public int compare(T o1, T o2) {
            Order or1 = o1.getAnnotation(Order.class); 
            Order or2 = o2.getAnnotation(Order.class); 
            if (or1 != null && or2 != null && or1.value() - or2.value() != 0) {
                return or1.value() - or2.value(); 
            }
            return o1.getName().compareTo(o2.getName()); 
        }
    
    }
    

    -

    public class ModelDAO extends AbstractTableModel {
    
        private List<?> data; 
    
        public ModelDAO(List data) {
            this.data = data;
        }
    
        @Override
        public int getRowCount() {
            return this.data.size(); 
        }
    
        @Override
        public int getColumnCount() {
            return data.get(0).getClass().getDeclaredFields().length; 
        }
    
        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            try {
                Method[] methods = data.get(rowIndex).getClass().getDeclaredMethods(); 
                Arrays.sort(methods, new ComparatorReflection<Method>()); 
                return methods[2*columnIndex].invoke(data.get(rowIndex), null);
            } catch (Exception ex) {
                ex.printStackTrace(); 
            }
            throw new UnsupportedOperationException("Not supported yet."); 
        }
    
        @Override
        public Class<?> getColumnClass(int columnIndex) {
            Field[] fields = data.get(0).getClass().getDeclaredFields(); 
            Arrays.sort(fields, new ComparatorReflection<Field>()); 
            return fields[columnIndex].getType(); 
        }
    
        @Override
        public String getColumnName(int column) {
            Field[] fields = data.get(0).getClass().getDeclaredFields(); 
            Arrays.sort(fields, new ComparatorReflection<Field>()); 
            return fields[column].getAnnotation(ColumnName.class).value(); 
        }
    
        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            Field[] fields = data.get(rowIndex).getClass().getDeclaredFields(); 
            Arrays.sort(fields, new ComparatorReflection<Field>()); 
            return fields[columnIndex].getAnnotation(Updatable.class).value(); 
        }
    }