Usando Towel para Paginação e Filtro

24 respostas
I

Tenho uma JTable aqui com um ResultSet gordo(50mil linhas, com 50 colunas cada), que eu tenho que usar para filtrar dados entre outros.
Peguei o sistema aqui usando algo bem parecido com isso:
http://www.inf.ufsc.br/~bosco/downloads/Livro-Java-Como-Programar-Deitel-Ed6/examples/ch25/Fig25_28_31/ResultSetTableModel.java

Até funciona OK, mas depois de certo tempo aberto, com muitas pesquisas feitas, ele não aguenta.Tem um leak aqui que não está muito fácil de achar.
Tô mudando o sistema todo para o Towel, e como não tem API(docs), preciso fazer umas perguntas pertinentes:
1-Alguém já fez uma JTable com paginação no Towel?
2-O Auto filtro parece quase perfeito, mas se vc precisa pesquisar num resultset bem grande, vcs fazem um mecanismo de cache ou eu terei que fazer essa implementação na mão(nesse caso meu projeto é um legado JDBC, tô ferrado, para escapar do resultset gordo vai ser select para todo lado…)?

24 Respostas

M

Então, o Towel tem duas coisas separadas, o TableFilter e o SelectTable, uma para filtrar e outra para paginar, agora me parece uma boa idéia junta-los.

Com o SelectTable voce precisa passar um Paginator que irá trazer “paginas” de resultados. Quando eu usei esse componente meu Paginator era uma interface para o Hibernate que trazia os resultados passando a quantidade de resultados a partir de alguma linha e funcionava excelentemente bem.

V

No caso do TableFilter ele trabalha diretamente sobre o model em memória.
Não há nenhum esquema de cache. A implementação é muito parecida com a que o próprio Java faz.

I

Mas quando os dados são jogados para paginação:

Eu posso estar exibindo 1000, mas eu já teria todos em memória, certo?O que eu quero evitar ao máximo é a duplicação de dados na memória.A maior tabela aqui, com 50mil linhas, tem na verdade, 20MB de dados(se vc tacar tudo num .txt), o que não é nenhuma monstruosidade hoje em dia.

Então seria uma evolução do RowSorter do próprio JDK? :smiley:

V

Sim, é um RowSorter excel-like.

Com a diferença de que ele foi feito no Java 5, antes do RowSorter sequer existir.

I

Não sei se seria uma boa juntá-los, derrepende a não dependencia pode ser útil, mas que falta um BOM exemplo que junte o uso das duas coisas no site do Towel(nem que seja um exemplo de uso avançado), ah isso faz falta. :smiley:


Com a diferença de que ele foi feito no Java 5, antes do RowSorter sequer existir.

Great. :smiley:

M

Mas quando os dados são jogados para paginação:

Eu posso estar exibindo 1000, mas eu já teria todos em memória, certo?O que eu quero evitar ao máximo é a duplicação de dados na memória.A maior tabela aqui, com 50mil linhas, tem na verdade, 20MB de dados(se vc tacar tudo num .txt), o que não é nenhuma monstruosidade hoje em dia.

Exato, ele só pagina o resultado, por isso que Paginator é uma interface que pode ser implementada com o que voce quiser, no meu caso eu tinha uma implementação com Hibernate.

Verdade, mas pelo menos tentarei integra-los de uma forma mais simples e não obrigatoria.

I

Bom… tô pondo no screenshot a tela do filtro que eu vou substituir.Será que eu consigo algo semelhante usando o TableFilter com a SelectTable?
Uma das coisas que eu mais gostei no towel é a simplicidade… já mandei uns 30 tablemodels diferentes pro saco. :slight_smile:


M

Talvez se colocar o header do TableFilter dentro da SelectTable funcione. Eu vejo com mais calma essa opção em breve.

I

Tô dando uma lida nos seus tutoriais, vou tentar implementar.

Esse tópico:
http://www.guj.com.br/java/100793-projeto-towel-autofiltro-em-jtable
Já está sendo de grande valia, mas sinto falta de um exemplo mais sofisticado no site do Towel(que junte váarias funcionalidades em uma algo tipo uma quicktable otimizada).

I

Marky, vc tem mais alguma informação sobre o TableFilter além daquele tópico que eu postei acima.As informações referentes a ele não estão presentes na página do Towel, eu estou me embananando aqui fundindo os dois(TableFilter com SelectTable).

M

Eu não tenho mais nenhum documento que fale de alguns dos componentes.

Eu começei a olhar com mais calma nisso, eu aviso qualquer avanço.

M

Como teste eu criei isso aqui e esperava que fosse automagicamente resolvido.

