Select Multiplo com VRaptor 3.5

31 respostas
java
M

Boa noite amigos.

Estou estudando o VRaptor e está bastante produtivo. Porém, travei no momento que preciso trabalhar com select multiplo. Se faço apenas um select, funciona, agora se seleciono mais de um, vai vazio para o banco.

Aqui meu form com o select:

<select class="selectpicker" multiple name="dentista.especialidades" id="especialidade" required autofocus /> <c:forEach var="especialidade" items="${especialidades}" varStatus="s"> <option value="${especialidade.cod}">${especialidade.nome}</option> </c:forEach> </select>

Meu controller para adicionar:

@Post @Acesso({TipoPerfil.ADMINISTRADOR, TipoPerfil.USUARIO}) public void adiciona(Dentista dentista) { try { this.daoFactory.getDentistaDao().salva(dentista); } catch (Exception ex) { Logger.getLogger(DentistaController.class.getName()).log(Level.SEVERE, null, ex); } result.forwardTo(this).lista(); }

E por fim o atributo de minha classe modelo:

private String especialidades; ...

Poderiam me ajudar nessa questão? Nunca achei que um dia iria precisar fazer um select multiplo…enfim chegou e estou perdidinha nessa situação.

31 Respostas

C

Bom dia,

Altere na classe modelo o private String especialidades; para private List<String> especialidades;.

Ja que você vai selecionar mais de uma especialidade, então o seu atributo deve ser uma lista.

G

@campelo.m estou com esse mesmo problema. Parece ser simples, mas como a amiga relatou, também não estou familiarizado com isso.

Essa dica que você deu, eu fui implementar em minha classe usando o netbeans:

List<String> especialidades;

Ocorre que que fica pedindo relacionamento:

Caused by: org.hibernate.MappingException: Could not determine type for: java.util.List, at table: dentista, for columns: [org.hibernate.mapping.Column(especialidades)]

Isso está me consumindo os neurônios também. Poderia me ajudar?

C

Nesse caso Especialidades deve ser uma tabela no banco de dados, onde o tabela Dentista é ligada com a tabela Especialidades.

@Entity("TBDentista")
public class Dentista {
     ......
     @OneToMany
     private List<Especialidade> especialidades;
     ......
}

@Entity("TBEspecialidades")
public class Especialidade {

    private Long id;
    private String nomeEspecialidade;
    @ManyToOne
    private Dentista dentista;
}
G

@campelo.m funcionou o relacionamento das entidades.

Poderia me ajudar a entender se estou fazendo certo para adicionar os dados na tabela?

Eu criei a classe DentistaEspecialidade:

`@Entity(“TBDentistaEspecialidades”)
public class DentistaEspecialidade {

private Long id;
private String nomeEspecialidade;
@ManyToOne
private Dentista dentista;

}`

Acrescentei o atributo List em Dentista:

@Entity("TBDentista") public class Dentista { ...... @OneToMany private List<DentistaEspecialidade> especialidades; ...... }

No meu formulário, acrescentei o select multiplo:

<select class="form-control" multiple name="dentista.especialidades.nomeEspecialidade" id="especialidade" required autofocus> <option>Espec1</option> <option>Espec2</option> <option>Espec3</option> </select>

