18
respostas

Ajuda em classe "orientada a Strings" para Spring Data JPA

Olá pessoal! Tenho uma classe que me da o acesso ao dataTable, ela roda numa boa, acontece que toda minha aplicação é em Spring Data JPA e preciso alterar essa classe para que ela acesse ao banco por JPQL, até por que essa classe esta toda "orientada a Strings" e gostaria de melhora-la, mas como meu conhecimento -e de iniciante, consigo algumas alterações mas emperro na falta de conhecimento, alguém poderia me dar uma força, ou uma sugestão de como encaixá-la melhor dentro da minha aplicação que, como já disse, esta sendo construída em Spring Data JPA.

package br.com.augebit.contas.web.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("tableTeste")
@SuppressWarnings("serial")
public class JqueryDatatablePluginDemo extends HttpServlet {

    private String GLOBAL_SEARCH_TERM;
    private String COLUMN_NAME;
    private String DIRECTION;
    private int INITIAL;
    private int RECORD_SIZE;
    private String ID_SEARCH_TERM,NAME_SEARCH_TERM,PLACE_SEARCH_TERM,CITY_SEARCH_TERM,STATE_SEARCH_TERM,PHONE_SEARCH_TERM;

    @RequestMapping("/acessaTabela")    
    public ModelAndView acessaTabela(){

        ModelAndView view = new ModelAndView("dataTable/index");
        return view;
    }    

    @RequestMapping("/tableAjax")
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String[] columnNames = { "id", "engine", "browser", "platform", "version", "grade" };

        JSONObject jsonResult = new JSONObject();
        int listDisplayAmount = 5;
        int start = 0;
        int column = 0;
        String dir = "asc";
        String pageNo = request.getParameter("iDisplayStart");
        String pageSize = request.getParameter("iDisplayLength");
        String colIndex = request.getParameter("iSortCol_0");
        String sortDirection = request.getParameter("sSortDir_0");

        if (pageNo != null) {
            start = Integer.parseInt(pageNo);
            if (start < 0) {
                start = 0;
            }
        }

        if (pageSize != null) {
            listDisplayAmount = Integer.parseInt(pageSize);
            if (listDisplayAmount < 5 || listDisplayAmount > 100) {
                listDisplayAmount = 5;
            }
        }
        if (colIndex != null) {
            column = Integer.parseInt(colIndex);
            if (column < 0 || column > 5)
                column = 0;
        }
        if (sortDirection != null) {
            if (!sortDirection.equals("asc"))
                dir = "desc";
        }

        String colName = columnNames[column];
        int totalRecords= -1;
        try {
            totalRecords = getTotalRecordCount();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }

        RECORD_SIZE = listDisplayAmount;
        GLOBAL_SEARCH_TERM = request.getParameter("sSearch");
        ID_SEARCH_TERM=request.getParameter("sSearch_0");
        NAME_SEARCH_TERM=request.getParameter("sSearch_1");
        PLACE_SEARCH_TERM=request.getParameter("sSearch_2");
        CITY_SEARCH_TERM=request.getParameter("sSearch_3");
        STATE_SEARCH_TERM=request.getParameter("sSearch_4");
        PHONE_SEARCH_TERM=request.getParameter("sSearch_5");
        COLUMN_NAME = colName;
        DIRECTION = dir;
        INITIAL = start;

        try {
            jsonResult = getPersonDetails(totalRecords, request);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        response.setContentType("application/json");
        response.setHeader("Cache-Control", "no-store");
        PrintWriter out = response.getWriter();
        out.print(jsonResult);
    }

