java – Redrawing JTree


There is a JTree based on the implemented TreeModel , which is built on the basis of a selection from the MySQL database. There are buttons by pressing which add / delete data to the MySQL table. How do I keep the data in JTree updated automatically? The revalidate() and repaint() methods do not work. Only updateUI() works, but for some reason it is not recommended to use it (by the way, the second question is why it is not recommended?).

The code:

package catalogs;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.event.TableModelListener;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.TableModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import main.Global;

/*------Класс окна справочника товаров.------*/
public class ProductsCatalog extends JInternalFrame{
    private static final long serialVersionUID = 1L;
    //Объявления компонент окна.
    private JTree groupTree;
    private GroupTreeModel groupTreeModel;
    private JScrollPane groupTreeScrlPane;
    private JButton btnAddGroup;
    private JButton btnDelGroup;
    private ButtonListener btnListener;
    private TreeListener groupTreeListener; 
    private JTable prodTable;
    private ProdTableModel prodTableModel;
    private JScrollPane prodTableScrlPane;
    public ProductsCatalog(){
        Global.DB_NAME = "mydb";                  //УДАЛИТЬ СТРОКУ
        //Инициализация компонент окна.
        //Древо групп товаров.
        groupTreeModel = new GroupTreeModel();
        groupTree = new JTree(groupTreeModel);
        groupTreeListener = new TreeListener();
        groupTreeScrlPane = new JScrollPane(groupTree);
        groupTreeScrlPane.setBounds(10, 10, 150, 200);
        //Кнопки добавления и удаления групп товаров.
        btnListener = new ButtonListener();
        btnAddGroup = new JButton();
        ImageIcon btnAddGroupIcon = new ImageIcon("images/btnAddGroup.png");
        btnAddGroup.setBounds(10, 220, 30, 30);
        btnDelGroup = new JButton();
        ImageIcon btnDelGroupIcon = new ImageIcon("images/btnDelGroup.png");
        btnDelGroup.setBounds(50, 220, 30, 30);
        //Таблица товаров.
        prodTableModel = new ProdTableModel();
        prodTable = new JTable(prodTableModel);
        prodTableScrlPane = new JScrollPane(prodTable);
        prodTableScrlPane.setBounds(170, 10, 500, 200);
        //Параметры окна.
        this.setTitle("Каталог товаров");
        this.setSize(700, 350);

/*------Модель дерева.------*/
    private class GroupTreeModel implements TreeModel{
        public Object getChild(Object parent, int index) {
            Object[][] buf = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `name`='"+ parent +"'");
            Object parentCode = buf[0][0];
            Object[][] res = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='"+ parentCode +"'");
            return res[index][1];
        public int getChildCount(Object parent) {
            Object[][] buf = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `name`='"+ parent +"'");
            Object parentCode = buf[0][0];
            Object[][] res = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='"+ parentCode +"'");
            return res.length;
        public int getIndexOfChild(Object parent, Object child) {
            Object[][] buf = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `name`='"+ parent +"'");
            Object parentCode = buf[0][0];
            Object[][] res = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='"+ parentCode +"'");
            int index = 0;
            for(int i =0; i<res.length; i++){
                if(res[i][1].equals(child)) index = i;
            return index;
        public Object getRoot() {
            Object[][] res = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='0'");
            return res[0][1];
        public boolean isLeaf(Object arg0) {
            return false;
        public void addTreeModelListener(TreeModelListener arg0) {}
        public void removeTreeModelListener(TreeModelListener arg0) {}
        public void valueForPathChanged(TreePath arg0, Object arg1) {}      

/*------Слушатель изменения выбранной строки дерева групп товаров.------*/
    public class TreeListener implements TreeSelectionListener{
        public void valueChanged(TreeSelectionEvent selection) {

/*------Слушатель нажатия кнопок добавления/удаления групп/товаров.------*/
    public class ButtonListener implements ActionListener{
        public void actionPerformed(ActionEvent e) {
            //Если нажата кнопка удаления группы товаров.
                //Проверяем, пуста ли папка, которую удаляем.
                boolean isEmpty = false;
                Object[][] buf = Global.sqlQueryResult("SELECT `id` FROM `prod_group` WHERE `name`='"+ groupTree.getLastSelectedPathComponent() +"'");
                Object[][] buf2 = Global.sqlQueryResult("SELECT * FROM `product` WHERE `group`='"+ buf[0][0] +"'");
                Object[][] buf3 = Global.sqlQueryResult("SELECT * FROM `prod_group` WHERE `parent`='"+ buf[0][0] +"'");
                if(buf2.length==0 && buf3.length==0) isEmpty = true;
                //Если выделена группа, она пуста и мы подтверждаем удаление, тогда удаляем.
                        if(JOptionPane.showConfirmDialog(null, "Вы уверены?", "Подтверждение удаления", JOptionPane.YES_NO_OPTION)==0){
                            Global.sqlQueryVoid("DELETE FROM `prod_group` WHERE `name`='"+ groupTree.getLastSelectedPathComponent() +"'");
                    }else JOptionPane.showMessageDialog(null, "Нельзя удалить эту папку, т.к. она не пуста!", "Ошибка", JOptionPane.ERROR_MESSAGE);         
                }else JOptionPane.showMessageDialog(null, "Вы не выбрали ни одну папку!", "Ошибка", JOptionPane.ERROR_MESSAGE);             

/*------Модель таблицы.------*/
    public class ProdTableModel implements TableModel{
        //Двумерный массив данных.
        private Object[][] selection = null;
        //Метод построения двумерного массива данных, на основе которого отрисовывается таблица товаров.
        public void buildTable(Object group){
            Object[][] buf = Global.sqlQueryResult("SELECT `id` FROM `prod_group` WHERE `name`='"+ group +"'");
            selection = Global.sqlQueryResult("SELECT `id`, `name`, `unit`, `comments` FROM `product` WHERE `group`='"+ buf[0][0] +"'");

        public Class<?> getColumnClass(int colInd) {
            return String.class;
        public int getColumnCount() {
            return 4;
        public String getColumnName(int colInd) {
            String colName ="";
            case 0: colName = "Код"; break;
            case 1: colName = "Наименование"; break;
            case 2: colName = "Ед. изм."; break;
            case 3: colName = "Комментарий"; break;
            return colName;
        public int getRowCount() {
            return selection.length;
        public Object getValueAt(int rowInd, int colInd) {
            return selection[rowInd][colInd];
        public void addTableModelListener(TableModelListener arg0) {}
        public boolean isCellEditable(int arg0, int arg1) {return false;}
        public void removeTableModelListener(TableModelListener arg0) {}
        public void setValueAt(Object arg0, int arg1, int arg2) {}      


You can transfer data operations to the tree model ( GroupTreeModel ), which will notify the view ( JTree ) of changes. To do this, you need to add listeners to the GroupTreeModel :

Set<TreeModelListener> listeners = new CopyOnWriteArraySet<>();

public void addTreeModelListener(TreeModelListener arg0) {
    listeners.add( arg0 );
public void removeTreeModelListener(TreeModelListener arg0) {
    listeners.remove( arg0 );

and the removal method:

public void removeGroupAtPath( TreePath selectionPath ) {
    Object groupToRemove = selectionPath.getLastPathComponent();
    TreePath pathToParent = selectionPath.getParentPath();
    if ( pathToParent != null ) { // one does not simply remove jtree root node
        Object parent = pathToParent.getLastPathComponent();
        int childIndex = getIndexOfChild( parent, groupToRemove );

        Global.sqlQueryVoid( "DELETE FROM `prod_group` WHERE `name`='" + groupToRemove + "'" );

        // для события удаления нужен путь к родительскому узлу
        //   индекс удаляемого узла в списке узлов родителя
        //   и удаляемый объект
        fireNodeRemoved( pathToParent, new int[] { childIndex }, new Object[] { groupToRemove } );

public void fireNodeRemoved( TreePath parentPath, int[] removedIndexes, Object[] removedChildren ) {
    TreeModelEvent e = new TreeModelEvent( this, parentPath, removedIndexes, removedChildren );
    for ( TreeModelListener l : listeners ) {
        l.treeNodesRemoved( e );

The tree will register itself in the list of listeners of the model and, when deleted, will be redrawn as needed.

Then, simply in the click handler, you can call the model method:

groupTreeModel.removeGroupAtPath( groupTree.getSelectionPath() );

No forced redrawing is required.

Scroll to Top