Quando clico em Salvar no meu formulário, os dados são inseridos com exceção dos dados do select.
Vou na tabela`de especialidades e a mesma está vazia.

O erro que recebo é esse:
check root cause for details: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: br.com.odonto.modelo.DentistaEspecialidade

M

Ainda não consegui resolver a questão. Segui a dica de @campelo.m e usei o exemplo de @gtalkSP e não vai nem com sabão rsrsrs.

No meu formulário quero que no momento do cadastro eu insira no BD os dados do dentista e as especialidades dele, que no caso serão + de 1…ou seja …o bendito do select multiple.

Alguma sugestão?

C

@gtalkSP,

no name do select coloque assim:
name="dentista.especialidades[].id"

e dentro do option faça assim:

<option value="1" selected="selected">Especialidade 1</option>
<option value="2">Especialidade 2</option>

no getEspecialidades faça o lazy do objeto.

public List<DentistaEspecialidade> getEspecialidades(){
      if(especialidades == null) {
         especialidades = new ArrayList<DentistaEspecialidade>();
      }
      return especialidades;
}

Obs: o correto para essa situação seria criar uma tabela dominio para Especialidade onde a aplicacao faria um “select * from Especialidade” e preencheria o select na tela.
No banco ficaria assim:

TBDentista, TBEspecialidade e TBDentistaEspecialidade.

A ligacao seria N pra N veja aqui como fazer.

C

@Mariana_Fontes, fazendo os passos que passei, voce nao conseguiu avançar, ainda esta no mesmo erro?

M

@campelo.m ainda não.

Pelo que entendi, seguindo sua ajuda, eu tenho duas entidades: Dentista e Especialidade, que agora com sua ajuda se relacionam com as anotações corretas .

Mas aí apareceu o problema de inserir dados. Ou seja, tenho meu formulário com : nome, cro e o campo especialidade… sendo ele um select que o usuário irá usar para selecionar uma ou mais especialidades do dentista a ser cadastrado.

Peguei um form aqui mesmo do GUJ pra essa situação e não funciona. Empaquei bonita nisso.

C

post o seu codigo e mostre onde parou

M

Vou fazer essa sua sugestão que você postou para o @gtalkSP . @campelo.m mais uma vez obrigada pela boa vontade em ajudar.

Se continuar o problema, volto a falar. E caso resolva, avisarei e posto a solução.

C

Entao,
Pelo que entendi, o problema seu é o mesmo do dele, entao voce deve usar todas as dicas nesse topico para resolver o seu tambem.

M

Fiz assim:

Coloquei o select no form:

<div class="row"> <div class="form-group col-md-3"> <label for="teste">Especialidades</label> <select class="form-control" id="teste" name="dentista.especialidades[].id"> <option value="1" selected="selected">Especialidade 1</option> <option value="2">Especialidade 2</option> </select> </div> </div>

E deu esse erro:

java.lang.Exception: Ocorreu um erro a tentar inserir: object references an unsaved transient instance - save the transient instance before flushing: br.com.odonto.modelo.DentistaEspecialidade at br.com.odonto.dao.Dao.salva(Dao.java:38) at br.com.odonto.controller.DentistaController.adiciona(DentistaController.java:44) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Meu controler é este:

@Post @Acesso({TipoPerfil.ADMINISTRADOR, TipoPerfil.USUARIO}) public void adiciona(Dentista dentista) { try { this.daoFactory.getDentistaDao().salva(dentista); } catch (Exception ex) { Logger.getLogger(DentistaController.class.getName()).log(Level.SEVERE, null, ex); } result.forwardTo(this).lista(); }

Ainda não entendi toda essa dificuldade… não era pra ser tão complicado assim rsrs

Eu não implementei esse método que você postou, não entendi a finalidade dele:

public List<DentistaEspecialidade> getEspecialidades(){ if(especialidades == null) { especialidades = new ArrayList<DentistaEspecialidade>(); } return especialidades; }

C

Bom dia,

O metodo getEspecialidades é um @Override, ele serve para não dar NullPointerException.

java.lang.Exception: Ocorreu um erro a tentar inserir: object references an unsaved transient instance - save the transient instance before flushing:

Esse erro ocorre por que o objeto DentistaEspecialidade esta com o id preenchido, entao deveria ser updade.

Altere os options para:

<option selected="selected">Especialidade 1</option>
<option >Especialidade 2</option>

ou altere o mapeamento da lista para:

@OneToMany(cascade=CascadeType.ALL)
private List<DentistaEspecialidade> especialidades;
M

@campelo.m , acho que está evoluindo… pelo menos o erro mudou …deu esse erro:

java.lang.Exception: Ocorreu um erro a tentar inserir: Could not execute JDBC batch update

Vou acrescentar mais informações, vai que estou esquecendo de algo e minha cara vai pegar fogo…

O form que estou usando:

<form name="formAddDentista" id="formAddDentista" action="<c:url value="/dentista/adiciona"/>" method="post"> <input type="hidden" name="dentista.cod" id="dentistaCod" class="id" value="${dentista.cod}"> ...OUTROS INPUTS ... <label for="teste">TESTE</label> <select class="form-control" id="teste" name="dentista.especialidades[].cod"> <option selected="selected">Especialidade 1</option> <option >Especialidade 2</option> </select>

A linha que você indicou no select:

form-control" id=“teste” name="dentista.especialidades[].id

Alterei para:

form-control" id=“teste” name="dentista.especialidades[].cod

Pensei que o erro era por estar usando “id” ao invés de “cod”… porém com ambas as formas o erro é o mesmo.

Estou estudando apostila do VRaptor pra ver se tem alguma dica pra resolver esse “mistério”. Acredito que não seja nada de outro mundo…devo estar errando em algo básico que não identifiquei ainda.

M

Boa noite! Resolvido. Realmente era uma bobeira que não estava enxergando.

Quem tiver o mesmo problema, basta seguir a dica do @campelo.m que vai funcionar.

O que coloquei e resolveu meu problema foi:

<select class="form-control" id="especialidades" multiple name="dentista.especialidades[].nomeEspecialidade">

Obrigado a todos.

M

Oi gente, voltei.

Agora que estou inserindo os dados de meu formulário com campo select multiple, me deparei com um problema.

Abri a tabela de meu BD e observei que não está salvando a coluna com o ID do dentista.

Aqui meu controller :

@Post @Acesso({TipoPerfil.ADMINISTRADOR, TipoPerfil.USUARIO}) public void adiciona(Dentista dentista) { try { this.daoFactory.getDentistaDao().salva(dentista); } catch (Exception ex) { Logger.getLogger(DentistaController.class.getName()).log(Level.SEVERE, null, ex); } result.redirectTo(this).lista(); }

E aqui o campo select do meu form que funciona agora:

<div class="form-group col-md-3"> <label for="especialidades">Especialidade</label> <select class="form-control" id="especialidades" multiple name="dentista.especialidades[].nomeEspecialidade"> <option value="Especialidade 1">Especialidade 1</option> <option value="Especialidade 2" >Especialidade 2</option> </select> </div>

Minhas entidades, estão anotadas no início deste post conforme a dica do @campelo.m .
Teria algo a colocar no meu controler pra salvar o id do dentista na tabela? (:blush:

C

Para que funcione assim, voce tem que colocar o dentista dentro da especialidade.
especialidade.setDentista(Dentista)

M

Oi @campelo.m . Seria assim, no método adiciona?

`public void adiciona(Dentista dentista,DentistaEspecialidade dentistaEspecialidade) {
try {

this.daoFactory.getDentistaDao().salva(dentista);
        dentistaEspecialidade.setDentista(dentista);
    } catch (Exception ex) {
        Logger.getLogger(DentistaController.class.getName()).log(Level.SEVERE, null, ex);
    }
    result.redirectTo(this).lista();
}`

Fiz e continua indo “null” . No próprio formulário não é possível passar ?
Por exemplo:

<input type="hidden" name="dentistaEspecialidade.cod" id="dentistaEspecialidadeCod" value="${dentistaEspecialidade.cod}">

Tentei e também não foi. Fiz a depuração pelo Netbeans e observei que vai null mesmo:

C

@Mariana_Fontes

Não, voce deve fazer o set do dentista antes de fazer o save.

M

Não vai @campelo.m . Gente… isso não era pra ser tão difícil…dá vontade de chorar por ficar horas nisso.:flushed:

Removi todos os atributos de minha classe e deixei ela só com nome e a lista de especialidades:

`public class Dentista implements Serializable {

private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "dentista_cod")
private Long cod;
private String nome;
@OneToMany(cascade=CascadeType.ALL)
private List<DentistaEspecialidade> especialidades;