    public JSONObject getPersonDetails(int totalRecords, HttpServletRequest request)
            throws SQLException, ClassNotFoundException {

        int totalAfterSearch = totalRecords;
        JSONObject result = new JSONObject();
        JSONArray array = new JSONArray();
        String searchSQL = "";

        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        String dbConnectionURL = "jdbc:mysql://localhost:3306/contas?user=root&password=root";
        Connection con = DriverManager.getConnection(dbConnectionURL);
        String sql = "SELECT " + "id, engine, browser, platform, version, "
                + "grade " + "FROM " + "ajax " + "WHERE ";

        String globeSearch = "id like '%" + GLOBAL_SEARCH_TERM + "%'"
                + "or engine like '%" + GLOBAL_SEARCH_TERM + "%'"
                + "or browser like '%" + GLOBAL_SEARCH_TERM + "%'"
                + "or platform like '%" + GLOBAL_SEARCH_TERM + "%'"
                + "or version like  '%" + GLOBAL_SEARCH_TERM + "%'"
                + "or grade like '%" + GLOBAL_SEARCH_TERM + "%'";

        String idSearch="id like " + ID_SEARCH_TERM + "";
        String nameSearch="engine like '%" + NAME_SEARCH_TERM + "%'";
        String placeSearch=" browser like '%" + PLACE_SEARCH_TERM + "%'";
        String citySearch=" platform like '%" + CITY_SEARCH_TERM + "%'";
        String stateSearch=" version like '%" + STATE_SEARCH_TERM + "%'";    
        String phoneSearch=" grade like '%" + PHONE_SEARCH_TERM + "%'";
        System.out.println(phoneSearch);

        if (GLOBAL_SEARCH_TERM != "") {
            searchSQL = globeSearch;
        }
        else if(ID_SEARCH_TERM !=""){
            searchSQL=idSearch;
        }
        else if(NAME_SEARCH_TERM !=""){
            searchSQL=nameSearch;
        }
        else if(PLACE_SEARCH_TERM!=""){
            searchSQL=placeSearch;
        }
        else if(CITY_SEARCH_TERM!=""){
            searchSQL=citySearch;
        }
        else if(STATE_SEARCH_TERM!=""){            
            searchSQL=stateSearch;
        }
        else if(PHONE_SEARCH_TERM!=null){
            searchSQL=phoneSearch;
            System.out.println(searchSQL);
        }

        sql += searchSQL;
        sql += " order by " + COLUMN_NAME + " " + DIRECTION;
        sql += " limit " + INITIAL + ", " + RECORD_SIZE;
        System.out.println(sql);
        //for searching
        PreparedStatement stmt = con.prepareStatement(sql);
        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            JSONArray ja = new JSONArray();
            ja.put(rs.getString("id"));
            ja.put(rs.getString("engine"));
            ja.put(rs.getString("browser"));
            ja.put(rs.getString("platform"));
            ja.put(rs.getString("version"));
            ja.put(rs.getString("grade"));
            array.put(ja);    
        }
        stmt.close();
        rs.close();

        String query = "SELECT " + "COUNT(*) as count " + "FROM " + "ajax " + "WHERE ";

        //for pagination
        if (GLOBAL_SEARCH_TERM != ""||ID_SEARCH_TERM != "" || NAME_SEARCH_TERM != "" ||PLACE_SEARCH_TERM != ""||CITY_SEARCH_TERM != ""|| STATE_SEARCH_TERM != "" || PHONE_SEARCH_TERM != "" ) {
            query += searchSQL;

            PreparedStatement st = con.prepareStatement(query);
            ResultSet results = st.executeQuery();

            if (results.next()) {
                totalAfterSearch = results.getInt("count");
            }
            st.close();
            results.close();
            con.close();
        }
        try {
            result.put("iTotalRecords", totalRecords);
            result.put("iTotalDisplayRecords", totalAfterSearch);
            result.put("aaData", array);
        } catch (Exception e) {

        }
        return result;
    }

    public int getTotalRecordCount() throws SQLException {
        int totalRecords = -1;
        String sql = "SELECT " + "COUNT(*) as count " + "FROM " + "ajax";
        String dbConnectionURL = "jdbc:mysql://localhost:3306/contas?user=root&password=root";
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Connection con = DriverManager.getConnection(dbConnectionURL);
        PreparedStatement statement = con.prepareStatement(sql);
                ResultSet resultSet = statement.executeQuery();

        if (resultSet.next()) {
            totalRecords = resultSet.getInt("count");
        }
        resultSet.close();
        statement.close();
        con.close();
        return totalRecords;
    }
}
18 respostas

