Dificuldade ao persistir

Bom dia!

Estou com dificuldade em persistir dados em uma tabela com relacionamento @ManyToMany, quando uso @ManyToOne funciona tudo perfeito, porém com muitos para muitos não estou conseguindo. Possuo 3 tabelas no banco, cnai, unidades e unidades_cnai. Segue o código:

Classe com relacionamento @ManytoMany

@Entity
@Table(name = "unidades")
public class Unidade {
	@Id
	@GeneratedValue
	private Integer id;

        //Atributos

	@ManyToMany
	@JoinTable(name = "unidades_cnai")
	private List<Cnai> cnai;

       //Geters e Setters

	public List<Cnai> getCnai() {
		EntityManager em = new JPAUtil().getEntityManager();
		CnaiDAO dao = new CnaiDAO(em);

		return dao.getLista();
	}

	public void setCnai(List<Cnai> cnai) {
		this.cnai = cnai;
	}

}

Classe cnai

@Entity
@Table(name = "cnai")
public class Cnai {
	@Id
	@GeneratedValue
	private Integer id;

	private String codigo;
	private String descricao;
	private String GR;
     
        //Geters e Setters
}

View de cadastro de unidades, local onde devo selecionar o Cnais a serem cadastrados

<td> 
 <select size="4" multiple="multiple" name="cnai">
 <c:forEach var="cnai" items="${dao.lista}">
 <option value="${cnai.id}">${cnai.codigo}</option>
 </c:forEach>
 </select>
</td>

Quando clico o botão de gravar, grava uma nova unidade perfeitamente com tudo que foi passado pela view exeto o cnai, que não é gravado em lugar algum. Alguém pode ajudar?

Esta faltando anotação ai:

Obs: retirado do blog do uaiHebert, muito bom por sinal

http://uaihebert.com/?p=52

	@ManyToMany
	@JoinTable(name = "unidades_cnai", joinColumns = { @JoinColumn(name = "unidades_id") }, inverseJoinColumns = { @JoinColumn(name = "cnai_id") })
	private List<Cnai> cnai;

Cara, mudei como você citou porém nenhum avanço, nada é gravado na tabela unidades_cnai :frowning:

Cara, @ManyToMany com @JoinColumn?

Você realmente precisa entender o que é um relacionamento, e para que serve e quando utilizar o @JoinColumn.

JPA: Mini Livro - Primeiros passos e conceitos detalhados

Veja esse post, fala dos 3 relacionamentos. Com 10min de leitura você vai entender e vai conseguir fazer.

[quote=Hebert Coelho]Cara, @ManyToMany com @JoinColumn?

Você realmente precisa entender o que é um relacionamento, e para que serve e quando utilizar o @JoinColumn.

JPA: Mini Livro - Primeiros passos e conceitos detalhados

Veja esse post, fala dos 3 relacionamentos. Com 10min de leitura você vai entender e vai conseguir fazer.[/quote]

O meu caso seria um relacionamento Unidirecional, de acordo com o Mini Livro que você me passou está correto (se não estou equivocado), apenas mudou os nomes em no lugar de person_dog ficou unidades_cnai, no lugar de person_id ficou unidades id e no lugar de dog_id ficou cnai_id.

Segue o link de consuta: http://uaihebert.com/?p=1622&page=21

Alguma ideia do por que não estou conseguindo gravar? será algo com a interface web?

Realizei algumas mudanças e boa noticia, está gravando na tabela unidades_cnai, porém ele grava TODOS os cnais e não apenas os que eu selecionei na minha lista na web. Alguma dica? Segue o código:

Unidades:

[code]
@Entity
@Table(name = “unidades”)
public class Unidade {
@ManyToMany
@JoinTable(name = “unidades_cnai”, joinColumns = @JoinColumn(name = “unidades_id”), inverseJoinColumns = @JoinColumn(name = “cnai_id”))
private List cnai;

public List<Cnai> getCnai() {
	EntityManager em = new JPAUtil().getEntityManager();
	CnaiDAO dao = new CnaiDAO(em);

	return dao.getLista();
}

public void setCnai(List<Cnai> cnai) {
	this.cnai = cnai;
}

}[/code]