//Construtor
public Dentista() {
}
//Getters e Setters`

Agora a classe de especialidades:

public class DentistaEspecialidade implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "especialidades_cod") private Long cod; private String nomeEspecialidade; @ManyToOne private Dentista dentista;

E por fim meu form:

`form name=“formAddDentista” id=“formAddDentista” action="<c:url value="/dentista/adiciona"/>" method=“post”>

<div class="row">
    <div class="form-group col-md-3">
      <input type="text" name="dentista.nome" id="dentistaNome" class="id" value="${dentista.nome}">  
    </div>
    <div class="form-group col-md-3">
        <label for="especialidades">Especialidade</label>
        <select class="form-control" id="especialidades" multiple name="dentista.especialidades[].nomeEspecialidade">
            <option value="Especialidade 1">Especialidade 1</option>
            <option value="Especialidade 2" >Especialidade 2</option>
        </select>
    </div>
</div>`

O controller do método adicionar:

`public void adiciona(Dentista dentista, DentistaEspecialidade dentistaEspecialidade) {

try {

dentistaEspecialidade.setDentista(dentista);

this.daoFactory.getDentistaDao().salva(dentista);
} catch (Exception ex) {
        Logger.getLogger(DentistaController.class.getName()).log(Level.SEVERE, null, ex);
    }
    result.forwardTo(this).lista();
}`

