Solucionado (ver solução)
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