vc postou muito codigo, especifico seu.. nem da para a gente olhar. De todo jeito, eu nao vi nada de spring no seu projeto, apenas servlet direto. Te aconselho fazer os cursos de spring da alura, eles vao te dar um norte de como trabalhar com a tecnologia como um todo.. ate pq nenhum projeto eh baseado no spring data jpa.

Olá Alberto. Fiz os cursos Carreira Desenvolvedor Java Web com Spring e outros fora da Alura, me deu um suporte, mas tem muita coisa específica que é por exemplo deixar toda a parte do código que faz o acesso aos dados que esta todo em String que são recuperadas em variáveis, mas que tenho que deixá-las em JPQL, que nesse caso fico bastante emperrado. Concordo com vc que tem muito código e devo ser mais específico. Me explica a sua última fala na qual diz: "ate pq nenhum projeto eh baseado no spring data jpa."!

E vou postar por partes....

Um exemplo aqui seria eliminar a conexão que é capturada em uma String:

String dbConnectionURL = "jdbc:mysql://localhost:3306/contas?user=root&password=root";
        Connection con = DriverManager.getConnection(dbConnectionURL);

Para alterar a conexão aqui fiz a injeção do DataSource e recupero a conexão sem necessariamente ter quer ser por uma String!

@Autowired
private DataSource dataSource;
...
//String dbConnectionURL = "jdbc:mysql://localhost:3306/contas?user=root&password=root";
        Connection con = dataSource.getConnection();
....

me corrija se não for isso e nem for assim!

E nesse caso para substituir a consulta em String

String sql = "SELECT " + "id, engine, browser, platform, version, grade " + "FROM " + "ajax " + "WHERE ";

estou utilizando:

List<Objeto>  obj = objRepository.findAll();

se for diferente ou puder melhorar, me orienta por favor?

aqui vc mandou bem! usar os recursos do framework para aliviar o trabalho manual é sempre uma ótima alternativa.

Tenta separar esses códigos do datatable, eu implementei algo parecido para vraptor, mas é totalmente aplicavel para o Spring, e até mais fácil se vc utiliza Spring data, pois ele já tem os objetos Page e Pageable, a qual eu pesquei inspiração

//copiado do próprio spring data
public interface Page<T> extends Iterable<T>{


    int getDraw();

    int getNumber();

    int getSize();
    int getTotalPages();
    int getNumberOfElements();
    long getTotalElements();
    boolean hasPreviousPage();
    boolean isFirstPage();
    boolean hasNextPage();
    boolean isLastPage();
    Iterator<T> iterator();
    Collection<T> getContent();
    boolean hasContent();

}
//dados no formato esperado pelo datatable
public class DataTable{


    private int draw;
    private int recordsTotal;
    private int recordsFiltered;
    private Collection<?> data;

    public DataTable(Page<?> page) {

        this.draw =page.getDraw();
        this.recordsTotal = (int) page.getTotalElements();
        this.recordsFiltered = (int) page.getTotalElements();
        this.data = page.getContent();
    }

    public int getDraw() {
        return draw;
    }
    public int getRecordsTotal() {
        return recordsTotal;
    }
    public int getRecordsFiltered() {
        return recordsFiltered;
    }
    public Collection<?> getData() {
        return data;
    }


}


//pega os dados da requisição vindos do datatable
@Convert(PageRequest.class)
@RequestScoped
public class DatatableRequestConverter implements Converter<PageRequest> {

    @Inject
    private  HttpServletRequest request;

    public DatatableRequestConverter() {

    }