Não funciona. Agora só salva dentista… não vai nada pra tabela de especialidades. Que coisa chata isso @campelo.m ! Desculpa por te encher.

C

Eh possivel voce colocar o projeto no github e compartilhar o link?

M

Oi @campelo.m o link é esse:
git

C

Apague esta linha

<input type="hidden" name="dentista.especialidades.cod" id="dentistaEspCod" class="id" value="${dentista.especialidades.cod}">

Altere o metodo adiciona para:

public void adiciona(Dentista dentista) {
        try {
            for(Especialidade especialidade: dentista.getEspecialidades){
               especialidade.setDentista(dentista);
            }
            this.daoFactory.getDentistaDao().salva(dentista);
            
        } catch (Exception ex) {
            Logger.getLogger(DentistaController.class.getName()).log(Level.SEVERE, null, ex);
        }
        result.forwardTo(this).lista();
    }

Veja se assim voce consegue salvar corretamente.

Obs: De uma olhada nesse tutorial para melhorar o(s) seu(s) mapeamento(s).

M

@campelo.m , obrigado por sua boa vontade. Mas infelizmente ainda não deu.

Fiz oque você pediu, mudando de Especialidade para DentistaEspecialidade:

`public void adiciona(Dentista dentista) throws Exception {

for(DentistaEspecialidade dentistaEspecialidade: dentista.getEspecialidades()){
           dentistaEspecialidade.setDentista(dentista);
        }
    this.daoFactory.getDentistaDao().salva(dentista);

    result.forwardTo(this).lista();
}`

e ao dar o submit no formulario dá essa msg:

14-Mar-2016 18:09:02.090 SEVERE [http-nio-8084-exec-75] br.com.odonto.controller.DentistaController.adiciona null

java.lang.Exception: Ocorreu um erro a tentar inserir: Could not execute JDBC batch update

at br.com.odonto.dao.Dao.salva(Dao.java:38)

at br.com.odonto.controller.DentistaController.adiciona(DentistaController.java:48)

Concordo com sua lógica, jurava que ia funcionar !!! :cold_sweat: oque será que está acontecendo que não vai!!!

C

Se possivel, mande o projeto inteiro para o meu email por favor, vou olhar com mais calma.
[email removido]