Cnai (permanece sem modificações):

[code]
@Entity
@Table(name = “cnai”)
public class Cnai {
@Id
@GeneratedValue
private Integer id;

private String codigo;
private String descricao;
private String GR;
    //getters e setters

}[/code]

Método que faz a gravação no banco:

	@RequestMapping("adicionaUnidade")
	public String adicionaUnidade(@Valid Unidade unidade, BindingResult result,
			Model model) {

		EntityManager em = new JPAUtil().getEntityManager();
		UnidadeDAO dao = new UnidadeDAO(em);
		em.getTransaction().begin();
                
                 //regras de validação
		unidade.setCnai(unidade.getCnai());

		dao.getAdiciona(unidade);
		em.getTransaction().commit();
		em.close();

		return "redirect:cadastroUnidade";
	}

Interface web:

<td> 
 <select size="4" multiple="multiple" name="cnai.id">
 <c:forEach var="cnai" items="${dao.lista}">
 <option value="${cnai.id}">${cnai.codigo}</option>
 </c:forEach>
 </select>
</td>

Cara, pq seu método public List getCnai() está indo ao banco de dados?

Você leu ou só comparou o código? O meu lá está com @JoinColumn?

Sério, leia o post… não só aquela página, mas leia também o que é uma entity.

[quote=Hebert Coelho]Cara, pq seu método public List getCnai() está indo ao banco de dados?

Você leu ou só comparou o código? O meu lá está com @JoinColumn?

Sério, leia o post… não só aquela página, mas leia também o que é uma entity.[/quote]

Fiz o get com banco pois fazendo apenas o get normal não consegui resultado. Por exemplo:

	public List<Cnai> getCnai() {
		return cnai;
	}

Não grava nada no banco de dados. E o get com o list do DAO grava TODOS no banco de dados. Seria algum problema com minha lista na interface web?

Ele grava todos porque você está carregando todos no seu get. Use um get normal, adicione na lista apenas os itens que você quer e depois grave.

Como citei acima, uso Get normal e não gera nenhum registro, alguma ideia do por que?

Como citei acima, uso Get normal e não gera nenhum registro, alguma ideia do por que?[/quote]

Dá algum erro? Não acontece nada? Sua lista está instanciada?

Fiz um teste para ver o retorno da lista, retornou vazia! As outras classes estão iguais

@RequestMapping("adicionaUnidade")
	public String adicionaUnidade(@Valid Unidade unidade, BindingResult result,
			Model model) {

		EntityManager em = new JPAUtil().getEntityManager();
		UnidadeDAO dao = new UnidadeDAO(em);
		em.getTransaction().begin();

		if (result.hasFieldErrors("sigla")) {
			model.addAttribute("erro", unidade);
			return "/planejamento/unidades_cadastrar";
		} else if (result.hasFieldErrors("descricao")) {
			model.addAttribute("erro", unidade);
			return "/planejamento/unidades_cadastrar";
		}

		System.out.println("O VALOR DO CNAI ESTA AQUI: " + unidade.getCnai());

		dao.getAdiciona(unidade);
		em.getTransaction().commit();
		em.close();

		return "redirect:cadastroUnidade";
	}

No console recebi: “O VALOR DO CNAI ESTA AQUI: []”

Cara, coloca try/catch e use o método printStrackTace no Catch para ver qual é o erro e onde está.
Pra ficar mais fácil, cria uma classe com um main e tente:

try{
  Unidade unidade = new Unidade();
  Cnai cnai = new Cnai();
   unidade.getCnai().add(cnai);
  //ou cria um método para adicionar direito
  //unidade.adicionarCnai(cnai);
  } catch(Exception e){
   e.printSTrackTace();
}

Se não der erro, vá testando adicionar no banco, imprimiar a lista etc. Provavelmente é porque sua lista não está instanciada, olhando pelo código que você postou.

Cara eu acho que meu problema está no Cnai passado por uma lista na web ( ) , acho que ela não gera um objeto Cnai para ser persistido, tentei fazer unidade.getCnai().getId(), porém não era possível acessar o Id por ser um get de Lista, há uma forma de saber quais selecionei na minha interface e instanciá-los para então add a lista?