    @Override
    public PageRequest convert(String value, Class<? extends PageRequest> type) {
        String iDraw = this.request.getParameter("draw");
        String iDisplayStart = this.request.getParameter("start");
        String iDisplayLength = this.request.getParameter("length");

        Integer start = 0;
        Integer size =10;
        Integer draw = 0;

        if(iDisplayStart != null){
         start = Integer.valueOf(iDisplayStart);
        }
        if(iDisplayLength != null){
         size = Integer.valueOf(iDisplayLength);
        }
        if(iDraw != null){
        draw = Integer.valueOf(iDraw);
        }    
        String search = this.request.getParameter("search[value]");
        PageRequest pageRequest = new PageRequest();
        pageRequest.setStart(start);
        pageRequest.setSize(size);
        pageRequest.setSearch(search);
        pageRequest.setDraw(draw);
        return pageRequest;
    }
}
//resposta que o datatable espera
public class PageResponse<T> implements Page<T>, Serializable {


    private static final long serialVersionUID = 867755909294344406L;

    private final Collection<T> content = new LinkedList<T>();
    private final PageRequest pageable;
    private final long total;
    private int draw;


    public PageResponse(List<T> content, PageRequest pageable, long total) {

        if (null == content) {
            throw new IllegalArgumentException("Content must not be null!");
        }

        this.content.addAll(content);
        this.total = total;
        this.pageable = pageable;
        this.draw = pageable.getDraw();
    }


    public PageResponse(List<T> content) {

        this(content, null, (null == content) ? 0 : content.size());
    }

    public int getNumber() {
        return pageable == null ? 0 : pageable.getPageNumber();
    }

    public int getSize() {
        return pageable == null ? 0 : pageable.getSize();
    }

    public int getTotalPages() {

        return getSize() == 0 ? 0 : (int) Math.ceil((double) total / (double) getSize());
    }

    public int getNumberOfElements() {

        return content.size();
    }

    public long getTotalElements() {

        return total;
    }

    public boolean hasPreviousPage() {

        return getNumber() > 0;
    }

    public boolean isFirstPage() {

        return !hasPreviousPage();
    }

    public boolean hasNextPage() {

        return ((getNumber() + 1) * getSize()) < total;
    }

    public boolean isLastPage() {

        return !hasNextPage();
    }

    public Iterator<T> iterator() {

        return content.iterator();
    }

    public Collection<T> getContent() {

        return Collections.unmodifiableCollection(content);
    }

    public boolean hasContent() {

        return !content.isEmpty();
    }

    @Override
    public String toString() {

        String contentType = "UNKNOWN";

        if (content.size() > 0) {
            contentType = content.iterator().next().getClass().getName();
        }

        return String.format("Page %s of %d containing %s instances", getNumber(), getTotalPages(), contentType);
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof PageResponse<?>)) {
            return false;
        }

        PageResponse<?> that = (PageResponse<?>) obj;

        boolean totalEqual = this.total == that.total;
        boolean contentEqual = this.content.equals(that.content);
        boolean pageableEqual = this.pageable == null ? that.pageable == null : this.pageable.equals(that.pageable);

        return totalEqual && contentEqual && pageableEqual;
    }

    @Override
    public int hashCode() {

        int result = 17;

        result = 31 * result + (int) (total ^ total >>> 32);
        result = 31 * result + (pageable == null ? 0 : pageable.hashCode());
        result = 31 * result + content.hashCode();

        return result;
    }

    @Override
    public int getDraw() {

        return this.draw;
    }

}

No controlador basta eu chamar

@Get("/materias/paginar")
    public void paginar(PageRequest pageRequest, Integer diarioId){

        Page<MateriaDTO> materiasPage = materiaRepository.paginate(pageRequest,diarioId);
        DataTable datatable = new DataTable(materiasPage);
        result.use(Results.json()).withoutRoot().from(datatable).include("data").serialize();    
    }

No dao

    public Page<MateriaDTO> paginate(PageRequest pageRequest,Integer diarioId) {

        TypedQuery<Materia> q =  manager.createQuery("select pd from Materia pd where pd.diario.diarioId = :id",Materia.class);
        q.setParameter("id", diarioId);
        q.setFirstResult(pageRequest.getStart());
        q.setMaxResults(pageRequest.getOffset());
        List<Materia> materias = q.getResultList();
        List<MateriaDTO> dtos =  new ArrayList<>();
        for(Materia materia : materias){    
            MateriaDTO dto = new MateriaDTO(materia);
            dtos.add(dto);
        }
        Page<MateriaDTO> page = new PageResponse<MateriaDTO>(dtos, pageRequest, count(diarioId));
        return page;
    }    
        public Long count(Integer diarioId){


        TypedQuery<Long> q = manager.createQuery("select count(m) from Materia m where m.diario.diarioId = :id",Long.class);
        q.setParameter("id", diarioId);
        return q.getSingleResult();
    }

