Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
14
respostas

Erro ao buscar um objeto no DB.

Ola, no desenvolvimento da minha aplicação de treinamento, onde preciso buscar um orcamento, pra popular a view com seus atributos, me deparei com o erro impresso logo abaixo:

Consegui isolar o codigo e o simulei numa classeTest:

1) Classe Test

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/springbeans.xml")
@TransactionConfiguration
public class TestSearch {

    @Autowired
    OrcamentoGeralController ogc;

    @Test
    public void test() {

        OrcamentoGeral og = ogc.buscarPorId("3");

        List<OrcamentoGeral>  listOG = og.getCliente().getListOrcamentoGeral();

        for (OrcamentoGeral o : listOG) {
            System.out.println(o.getId());

        }
    }

2) Resultado da execução:

1
2
3

3) Ao terminar a impressao do resultado, ele exibe essa pilha de erro:

...
...
...
at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
    at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
    at com.cursoAlura5.entities.Escritorio.hashCode(Escritorio.java:19)
    at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
2017-04-29 10:37:28,801 ERROR org.springframework.beans.factory.support.DefaultListableBeanFactory - Destroy method on bean with name 'perfilController' threw an exception
java.lang.StackOverflowError
    at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
    at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
    at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
    at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
    at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
....
....
....
    at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
    at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
    at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
2017-04-29 10:37:28,806 INFO  org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'fabricaConexao'
2017-04-29 10:37:28,806 INFO  org.hibernate.impl.SessionFactoryImpl - closing

4) Classe Cliente:

@Entity @ManagedBean @Data
public class Cliente implements Serializable {

    @OneToMany
    private List<OrcamentoGeral>     listOrcamentoGeral ;

5) Classe OrcamentoGeral:

@Entity @ManagedBean @Data
public class OrcamentoGeral implements Serializable {

    @ManyToOne(cascade = CascadeType.ALL)
    private                                               Cliente  cliente;

Alguem pode me dar uma ideia fazendo o favor do que estar acontecendo?

14 respostas

E aí, Jonas! Tudo certo, cara? =)

Aparentemente, há um erro com sua classe Escritorio, você poderia postá-la aqui, por favor? =)

Ahh, além disso, coloca a Stack Trace completa também! Assim podemos auxiliá-lo de forma mais precisa...

Fábio

Bom dia Fabio, segue a Classe Escritorio:

@Entity @ManagedBean @Data
public class Escritorio implements Serializable {

    private static final long serialVersionUID = 1L;
    @Version
    private Long version;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer           id;

    @OneToOne(cascade = CascadeType.ALL)
    private Perfil            proprietario;
    private String            nomeComercial;
    @OneToMany
    private List<Colaborador> listColaboradores;
    @OneToMany
    private List<Cliente>     listClientes;
    @OneToMany
    private List<Fornecedor>  listFornecedor;
    @OneToMany
    private List<Perfil>      listParceiros;
    @OneToMany 
    private List<Servico>     listServicos;

    private String Rua;
    private String numero;
    private String Bairro;
    private String Cidade;
    private String Estado;
    private String pais;
    private String foneFixo;
    private String whats;
    private String skype;
    private String twitter;
    private String Email;
    private String site;


}
linha 19: public class Escritorio implements Serializable {
@Entity @ManagedBean @Data
public class Perfil implements Serializable {

    private static final long serialVersionUID = 1L;
    @Version
    private Long       version;//

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer    Id;
    @Lob 
    private byte[]     imagemPerfil;
    @Lob 
    private byte[]     imagemPerfilBanner;
    private String     perfilName;
    private String     sexo;
    private String     genero;
    private String     rg;/*    @Column(unique=true)*/
    private String     cpf;
    private String     perfilEmail;
    private String     whats;
    private String     foneComercial;
    private String     cep;
    private String     rua;
    private String     cidade;
    private String     estado;
    private String     numero;
    private String     renda;
    private String     password;
       private String     idade;
    private String     instituicaoFormacao;
    private String     curso="Arquiteto";
    private String     anoFormatura;
    private String     turma;
    private String     matricula;
    private String     obs;
    private String     CAU;
    private String     formacao;
    private Boolean    active=true;
    private Boolean    concordoTermoPrivacidade=false;
    @OneToOne
    private Escritorio escritorio;

}
LInha17: public class Perfil implements Serializable {
at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
at com.cursoAlura.entities.Escritorio.hashCode(Escritorio.java:19)
at com.cursoAlura.entities.Perfil.hashCode(Perfil.java:17)
2017-04-29 10:37:28,806 INFO  org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'fabricaConexao'
2017-04-29 10:37:28,806 INFO  org.hibernate.impl.SessionFactoryImpl - closing

Quebrando a cabeca pra descobrir o erro, suspeito que seja por que:

Escritorio tem listCliente onde cada cliente tem Escritorio, que tem listCliente. e assim infinitamente.

Será que seria isso mesmo, se for, como sair dele?

Jonas uma coisa estranha que eu vi no seu código é.

@Entity @ManagedBean @Data
public class Escritorio implements Serializable {

Essas anotação data e managedBean, não entendo o porque delas ai, deixa só como @Entity para ver.

Jonas,

Acredito que não seja esse o motivo, não. Rs. A menos que o mapeamento esteja incorreto.

Mas agora que vi, há uma menção sobre um PerfilController, pode colocá-lo aqui? =)

Fábio

Obrigado Fabio e Alisson pelas respostas.

Descobri o problema, não era o Escritorio e o Cliente que estava gerando o loop infinito, era o Escritorio e o Perfil.

Comentei o relacionamento @OneToOne do atributo escritorio, na Classe Perfil, e resolveu.

@Entity @ManagedBean @Data
public class Perfil implements Serializable {
//    @OneToOne
    private Escritorio escritorio;

Respondendo ao questionamento do Alisson: 1) Anotação @Data, ela insere automaticamente os getter e setter pra nao precisar ficar dando CTRL+F3, cada vez que altera os atributos do bean. Não é boa pratica usar getters e setter abusiavamente, mas como é uma aplicacao web e preciso dar getter e setter em praticamente todos os atributos, entao uso ele. 2) Anotacao @ManagedBean, estou usando o JSF, essa anotação incluir o bean no contexto do JSF.

Jonas,

Tem certeza que está resolvido? Rs. Pergunto, porque acredito que a JPA precisará reconhecer esse mapeamento de alguma forma.

Como você colocou o @OneToMany no atributo da classe Escritório, acho que seria interessante colocar um @ManyToOne do outro lado. E ainda colocar o mappedBy no lado do Escritorio...

Fábio

Fabio, resolveu, mas criou problema em outro lugar, Vou fazer a sugestão que você falou, pra ver o que dá.

Jonas,

E aí, resolveu? =)

