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)
10
respostas

Relacionamento ManyToOne e OneToMany

Olá. Estou com uma dificuldade com a persistência de dados com relacionamentos entre entidades. Criei duas entidades chamadas PACIENTES e outra CONSULTAS, que se relacionam entre si. Criei o CRUD de PACIENTES e estã funcionando perfeitamente. Referente ao relacionamento, um PACIENTE pode ter várias consultas. E várias CONSULTAs podem ter um PACIENTE. Quando vou fazer um cadastro de uma CONSULTA, seleciono o CLIENTE (conforme o print), e ao dar o submit grava perfeitamente os dados na tabela CONSULTA com os dados da consulta, e o ID do PACIENTE. (Não sei se fiz da melhor forma, podem me corrigir).

Mas o problema está quando faço um SELECT com um JOIN.

Exibe o erro do print abaixo.

CLASSE PACIENTE

import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="PACIENTES")
public class Paciente {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String nome;
    @OneToMany
    @JoinColumn(name="consulta_id")
    private List<Consulta> consultas;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getNome() {
        return nome;
    }
    public void setNome(String nome) {
        this.nome = nome;
    }
    public List<Consulta> getConsultas() {
        return consultas;
    }
    public void setConsultas(List<Consulta> consultas) {
        this.consultas = consultas;
    }

    @Override
    public String toString() {
        return "Paciente [id=" + id + ", nome=" + nome + ", consultas=" + consultas + "]";
    }
}

CLASSE CONSULTA

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name= "CONSULTAS")
public class Consulta {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String motivo;
    @ManyToOne
    private Paciente paciente;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getMotivo() {
        return motivo;
    }

    public void setMotivo(String motivo) {
        this.motivo = motivo;
    }

    public Paciente getPaciente() {
        return paciente;
    }

    public void setPaciente(Paciente paciente) {
        this.paciente = paciente;
    }

    @Override
    public String toString() {
        return "Consulta [id=" + id + ", motivo=" + motivo + ", paciente=" + paciente + "]";
    }
}

FORMULÁRIO de CADASTRO

<form action="/erp/consultas/grava" autocomplete="off">
                <div class="panel-body">
                    <div class="tab-content">
                        <div class="tab-pane fade active show" id="dados">
                            <div class="row">
                                <div class="col-xl-12">
                                    <div class="form-group">
                                        <label for="exampleInputPassword1">Paciente</label>
                                        <select class="form-control" name="paciente.id">
                                            <option value="1">Eu</option>
                                            <option value="2">Eu Mesmo</option>
                                            <option value="3">Irene</option>
                                        </select>
                                    </div>
                                </div>
                                <div class="col-xl-12">
                                    <div class="form-group">
                                        <label for="exampleInputPassword1">Motivo da Consulta</label>
                                        <textarea class="form-control" rows="10" name="motivo"></textarea>
                                    </div>
                                </div>                            
                            </div>
                        </div>
                    </div>
                </div>
                <div class="panel-footer text-right">
                    <button type="reset" class="btn btn-warning btn-sm">Limpar</button>
                    <button type="submit" class="btn btn-success btn-sm m-l-5">Cadastrar</button>
                </div>
            </form>
10 respostas

PACIENTE DAO

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import br.com.rdgsolutions.erp.models.Paciente;

@Repository
@Transactional
public class PacienteDAO {

    @PersistenceContext
    private EntityManager manager;

    public void gravar(Paciente paciente) {
        manager.persist(paciente);
    }
}

CONSULTA DAO

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import br.com.rdgsolutions.erp.models.Consulta;

@Repository
@Transactional
public class ConsultaDAO {

    @PersistenceContext
    private EntityManager manager;

    public void gravar(Consulta consulta) {
        manager.persist(consulta);
    }

    public List<Consulta> listar() {
        return manager.createQuery("select c from Consulta c INNER JOIN FETCH c.paciente p", Consulta.class).getResultList();
    }
}

ERRO APRESENTADO

HTTP Status 500 – Internal Server Error

Type Exception Report

Message Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.rdgsolutions.erp.models.Paciente.consultas, could not initialize proxy - no Session

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.rdgsolutions.erp.models.Paciente.consultas, could not initialize proxy - no Session
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)