a parte do datatable é genéria e pode ser utilizada para qualquer Object, agora é só adaptar para o Spring e para seu contexto

já que não está usando JPA, mas mesmo assim separa a parte de acesso a dados em um DAO, no teu controler pode ser assim

@Autowired
private DataSource dataSource;
...
public void fazAlgumaCoisa(){
        Connection con = dataSource.getConnection();

AlgoDao dao = new AlgoDao(con);

dao.consulta();

con.close();
public class AlgoDao(){

private Connection conn;
public AlgoDAO(Connection con){
this.connection = conn;
}
public void consulta(){
//faz as consultas necessárias
}
}

dá pra melhorar ainda, dá pra pegar essa conexão de uma ConnectionFactory, tem exemplo de Connection Factory e DAO com JDBC na apostila da Caelum:

https://www.caelum.com.br/download/caelum-java-web-fj21.pdf

Olá Ricardo Johannsen, consegui fazer na minha aplicação dessa forma

// A entidade datatable

package br.com.clipboard.financeiro.entity;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.OneToMany;

import org.springframework.data.jpa.domain.AbstractPersistable;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@Entity
@JsonIgnoreProperties(value = {"id", "sEcho", "sColumns", "new"})
public class DataTableEtiquetas extends AbstractPersistable<Long> {

    int iTotalRecords;
    int iTotalDisplayRecords;
    String sEcho;
    String sColumns;

    @OneToMany
    List<Etiquetas> data;

    public int getiTotalRecords() {
        return iTotalRecords;
    }

    public void setiTotalRecords(int iTotalRecords) {
        this.iTotalRecords = iTotalRecords;
    }

    public int getiTotalDisplayRecords() {
        return iTotalDisplayRecords;
    }

    public void setiTotalDisplayRecords(int iTotalDisplayRecords) {
        this.iTotalDisplayRecords = iTotalDisplayRecords;
    }

    public String getsEcho() {
        return sEcho;
    }

    public void setsEcho(String sEcho) {
        this.sEcho = sEcho;
    }

    public String getsColumns() {
        return sColumns;
    }

    public void setsColumns(String sColumns) {
        this.sColumns = sColumns;
    }

    public List<Etiquetas> getData() {
        return data;
    }

    public void setData(List<Etiquetas> data) {
        this.data = data;
    }
}

// Entidade Etiquetas

package br.com.clipboard.financeiro.entity;

import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.springframework.data.jpa.domain.AbstractPersistable;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;

@Entity
@Table(name = "etiquetas")
@JsonIgnoreProperties(value = {"new"})
public class Etiquetas extends AbstractPersistable<Long> {

    private String nome;

    @JsonManagedReference
    @JsonBackReference
    @ManyToMany
    @JoinTable(name = "etiquetas_has_receitas",
    joinColumns = @JoinColumn(name = "etiquetas_id"),
    inverseJoinColumns = @JoinColumn(name = "receitas_id"))
    private List<MinhasReceitas> minhasReceitas;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public List<MinhasReceitas> getMinhasReceitas() {
        return minhasReceitas;
    }

    public void setMinhasReceitas(List<MinhasReceitas> minhasReceitas) {
        this.minhasReceitas = minhasReceitas;
    }

    @Override
    public void setId(Long id) {
        super.setId(id);
    }
/*
    @Override
    public String toString() {
        return "Etiquetas [nome=" + nome + "]";
    }*/
}

// Recupero no controller

@RequestMapping(value = "/movimentacaoPorEtiquetaListJson/{tag}", method = {RequestMethod.GET, RequestMethod.POST} )
    public @ResponseBody ResponseEntity<DataTableMinhasReceitas> receitasPorEtiquetaListJson(@PathVariable("tag") Optional<String> tag, @ModelAttribute("etiquetasModel") Etiquetas etiquetas2, ModelMap model){

        ModelAndView view = new ModelAndView();

        view.addObject("minhasContas", minhasContasService.findAll(model));

        List<MinhasReceitas> minhasReceitas = receitasService.findByEtiquetas(tag.get());
        List<Etiquetas> etiquetas = etiquetasServices.findByNome(tag.get());
        List<Etiquetas> etiquetasSelect = etiquetasServices.findAll();

        DataTableMinhasReceitas dataTableMinhasReceitas = new DataTableMinhasReceitas();

        dataTableMinhasReceitas.setData(minhasReceitas);
        dataTableMinhasReceitas.setiTotalRecords(minhasReceitas.size());
        dataTableMinhasReceitas.setiTotalDisplayRecords(minhasReceitas.size());

        return ResponseEntity.status(HttpStatus.OK).body(dataTableMinhasReceitas);
        //return etiquetas;

    }

O que acha? O que posso melhorar no código? Dessa forma por ter outras entidades que se relacionam, em uma ponta do relacionamento ocorre tudo beleza, mas na outra ter acontece um loop infinito que ainda não consegui resolver!

Provavelmente vou ter que utilizar o seu exemplo, pois estou tendo problemas nos relacionamentos, ao mudar a anotação de EAGER para LAZY, nesse meu exemplo.

achei sensacional!! ficou muito bom, apenas no lugar de :

DataTableMinhasReceitas dataTableMinhasReceitas = new DataTableMinhasReceitas();

        dataTableMinhasReceitas.setData(minhasReceitas);
        dataTableMinhasReceitas.setiTotalRecords(minhasReceitas.size());
        dataTableMinhasReceitas.setiTotalDisplayRecords(minhasReceitas.size());

eu faria

DataTableMinhasReceitas dataTableMinhasReceitas = new DataTableMinhasReceitas(minhasReceitas);
;

mas isso é pura permuraria, quanto ao loop infinito vc sabe me dizer se é erro no hibernate ou na serialização dos objetos? na serialização tem que ter cuidado com referências cíclicas(recursivas) senão vc acaba caindo em problemas desse tipo, esse problema tem até nome, Circular Reference Exception, pode acontecer com xml,json, etc... para entender melhor o problema dá uma checada aqui: http://x-stream.github.io/graphs.html

normalmente acontece quando se serializa listas

É Exatamente referências cíclicas(recursivas) que esta ocorrendo em uma das pontas do relacionamento! Como posso resolver?

Eu manteria as Coleções Lazy, e carregaria os relacionamentos na query mesmo com JOIN Fetch

Bacana, só estou com problema de exibição na view no datatable que recebe os dados em JSON, não esta exibindo, pelo menos com a anotação LAZY não mostra os dados, quando pego diretamente pela url o json esta lá, mas não mostra na tabela, agora quando anoto com EAGER mostra na tabela normalmente! Sabe por que?

Antes só me diz uma coisa, essa tabela que vc quer exibir, é uma lista de receitas ou de etiquetas?

Ah sim, ambas, mas em situações diferentes, tanto de receitas quanto de etiquetas,...

Porque a referecia cíclica ocorre? simples porque vc está serializando a lista de minhas receitas e a classe minhasreceitas também está serializando etiqueta, ai ele entra em loop. Como resolver? forma fácil: não serializar a lista, forma trabalhosa: cria um objeto dto com todas as propriedades que precisam ser exibidas na tabela e carraga esse DTO com uma querie especifíca e por cima ainda mata o problema2

ou No problema 2 fazer a querie com Join Fetch, que funciona da mesma forma que o EAGER

OK, preciso filtrar isso. Não serializar, seria não ter os dados dessa lista? É isso mesmo? E a forma trabalhosa seria ter um objeto do objeto principal? Pode me mostrar um ex. bem simples?