JTable e autoRefresh

Fala pessoal, eu estou tentando fazer uma tela de consulta de usuários(uso java), porém eu gostaria de no campo de pesquisa tenha um auto-complete, se a pessoas digitar 4 letras o campo vai dar sugestões ou atualizar a tabela para apenas mostrar dados que contenham aquilo digitado, semelhante a um filtro, mas atualizado na hora em que o usuário esta digitando. Eu gostaria de saber se isso é possível, e se alguém teria alguns artigos sobre isso, eu procurei um dia todo e não achei nada que pudesse ajudar. Desde já agradeço!

Basicamente você tem que ter um TableModel próprio que renderize sua coleção de objetos.
Aí, a medida que você vai preenchendo aquele campo, você filtra essa coleção de objetos e manda o TableModel redesenhar o conteúdo da JTable com o método fireTableDataChanged

Eu acho que mais ou menos entendi, mas to tendo problema na hora de listar meus dados na tabela, to utilizando o hibernate e AbstractTableModel, segue abaixo os codigos

public class UsuarioTableModel extends AbstractTableModel {

private final static long serialVersionUID = 1L;

private static final int CODIGO = 0;
private static final int NOME = 1;
private static final int CPF = 2;
private int row = 0;

private List<Usuario> valores;

public UsuarioTableModel(List<Usuario> valores) {
	this.valores = new ArrayList<>(valores);
	fireTableStructureChanged();
}


@Override
public int getColumnCount() {
	return 3;
}

@Override
public String getColumnName(int column) {
	if (column == CODIGO)
		return "Código";
	if (column == NOME)
		return "Nome";
	if (column == CPF)
		return "CPF/Cnpj";
	throw new IllegalArgumentException("Coluna não existe.");
}

@Override
public int getRowCount() {
	return valores.size();
}

/** Retorna o número da linha selecionada */
public int getSelectedRow() {
	return row;
}

@Override
public Object getValueAt(int row, int column) {
	this.row = row; // Recebe o numero da linha selecionada
	Usuario testeUsuario = valores.get(row);
	if (testeUsuario == null)
		return null;
	switch (column) {
	case CODIGO:
		return testeUsuario.getUsuId();
	case NOME:
		return testeUsuario.getUsuDesc();
	case CPF:
		return testeUsuario.getTipo();
	}
	throw new IllegalArgumentException("Coluna não existe.");
}

public boolean isCellEditable(int rowIndex, int columnIndex) {
	return false;
}

public void setValueAt(Object aValue, int rowIndex, int columnIndex) { // imutável
	throw new UnsupportedOperationException("Edição não é suportada.");
}

void addAll(Collection<Usuario> usuario) {
	valores.addAll(usuario);
	fireTableDataChanged();
}

void clear() {
	valores.clear();
	fireTableDataChanged();
}
Usuario get(int row) {
	return valores.get(row);
}

}

E esse é o modo como estou tentando popular, consigo só pegar o nome e quantidade de campos(o model no caso), mas nenhum dado

private JTable getTableCliente() {

	if (tableUsuario == null) {
		tableUsuario = new JTable();
		tableUsuario.setAutoCreateRowSorter(true);
		tableUsuario.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		tableUsuario.setModel(new UsuarioTableModel(new ArrayList<Usuario>()));
		tableUsuario.getColumnModel().getColumn(0).setPreferredWidth(20);
		tableUsuario.getColumnModel().getColumn(1).setPreferredWidth(150);
		tableUsuario.getColumnModel().getColumn(2).setPreferredWidth(40);
	}
	return tableUsuario;
}

tentei desta outra forma também mas da erro

private void populaTabel(JTable table) {

	EntityManagerFactory emf = Persistence.createEntityManagerFactory("fca");
	EntityManager em = emf.createEntityManager();
	
	em.getTransaction().begin();
	TypedQuery<Usuario> query = em.createQuery("SELECT u FROM Usuario u", Usuario.class);
	
	List<Usuario> usuarios = query.getResultList();
	
	for(Usuario usuario: usuarios) {
		table.getValueAt(usuario.getTipo(), 1);
	}
}