Com objeto Cnai podendo ser adicionado a lista creio que funcione perfeitamente!

Tentando fazer:

		List<Cnai> cnai = new ArrayList<Cnai>();

		cnai.add((Cnai) unidade.getCnai());

ao tentar salvar gera um erro falando que o Objeto passado não é um Cnai …

Porque não é. É uma lista de Cnai, não um cNai.
Você pode fazer:

unidade.getCnai().get(0); //pega o primeiro da lista

Ai sim pegará um objeto Cnai da lista. Dá pra copiar uma lista também. Se não me engano o método é o addAll();

Segue o que aconteceu…

Metodo que grava:

	@RequestMapping("adicionaUnidade")
	public String adicionaUnidade(@Valid Unidade unidade, BindingResult result,
			Model model) {

		EntityManager em = new JPAUtil().getEntityManager();
		UnidadeDAO dao = new UnidadeDAO(em);
		em.getTransaction().begin();

		if (result.hasFieldErrors("sigla")) {
			model.addAttribute("erro", unidade);
			return "/planejamento/unidades_cadastrar";
		} else if (result.hasFieldErrors("descricao")) {
			model.addAttribute("erro", unidade);
			return "/planejamento/unidades_cadastrar";
		}

		List<Cnai> cnai = new ArrayList<Cnai>();

		cnai.add(unidade.getCnai().get(0));

		System.out.println("O VALOR DO CNAI ESTA AQUI " + cnai);

		dao.getAdiciona(unidade);
		em.getTransaction().commit();
		em.close();

		return "redirect:cadastroUnidade";
	}

Front end (acho que o problema ta por aqui em algum lugar)

<td> 
 <select size="4" multiple="multiple" name="cnai.id">
 <c:forEach var="cnai" items="${dao.lista}">
 <option value="${cnai.id}">${cnai.codigo}</option>
 </c:forEach>
 </select>
</td>

E o erro dizendo que a lista tem tamanho 0

exception

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:681)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:585)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

root cause

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	java.util.ArrayList.rangeCheck(ArrayList.java:571)
	java.util.ArrayList.get(ArrayList.java:349)
	br.com.e2s.controller.UnidadesController.adicionaUnidade(UnidadesController.java:55)
	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.lang.reflect.Method.invoke(Method.java:616)
	org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)
	org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:585)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

Meu front não tem algum problema que não está passando os Cnais?

Chamar o dao na tela é errado, mas desconsiderando isso…

Como você está pegando os itens selecionados? Como está adicionando eles na unidade?

[quote=ErickRAR]Chamar o dao na tela é errado, mas desconsiderando isso…

Como você está pegando os itens selecionados? Como está adicionando eles na unidade?[/quote]

Eu apenas passo o ID pela lista na web, e uso o método adiciona, com relacionamento @ManyToOne sempre funcionou, observe:

metodo que adiciona uma gerência, por exemplo:

@RequestMapping("/gravarGerencia")
	public String gravarGerencia(@Valid Gerencia gerencia, BindingResult result) {
                //Validações
		EntityManager em = new JPAUtil().getEntityManager();
		GerenciaDAO dao = new GerenciaDAO(em);
		em.getTransaction().begin();

		dao.getAdiciona(gerencia);
		em.getTransaction().commit();
		em.close();

		return "redirect:cadastroGerencia";
	}

Front desse metodo

<td>
	<select id="unidade" name="unidade.id">
		<option></option>
		<c:forEach var="unidade" items="${dao.lista}">
			<option value="${unidade.id}">${unidade.descricao}</option>
		</c:forEach>
	</select>
</td>						

Nesse caso uma gerência recebe uma unidade, funciona perfeitamente. Pensei em montar o Cnai no método que adiciona, porém por ser uma lista não é possível então não sei o que fazer!

O meu get não devia pegar o que eu seleciono na lista da web e enviar como unidades.getCnai() ?

Alguém tem ideia de como posso passar o Cnai da minha lista da interface web para minha lista do Java, ou resgatar os valores de ID para que eu possa fazer uma busca e monta-lo