ObjectTableModel<Person> model = new ObjectTableModel<Person>(
				new AnnotationResolver(Person.class), "name,age,live");

		model.setEditableDefault(true);

		model.add(new Person("A", 10, true));
		model.add(new Person("B", 20, true));
		model.add(new Person("C", 30, false));
		model.add(new Person("D", 40, true));
		model.add(new Person("E", 50, true));
		

		SelectTable<Person> sel = new SelectTable<Person>(
				new AnnotationResolver(Person.class), "name,age,live",
				new ListPaginator<Person>(new PreData().getSampleList()));
		
		
		
		new TableFilter(sel.getTable().getTableHeader(), sel.getModel());
		
		sel.showSelectTable();

Mas nada, vai ter que ser um pouco mais interno, eu vou aproveitar e fazer um refactoring no SelectTable que eu queria a tempos.

I

Boa!Vc podia por um método para pegar a movimentação da página do botão de navegação para paginar sob demanda!Aí seria fácil fazer a paginação com o velho JDBC mesmo, tipo em Oracle:

public static String paging(String tabela, int intInicio, int intLimite) { return &quot;SELECT * FROM (SELECT PAGING.*, ROWNUM PAGING_RN FROM&quot; + &quot; (&quot; + tabela + &quot;) PAGING WHERE (ROWNUM &lt;= &quot; + intLimite + &quot;))&quot; + &quot; WHERE (PAGING_RN &gt;= &quot; + intInicio + &quot;)&quot;; }

Claro, sempre dá para eu criar meu próprio TableModel e ir buscar os resultados, mas o ideal seria deixar o menos invasivo possível(menor não uso do Towel possível).Se tiver idéias eu aceito! :smiley:

M

Eu vou procurar nos meus projetos antigos meu Paginator com hibernate que carrega sob demanda para voce ver.

I

Boa!

Marky, quando vc refatorar a SelectTable peço a voce para por um método setFont nela.Não sei pq, as fontes deixam de ficar em negrito em alguns look and feel.Tô no liquid, e todos perdem o negrito.

M

Ta ai o Paginator generico usando Hibernate, que carrega sobre demanda.

import java.util.List;

import mark.utils.collections.paginator.Paginator;
import mark.utils.el.FieldResolver;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;

import br.com.sistram.database.dao.DAO;
import br.com.sistram.database.hibernate.HibernateUtil;
import br.com.sistram.model.conhecimento.Conhecimento;
import br.com.sistram.model.prestador.Motorista;

public class DaoListPaginator<T> implements Paginator<T> {

	private SimpleExpression exp;
	private Criteria criteria;
	private Criteria countCriteria;
	private DAO<T> dao;
	private Order order;
	private int currentPagination;
	private int maxPage;
	private int pageResults;
	private int listSize;

	public DaoListPaginator(DAO<T> dao, Order order, int resultsPerPage) {
		pageResults = resultsPerPage;
		this.dao = dao;
		this.order = order;

		countCriteria = dao.createCriteria();
		countCriteria.addOrder(order);
		calcPages();
	}

	public DaoListPaginator<T> setRestriction(SimpleExpression exp) {
		this.exp = exp;
		countCriteria = dao.createCriteria();
		countCriteria.addOrder(order);
		countCriteria.add(exp);
		calcPages();
		return this;
	}

	private void calcPages() {
		currentPagination = 0;
		listSize = dao.getListSize(countCriteria);
		if (pageResults == 0) {
			return;
		}
		if (listSize % pageResults == 0) {
			maxPage = listSize / pageResults - 1;
		} else {
			maxPage = listSize / pageResults;
		}
	}

	public List<T> nextResult() {
		int toIndex = (currentPagination + 1) * pageResults;
		if (toIndex > listSize) {
			toIndex = listSize;
		}
		if (criteria == null) {
			criteria = dao.createCriteria();
			criteria.addOrder(order);
			if (exp != null)
				criteria.add(exp);
		}
		List<T> list = dao.getListByLimit(criteria, currentPagination
				* pageResults, toIndex);
		currentPagination++;
		return list;
	}

	@Override
	public int getCurrentPage() {
		return currentPagination;
	}

	@Override
	public int getMaxPage() {
		return maxPage;
	}

	@Override
	public void setCurrentPage(int page) {
		currentPagination = page;
	}

	@Override
	public List<T> getData() {
		return dao.getList();
	}

	@Override
	public void setData(List<T> list) {
		throw new RuntimeException("Use the repository to add data.");
	}

	@Override
	public void filter(String text, FieldResolver field) {
		clearCriterias();

		if (!text.isEmpty()) {
			String fieldName = field.getFieldName();
			
			try{
				addRestriction(field, fieldName, text);
				calcPages();
			}catch (Exception e) {
				clearCriterias();
				
				String[] fields = fieldName.split("[.]");
				for (int i = 0; i < fields.length - 1; i++) {
					String colName = getString(fields, i - 1, i + 1, ".");
					String tfieldName = fields[i];
					criteria.createAlias(colName, tfieldName);
					countCriteria.createAlias(colName, tfieldName);
				}
				fieldName = getString(fields, fields.length - 2, fields.length,
						".");
				addRestriction(field, fieldName, text);
				calcPages();
			}
		}
	}
	