Não crie uma nova lista, use a lista passada por parâmetro e no constutor não precisa chamar o método fireTableStructureChanged().

Deixe o construtor do UsuarioTableModel assim:

public UsuarioTableModel(List<Usuario> valores) {
    this.valores = valores; // não cria uma nova lista, usa o mesmo objeto
}

Adiciona o seguinte método ao UsuarioTableModel :

public void atualizar(List<Usuario> valores) {
    this.valores = valores; // não cria uma nova lista, usa o mesmo objeto
    fireTableDataChanged();
}

Modifica o seu método populaTabel dessa forma:

private void populaTabel(JTable table) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("fca");
    EntityManager em = emf.createEntityManager();
    
    TypedQuery<Usuario> query = em.createQuery("SELECT u FROM Usuario u", Usuario.class);
    List<Usuario> usuarios = query.getResultList();
    
    UsuarioTableModel model = (UsuarioTableModel) table.getModel();
    model.atualizar(usuarios); 
}
1 curtida

Caraca, deu certo! Onde exatamente tava o meu erro? e por que essa simples modificação fez funcionar? eu sou meio iniciante no java e sempre tive problemas pra entender a JTable, mas agradeço muito a sua ajuda!!

O maior erro estava aqui:

List<Usuario> usuarios = query.getResultList();

for(Usuario usuario: usuarios) {
    table.getValueAt(usuario.getTipo(), 1);
}

Você só estava chamando o getValueAt passando uma linha e coluna.
Não faz sentido.
Esse método é chamado pela JTable a medida que ela vai desenhando as células visíveis na tela.

E a outra mancada era essa:

this.valores = new ArrayList<>(valores);

Pra que criar uma nova lista de você já está recebendo a lista por parâmetro?
É desperdício de memória criar outra copiando o conteúdo.

2 curtidas

Entendi, consegui aplicar aqui e com mais algumas alterações funcionou como eu estava precisando, eu só continuo com o problema do auto-refresh enquanto o usuário digita, a ideia era não ter um botao de pesquisar, e sim a medida q fosse sendo digitado fosse sendo filtrado e já atualizando a tabela, mas não consegui implementar isso ainda, segue uma das minhas tentativas:

//Metodo principal onde popula as tabelas
public <T> ConsultasSql(JTable table, Class<T> classe, String className, String search, JComboBox<?> box,JTextField field) {
		logger.info("Populando a tabela");
		EntityManagerFactory emf = Persistence.createEntityManagerFactory("fca");
	    EntityManager em = emf.createEntityManager();
	    List<T> classes;
	    if(field.getText().trim().equals("")) {
	    	TypedQuery<T> queryAll = em.createQuery("SELECT u FROM "+className+" u", classe);
	    	classes = queryAll.getResultList();
	    }else {
		    TypedQuery<T> queryLike = em.createQuery("SELECT u FROM "+className+" u WHERE "+box.getSelectedItem().toString().trim().toLowerCase()+" LIKE '%"+search+"%'", classe);
		    classes = queryLike.getResultList();
	    }
	    if(classe == Usuario.class) {
	    	UsuarioTableModel model = (UsuarioTableModel) table.getModel();
	    	model.atualizar(classes); 
	    } else if(classe == Vendedor.class) {
	    	VendedorTableModel model = (VendedorTableModel) table.getModel();
//	    	model.atualizar(classes); 
	    } else if(classe == Produto.class) {
	    	ProdutoTableModel model = (ProdutoTableModel) table.getModel();
//	    	model.atualizar(classes); 
	    } else if(classe == Municipio.class) {
	    	MunicipioTableModel model = (MunicipioTableModel) table.getModel();
//	    	model.atualizar(classes); 
	    }
	}
//Este método pegaria o evento do click no teclado, mas não consegui achar uma lógica que pudesse fazer sentido
private static void teclasPressionadas(JTextField field) {
		field.addKeyListener(new KeyAdapter() {
			
		});
	}