Root Cause

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.rdgsolutions.erp.models.Paciente.consultas, could not initialize proxy - no Session
    org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:612)
    org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
    org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:591)
    org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
    org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:621)
    java.base/java.lang.String.valueOf(String.java:3365)
    java.base/java.lang.StringBuilder.append(StringBuilder.java:169)
    br.com.rdgsolutions.erp.models.Paciente.toString(Paciente.java:45)
    java.base/java.lang.String.valueOf(String.java:3365)
    java.base/java.lang.StringBuilder.append(StringBuilder.java:169)
    br.com.rdgsolutions.erp.models.Consulta.toString(Consulta.java:47)
    java.base/java.lang.String.valueOf(String.java:3365)
    java.base/java.lang.StringBuilder.append(StringBuilder.java:169)
    java.base/java.util.AbstractCollection.toString(AbstractCollection.java:457)
    java.base/java.lang.String.valueOf(String.java:3365)
    java.base/java.lang.StringBuilder.append(StringBuilder.java:169)
    java.base/java.util.AbstractMap.toString(AbstractMap.java:556)
    java.base/java.lang.String.valueOf(String.java:3365)
    java.base/java.lang.StringBuilder.append(StringBuilder.java:169)
    org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:310)
    org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1396)
    org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1141)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)

Note A pilha de erros completa da causa principal está disponível nos logs do servidor.

Oi Rodrigo,

No seu @OneToMany faltou adicionar o mappedBy para que a JPA saiba que se trata de um relacionamento bidirecional:

@OneToMany(mappedBy = "paciente")
@JoinColumn(name="consulta_id")
private List<Consulta> consultas;

Após essa mudança o recomendado é gerar novamente as tabelas, para evitar problemas com os registros existentes.

Olá chará!!!

Quando eu faço da forma que você sugeriu, conyinua gravando corretamente, mas quando faço o SELECT, aparece um erro:

Sua sugestão

@OneToMany(mappedBy = "paciente")
@JoinColumn(name="consulta_id")
private List<Consulta> consultas;

Erro

Type Exception Report

Message Servlet.init() for servlet [dispatcher] threw exception

Description The server encountered an unexpected condition that prevented it from fulfilling the request.

Exception

javax.servlet.ServletException: Servlet.init() for servlet [dispatcher] threw exception
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:831)

Root Cause

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in br.com.rdgsolutions.erp.conf.JPAConfiguration: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn: br.com.rdgsolutions.erp.models.Paciente.consultas
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
    org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:702)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:578)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:530)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:170)
    javax.servlet.GenericServlet.init(GenericServlet.java:158)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:831)

Continuação do erro

Root Cause

org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn: br.com.rdgsolutions.erp.models.Paciente.consultas
    org.hibernate.cfg.annotations.CollectionBinder.bind(CollectionBinder.java:542)
    org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:2204)
    org.hibernate.cfg.AnnotationBinder.processIdPropertiesIfNotAlready(AnnotationBinder.java:977)
    org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:804)
    org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:225)
    org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:239)
    org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:282)
    org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1372)
    org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1406)
    org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:58)
    org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365)
    org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
    org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
    org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
    org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154)
    org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
    org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:702)
    org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:578)
    org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:530)
    org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:170)
    javax.servlet.GenericServlet.init(GenericServlet.java:158)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
    org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
    org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    java.base/java.lang.Thread.run(Thread.java:831)

Ah sim,

Esqueci de mencionar que precisa apagar essa anotação do atributo:

@JoinColumn(name="consulta_id")

Meu SELECT:

return manager.createQuery("SELECT c FROM Consulta c INNER JOIN c.paciente p", Consulta.class).getResultList();

Já tentei assim também:

return manager.createQuery("SELECT c FROM Consulta c  JOIN c.paciente p", Consulta.class).getResultList();

Mesmo tirando @JoinColumn(name="consulta_id") continua com erro

solução!

Resolvido adicionando o FETCH na anotação @OneToMany

@OneToMany(mappedBy = "paciente", fetch = FetchType.EAGER)

Muito obrigado pela ajuda