	private void clearCriterias(){
		criteria = dao.createCriteria();
		countCriteria = dao.createCriteria();
		criteria.addOrder(order);
		countCriteria.addOrder(order);
		if (exp != null) {
			criteria.add(exp);
			countCriteria.add(exp);
		}
	}
	
	private void addRestriction(FieldResolver field, String fmtdFieldName, String text){
		if (field.getFieldType().isAssignableFrom(String.class)) {
			criteria.add(Restrictions.like(fmtdFieldName, text,
					MatchMode.ANYWHERE));
			countCriteria.add(Restrictions.like(fmtdFieldName, text,
					MatchMode.ANYWHERE));
		} else {
			criteria.add(Restrictions.eq(fmtdFieldName, field
					.getFormatter().parse(text)));
			countCriteria.add(Restrictions.eq(fmtdFieldName, field
					.getFormatter().parse(text)));
		}
	}

	private static String getString(String[] str, int off, int pos, String div) {
		StringBuilder builder = new StringBuilder();
		if (off < 0)
			off = 0;
		for (int i = off; i < pos; i++)
			builder.append(str[i]).append(div);
		return builder.substring(0, builder.length() - div.length());
	}
}
I

Valeu Marky!
No momento eu uso uma abordagem como essa:
http://codeoop.com/2008/11/01/paging-or-pagable-jtable-table-model-for-large-data-set/

Mas eu quero adaptar e deixar “Towel Like”. :smiley:

O problema nesse projeto que eu peguei é que deixaram o model que traz todos os dados do BD(a classe que herda de AbstractTableModel), responsável por TUDO, inclusive geração de relatórios, carregar comboboxes para pesquisa… :shock:
É o problema conhecido como religioso, vc tira a classe Deus, seu sistema vira um Inferno. :roll: :lol:

M

Ironlynx:
Valeu Marky!
No momento eu uso uma abordagem como essa:
http://codeoop.com/2008/11/01/paging-or-pagable-jtable-table-model-for-large-data-set/

Eu gostei desse método createPagingScrollPaneForTable(JTable jt), acho que posso usar uma abordagem assim para ser facil colocar o TableFilter nela.

Eu acho que vou conseguir simplificar bastante :smiley:

:lol: :lol:

I

Bacana!Não dá nem para eu rir… se eu modificar o acesso aos relatórios, tenho que mudar tudo!Deram um select * from tabela tacaram tudo em memória e fizeram toda a manipulação/operações apartir desse model… funciona uma maravilha… para mil,2mil registros, não para 50mil ou mais! :roll: :x

Uma perguntinha:o SelectTable ajusta a largura das colunas de forma automática?Algo desse tipo:

// Ajusta a largura preferida da coluna visível especificada pelo vColIndex. // A coluna será larga o bastante para mostrar o cabeçalho da coluna e a célula de maior conteúdo. public void packColumn(JTable table, int vColIndex, int margin) { DefaultTableColumnModel colModel = (DefaultTableColumnModel)table.getColumnModel(); TableColumn col = colModel.getColumn(vColIndex); int width = 0; // Obtém a largura do cabeçalho da coluna TableCellRenderer renderer = col.getHeaderRenderer(); if (renderer == null) { renderer = table.getTableHeader().getDefaultRenderer(); } Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false, false, 0, 0); width = comp.getPreferredSize().width; // Obtém a largura maxima da coluna de dados for (int r=0; r&lt;table.getRowCount(); r++) { renderer = table.getCellRenderer(r, vColIndex); comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex), false, false, r,vColIndex); width = Math.max(width, comp.getPreferredSize().width); } width += 2*margin; // Configura a largura col.setPreferredWidth(width); }

>

M

Ironlynx:
Uma perguntinha:o SelectTable ajusta a largura das colunas de forma automática?

Não, mas irá.

I

Punk.Precisou de beta tester, é só me mandar um email.

M

Pode deixar que mandarei, só me responda algo.

Voce quer utilizar a parte de filtro do SelectTable também ou só o Paginator?

Por que pode usar o “Customize” do TableFilter que já é possivel pesquisar (nao de uma forma ta pratica, mas é possivel).

I


Voce quer utilizar a parte de filtro do SelectTable também ou só o Paginator?

Ambos! :smiley:

I

Marky, depois me reporte como anda o projeto.Quando vc me passar para testes, quero fazer uma versão menor da minha Table de pesquisa com paginação para vc por lá no seu site.Sinto falta de uns exemplos mais sofisticados, com ordenação por célula, essas coisas. :smiley:

Criado 10 de julho de 2011
Ultima resposta 23 de jul. de 2011
Respostas 24
Participantes 3