Fábio

Bom dia Fabio, parei ontem, retomei agora a poco... postarei assim que tiver uma conclusão do que você sugeriu.

OI Fábio, não sei se entendi direito o que você quis dizer, porque a classe Escritério tem mais de um atributo do tipo Perfil com seus respectivos relacionamentos, mas o que esta dando problema é esse relacionamento.

1) Clase Escritorio:

@Entity @ManagedBean @Data
public class Escritorio implements Serializable {

    @OneToOne(cascade = CascadeType.ALL)
    private Perfil            proprietario;

2) Classe Perfil:

@Entity @ManagedBean @Data
public class Perfil implements Serializable {

    @OneToOne
    private Escritorio escritorio;

}

Quando eu comento o relacionamento abaixo, resolve, nao da mais a StackTrace que postei:

//@OneToOne(mappedBy="proprietario")
    private Escritorio escritorio;

Só que ai eu crio um problema em outro lugar.

@Controller @ViewScoped
public class EscritorioController {

escritorio.setProprietario(perfilController.getPerfil());
perfilController.getPerfil().setEscritorio(escritorio);
        }
    }
    public void salvar(){
        escritorioService.salvar(escritorio);

Quando eu salvo a Entity escritorio, o CASCADE, salva o a Entity perfil automaticamente. Mas, ao remover o relacionamento bidirecional, nao salva mais a Entity peril, entao teria que salvar "manualmente" , sem o recurso do CASCADE. Nao sei se essa seria a forma corretar de resolver o problema, como estou aprendendo gostaria de aprender fazer do jeito certo.

Jonas,

Vixi... Não tinha me atentado a uma coisa... A classe Escritorio possui dois atributos que envolvem a classe Perfil...

@Data
@Entity
@ManagedBean
public class Escritorio {
    @OneToOne
    private Perfil proprietario;

    @OneToMany
    private List<Perfil> listParceiros;

    // Restante do código...
}

Não sei se a JPA consegue resolver esse código, porque a classe Perfil deveria ter dois mapeamentos: uma para o @OneToOne (proprietario) e um @ManyToOne, para o @OneToMany (listParceiros), sacou? =|

Uma possível solução é colocar o proprietário dentro da lista de parceiros e, colocar um atributo na classe Perfil, para indicar que ele é o proprietário... O que acha?

Tirando isso, eu não sei como contornar o seu problema... Vou continuar pesquisando e, se achar alguma coisa, eu posto aqui para lhe ajudar, pode ser? =)

Fábio

Obrigado Fabio, vou tentar resolver considerando suas sugestoes,,,

solução!

Problema solucionado: a @Data do projeto Lombok que estava causando o erro.

import lombok.Data;

@Entity @ManagedBean @Data
public class Cliente implements Serializable

Essa anotação insere automaticamente os metodos GETTERS, os SETTERS, o toString() e o HashCode().

Ao remove-la, o código executou sem erro.

Não sei exatamente o porque ocorreu o erro, mas nos haschCode que inseri manualmente na Classe Escritório e na Classe Perfil, setei só o Id.

Nao verifiquei a fundo pra descobrir o motivo, mas presumo que os HashCode gerado pela @Data estava pegando todos os atributos dos beans, gerando um codigo hasch muito complexo. Ou talvez um atributo, como version, ou o serialVersionUID , que o Data incluíra no hascCode possa ter gerado o erro.

Obrigado ao Fabio e ao Alisson que responderam. O erro foi ótimo pra consolidar conhecimento sobre relacionamento, e suas sugestões ajudaram muito.

Vou encerrar o tópico.