Solucionado (ver solução)
Solucionado
(ver solução)
7
respostas

Relacionamentos | Java - JPA - Hibernate - Spring MVC

Olá. Estou com uma dúvida referente à persistência de dados em relacionamentos de entidades. Criei uma entidade chamada MATRICULAS, onde terá um relacionamento com a entidade ALUNOS e outro com a entidade CURSOS. Ou seja, para efetuar a matrícula tem que ter um aluno e tem que ter um curso.

Abaixo segue a classe da entidade MATRICULAS

package br.com.rdgcloud.nexus.modelos;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

@Entity
public class Matriculas {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String situacao;
    private String observacoes;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id")
    private Alunos aluno;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id")
    private Cursos curso;

    public Long getId() {
        return id;
    }

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

    public String getSituacao() {
        return situacao;
    }

    public void setSituacao(String situacao) {
        this.situacao = situacao;
    }

    public String getObservacoes() {
        return observacoes;
    }

    public void setObservacoes(String observacoes) {
        this.observacoes = observacoes;
    }

    public Alunos getAluno() {
        return aluno;
    }

    public void setAluno(Alunos aluno) {
        this.aluno = aluno;
    }

    public Cursos getCurso() {
        return curso;
    }

    public void setCurso(Cursos curso) {
        this.curso = curso;
    }

    @Override
    public String toString() {
        return "Matriculas [id=" + id + ", situacao=" + situacao + ", observacoes=" + observacoes + ", aluno=" + aluno + ", curso=" + curso + "]";
    }
}

Quando eu dou submit no formulário, os campos ID, SITUACAO e OBSERVACOES são salvos no banco, porém os dados do ALUNO e do CURSO não são salvos.

Gostaria de uma ajuda pra saber onde estou errando.

7 respostas

Seus anotations de @OneToOne provavelmente deveriam ser @OneToMany, visto que vários alunos podem ser referenciados na tabela de matrícula. O mesmo acontece para Curso.

É importante notar também, que a definição de tipo dos seus atributos é Alunos e Curso.

O que está acontecendo é que você provavelmente não está setando o valor desses campos utilizando essa tipagem. Logo, o atributo fica null e o banco salva assim (Inclusive, seu schema de banco deveria ter Not Null nesses campos).

Pode postar a parte do código que você efetua esse save?

Esse é o FORM

<form action="/nexus/matriculas/grava" method="post">
    <div class="tab-content p-3">
        <div class="tab-pane fade show active" id="dados" role="tabpanel">
            <div class="form-row">
                <div class="form-group col-md-12" data-select2-id="103">
                    <label class="form-label" for="single-default">Nome do
                        aluno</label> <select
                        class="select2 form-control w-100 select2-hidden-accessible"
                        name="aluno_id" data-select2-id="single-default" tabindex="-1"
                        aria-hidden="true">
                        <option value="">Selecione um aluno</option>
                        <c:forEach items="${alunos}" var="aluno">
                            <option value="${aluno.id}">${aluno.nome}| ${aluno.id} |
                                ${aluno.matricula}</option>
                        </c:forEach>
                    </select>
                    <div class="help-block">
                        Selecione o aluno de acordo com a legenda
                        <code>Nome</code>
                        |
                        <code>Código</code>
                        |
                        <code>Matrícula</code>
                    </div>
                </div>
                <div class="form-group col-md-8" data-select2-id="103">
                    <label class="form-label" for="single-default">Curso</label> <select
                        name="curso_id" class="form-control form-control-sm">
                        <option value="">Selecione o curso desejado</option>
                        <c:forEach items="${cursos}" var="curso">
                            <option value="${curso.id}">${curso.id}| ${curso.nome}</option>
                        </c:forEach>
                    </select>
                </div>
                <div class="form-group col-md-4">
                    <label for="inputState">Situação</label> <select name="situacao"
                        class="form-control form-control-sm">
                        <option selected>ATIVO</option>
                        <option>INATIVO</option>
                    </select>
                </div>
                <div class="form-group col-md-12">
                    <div class="form-group">
                        <label for="inputAddress">Observações</label>
                        <textarea class="form-control" name="observacoes" rows="5"></textarea>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div>
        <button type="submit"
            class="btn btn-success btn-sm btn-block waves-effect waves-themed">Matricular</button>
    </div>
</form>

Esse é o CONTROLLER

package br.com.rdgcloud.nexus.controllers;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import br.com.rdgcloud.nexus.daos.AlunoDAO;
import br.com.rdgcloud.nexus.daos.CursoDAO;
import br.com.rdgcloud.nexus.daos.MatriculaDAO;
import br.com.rdgcloud.nexus.modelos.Alunos;
import br.com.rdgcloud.nexus.modelos.Cursos;
import br.com.rdgcloud.nexus.modelos.Matriculas;

@Controller
@RequestMapping("/matriculas")
public class MatriculasController {

    @Autowired
    private MatriculaDAO matriculaDAO;
    @Autowired
    private AlunoDAO alunoDAO;
    @Autowired
    private CursoDAO cursoDAO;

    @RequestMapping("/cadastro")
    public ModelAndView cadastro() {
        ModelAndView modelAndView = new ModelAndView("/matriculas/cadastro");
        List<Alunos> alunos = alunoDAO.listar();
        modelAndView.addObject("alunos", alunos);
        List<Cursos> cursos = cursoDAO.listar();
        modelAndView.addObject("cursos", cursos);
        return modelAndView;
    }