M

Oi @campelo.m . Acabei de enviar via DropBox.

obrigada mais uma vez.

M

@campelo.m vi que você comentou sobre isso.

Olhei meu banco e apareceu uma terceira tabela.

Observei que tenho a tabela dentista, especialidade e agora dentista_dentistaespecialidade .

Então eu preencho os dados do meu formulário com select multiple, clico em salvar. Ocorre que os dados são salvos na tabela dentista… já na tabela especialidade salva o nome mas o ID do dentista não é salvo… e na tabela dentista_especialidade que encontrei hoje, lá é salvo o id do dentista e o id da especialidade.

Portanto, acho que está certo. Agora é correto a tabela “especialidade” ficar com o campo ID de dentista vazio?

Tabela dentista - Salva todos os dados…(não coloquei a imagem, pois nessa tabela, tudo é inserido_

Tabela dentistaespecialidades - Não salva o ID de Dentista:

Tabela dentista_especialidades (descobri hoje essa tabela)

Outra coisa, como recupero os dados da tabela dentista_especialidade? Por exemplo, se quero editar os dados de dentista? Fiz um form, e peguei os dados de dentista, já das especialidades não sei como pegar, já que estão nessa terceira tabela que descobri hj rsrsrrs.

C

Bom dia Mariana,

no Caso a tabela especialidade é uma tabela de dominio, ou seja, ela ja deve estar preenchida quando voce subir a app e ter mais uma funcionalidade para dar manutenção, CRUD basico.

Essas tres tabelas existes pelo fato que voce esta fazendo um mapeamento “n para n”, na tabela dentista_especialidade nao precisa ter o id do dentista, pois a ligação esta na tabela do meio.

O hibernate recuperar esses dados, quando voce faz “dententista.getEspecialidades()”, o valor ja devera esta preenchido.

M

Ok, vou fazer testes disso então. Obrigada

M

Boa noite @campelo.m . Ainda estou estudando isso.

Então, achei algo semelhante ao que quero e vou usar ele como exemplo e se funcionar farei no meu sistema.

Achei uma dúvida parecida com a minha, onde um usuário cita a situação Advogado e Estagiário. Pensei nisso e por ter nomes mais “simples”, criei outra aplicacação web para eu aprender.

Nessa situação eu criei as entidades Advogado e Estagiário, relacionei as tabelas, criei os DAO e Controllers.

Rodei a aplicação e foram criadas as 3 tabelas que você me explicou, porém agora na situação:
Tabelas advogado, estagiario e advogado_estagiario.

Na tabela advogado, foram todos os dados do formulario. Na tabela estagiario foram os dados de id,nome do estagiario e id do advogado não foi (igual ao meu sistema).
E na tabela advogado_estagiario (Terceira tabela explicada por você) preencheu o id do estagiario e id advogado.

Até aí tudo bem.

Agora @campelo.m como eu pego os dados pra editar?

Meu controller:

@Acesso({TipoPerfil.ADMINISTRADOR}) public void edita(Long cod) { result.include("advogado", daoFactory.getAdvogadoDao().carrega(cod)); //Pego o id do objeto advogado e preencho o form para edição. }

Onde eu colocaria “dententista.getEspecialidades()” que nesse teste alterei para: “advogado.getEstagiarios()

Dentro desse controller?

C

veja como esta o seu select, e coloque um fetch nos join.

Select a from Advogado as a fetch join a.estagiarios es

Creio que o advogado e dentista nao tenha a mesma relação que voce disse… O advogado tem uma condição de estagiario, entao bastaria colocar uma flag na table advogado que voce ja saberia se ele é estagiario.

Advogados tambem tem a mesma condição de dentistas, um advogado pode ter varias especialidades(criminal, familiar, etc).

Criado 6 de março de 2016
Ultima resposta 24 de mar. de 2016
Respostas 31
Participantes 3