    @RequestMapping("/grava")
    public ModelAndView grava(Matriculas matricula, Alunos aluno, RedirectAttributes redirectAttributes) {
        matriculaDAO.gravar(matricula);
        System.out.println(matricula);
        redirectAttributes.addFlashAttribute("toast","<div id='showtoast'></div>");
        redirectAttributes.addFlashAttribute("mensagem","Matrícula efetuada com sucesso!");
        redirectAttributes.addFlashAttribute("titulo","Sucesso");
        return new ModelAndView("redirect:/matriculas/edita/"+matricula.getId());
    }

    @RequestMapping("/lista")
    public ModelAndView lista(Matriculas matricula) {
        List<Matriculas> matriculas = matriculaDAO.listar();
        ModelAndView modelAndView = new ModelAndView("/matriculas/lista");
        modelAndView.addObject("matriculas", matriculas);
        return modelAndView;
    }

    @RequestMapping("/edita/{id}")
    public ModelAndView edita(@PathVariable("id") Long id) {
        ModelAndView modelAndView = new ModelAndView("matriculas/edita");
        Matriculas matricula = matriculaDAO.buscaID(id);
        modelAndView.addObject("matricula", matricula);
        return modelAndView;
    }

    @RequestMapping("/altera")
    public ModelAndView altera(Matriculas matricula, RedirectAttributes redirectAttributes){
        System.out.println("Alterando ALUNO -> "+matricula);
        matriculaDAO.altera(matricula);
        redirectAttributes.addFlashAttribute("toast","<div id='showtoast'></div>");
        redirectAttributes.addFlashAttribute("mensagem","Matrícula alterada com sucesso!");
        redirectAttributes.addFlashAttribute("titulo","Sucesso");
        return new ModelAndView("redirect:/matriculas/edita/"+matricula.getId());
    }
}

Esse é o DAO

package br.com.rdgcloud.nexus.daos;

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.rdgcloud.nexus.modelos.Matriculas;

@Repository
@Transactional
public class MatriculaDAO {

    @PersistenceContext
    private EntityManager manager;

    public void gravar(Matriculas matricula) {
        manager.persist(matricula);
    }

    public List<Matriculas> listar() {
        return manager.createQuery("select m from Matriculas m", Matriculas.class).getResultList();
    }

    public Matriculas buscaID(Long id) {
        return manager.find(Matriculas.class, id);
    }

    public void altera(Matriculas matricula) {
        manager.merge(matricula);
    }
}

Olá Rodrigo, tudo bem com você?

Tem algumas questões na sua .jspe na estrutura que acredito que estejam trazendo problemas, por exemplo:

Tanto no de curso, quanto de aluno:

<select name="curso_id" class="form-control form-control-sm">
                        <option value="">Selecione o curso desejado</option>
                        <c:forEach items="${cursos}" var="curso">
                            <option value="${curso.id}">${curso.id}| ${curso.nome}</option>
                        </c:forEach>
</select>

Na verdade deveria ser:

<select name="curso.id" class="form-control form-control-sm">

Dado que que queremos pegar o matricula -> curso -> id

Agora tem uma questão em relação as anotações em Matriculas:

Eu concordo com o Mateus, que provavelmente o que você queira seja trabalhar com algo diferente do @OneToOne neste caso, mas vou seguir na ideia que você fez

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id")
    private Alunos aluno;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "id")
    private Cursos curso;

Aqui você está tentando criar na mesma tabela 2 campos com o nome id, na verdade o que provavelmente você quer é algo assim:

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "aluno_id", unique = true)
    private Alunos aluno;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "curso_id", unique = true)
    private Cursos curso;

Em Aluno.java

    @OneToOne(mappedBy = "aluno")
    private Matriculas matricula;


Em Curso.java

    @OneToOne(mappedBy = "curso")
    private Matriculas matricula;

Dessa maneira a tabela Matriculas terá 5 campos:

  • id
  • observacao
  • status
  • aluno_id
  • curso_id

Eu também coloquei o unique = true porque como você está com um 1-1, sem ela você poderia criar mais de uma associação, e na hora de listar daria um erro pois não seria um array

E a última questão é o CascadeType.ALL se você pretende trabalhar dessa maneira no seu Controller você terá que pegar esses objetos do banco de dados, caso contrário, a JPA dará um erro de objeto não gerenciado por ela, então no :

    @RequestMapping("/grava")
    public ModelAndView grava(Matriculas matricula, Alunos aluno, RedirectAttributes redirectAttributes) {

Aqui você também poderia estar retirando o Atributo aluno pois em nenhum momento você está utilizando ele ou coisa do tipo

Você provavelmente precisará do seguinte trecho de código:

Alunos aluno = alunoDAO.buscaID(matricula.getAluno().getId());
Cursos curso = cursoDAO.buscaID(matricula.getCurso().getId());

matricula.setAluno(aluno);
matricula.setCurso(curso);

Mas sinceramente não sei se é necessário essa anotação, dado o caso de uso que você mostrou

Um último ponto é esse exemplo:

        return new ModelAndView("redirect:/matriculas/edita/"+matricula.getId());

Faz tempo que não utilizo jsp mas neste caso você teria que ter um arquivo chamado, por exemplo matriculas/edita/1.jsp, correto?

O ideal seria fazer igual no @RequestMapping("/edita/{id}"), uma jsp genérica e adicionar o objeto criado :)

Acredito que são esses pontos que estão causando confusão :)

No geral eu só pensaria melhor nessa anotação @OneToOne e nesse CascadeType.All

Abraços e Bons Estudos!

Não funcionou da forma que sugeriram

solução!

Se passaram 1 ano e 4 meses, e ainda não foi resolvido

Resolvido em outro local: https://cursos.alura.com.br/forum/topico-relacionamento-manytoone-e-onetomany-197097