Hibernate, JPA - Problemas com relacionamento ManyToMany

Pessoal, travei no meu projeto (TCC), e acredito que seja por causa desse relacionamento.

Bom, vamos ao que interessa.

No projeto irei cadastrar novos perfis de usuário, onde irei escolher as permissões que o perfil terá.
Utilizo JSF, Spring Security, Hibernate, JPA e PrimeFaces.

Possuo as seguintes tabelas no banco:


--------------------PERFIL---------------------

----------ID_PERFIL----------NOME----------


-----------PERFIL_PERMISSAO--------------

------ID_PERFIL------ID_PERMISSAO------


-----------------PERMISSAO------------------

----ID_PERMISSAO-----NOME----ROLE----

Classe Perfil

@Entity
@Table(name="PERFIL")
public class Perfil implements Serializable{
	private static final long serialVersionUID = 1L;
	private int id;
	private String nome;
	private List<Permissao> permissoes;
	
	@Id
	@Column(name="ID_PERFIL")
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	@Column(name="NOME")
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	
	@ManyToMany
	@LazyCollection(LazyCollectionOption.FALSE)
	@JoinTable(name = "PERFIL_PERMISSAO",    
	joinColumns = { @JoinColumn(name = "ID_PERFIL", referencedColumnName="ID_PERFIL")},  
	inverseJoinColumns={@JoinColumn(name="ID_PERMISSAO", referencedColumnName="ID_PERMISSAO")})
	public List<Permissao> getPermissoes() {
		return permissoes;
	}
	public void setPermissoes(List<Permissao> permissoes) {
		this.permissoes = permissoes;
	}
}

Acredito que é aqui onde esteja o problema, nesse relacionamento ManyToMany, pois não entendi muito bem como utilizá-lo.

Classe Permissao

@Entity
@Table(name="PERMISSAO")
public class Permissao {
	private int id;
	private String nome;
	private String role;
	
	@Id
	@Column(name="ID_PERMISSAO")
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
	@Column(name="NOME")
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	
	@Column(name="ROLE")
	public String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
}

Persistência Perfil

public class PerfilHIB {
	public void salvar(Perfil perfil) {
		Session session = HibernateUtil.getSession();
		Transaction tx = session.beginTransaction();
		session.saveOrUpdate(perfil);
		tx.commit();
		session.close();
	}
	public void excluir(Perfil perfil) {
		Session session = HibernateUtil.getSession();
		Transaction tx = session.beginTransaction();
		session.delete(perfil);
		tx.commit();
		session.close();
	}

	@SuppressWarnings("unchecked")
	public List<Perfil> listar() {
		Session session = HibernateUtil.getSession();
		try {
			return session.createCriteria(Perfil.class).addOrder(Order.asc("id")).list();
		} finally {
			session.close();
		}
	}
	public Perfil consultar(int id) {
		Session session = HibernateUtil.getSession();
		try {
			Perfil perfil = (Perfil) session.get(Perfil.class, id);
			return perfil;
		} finally {
			session.close();
		}
	}
}

Perfil Managed Bean

public class PerfilMB implements Serializable{
	private static final long serialVersionUID = 1L;
	private Perfil perfil;
	private List<Perfil> perfis;
	
	
	public PerfilMB(){
		this.perfil = new Perfil();
		setPerfis(null);
	}
	
	public String listar(){
		this.perfil = new Perfil();
		this.perfis = new PerfilHIB().listar();
		return "success";
	}
	
	public String salvar(){
			if (this.perfil.getId() == 0){
				JSFMensageiro.info("Perfil incluido com sucesso!");
			}
			else{
				JSFMensageiro.info("Perfil alterado com sucesso.");  
			}
			new PerfilHIB().salvar(this.perfil);
			this.perfil = new Perfil();
			perfis = new PerfilHIB().listar();
			
			return "success";
	}
	
	public void excluir() {
		try {
			new PerfilHIB().excluir(getPerfil());
			JSFMensageiro.info("Perfil excluido com sucesso!");
		} catch (ConstraintViolationException e) {
			e.printStackTrace();
			JSFMensageiro.error("Erro inesperado ao excluir Consultor!");
		}		
		this.perfil = new Perfil();
		this.setPerfis(new PerfilHIB().listar());
	}

	public Perfil getPerfil() {
		return perfil;
	}

	public void setPerfil(Perfil perfil) {
		this.perfil = perfil;
	}
	
	public List<Perfil> getPerfis() {
		if (this.perfis == null)
			this.perfis = new PerfilHIB().listar();		
		return this.perfis;
	}

	public void setPerfis(List<Perfil> perfis) {
		this.perfis = perfis;
	}
}

Já está listando as permissões tudo corretamente, porém na hora de salvar um perfil surge o problema:

Advertência: #{perfilMB.salvar}: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entityBean.Permissao.id
javax.faces.FacesException: #{perfilMB.salvar}: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entityBean.Permissao.id
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
	at javax.faces.component.UICommand.broadcast(UICommand.java:315)
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
	at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:343)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:109)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:97)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:100)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:78)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:35)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:177)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:187)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:355)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:149)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:931)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Caused by: javax.faces.el.EvaluationException: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entityBean.Permissao.id
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
	... 50 more
Caused by: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entityBean.Permissao.id
	at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:187)
	at org.hibernate.tuple.entity.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:341)
	at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:4273)
	at org.hibernate.type.EntityType.toLoggableString(EntityType.java:507)
	at org.hibernate.type.CollectionType.renderLoggableString(CollectionType.java:193)
	at org.hibernate.type.CollectionType.toLoggableString(CollectionType.java:184)
	at org.hibernate.internal.util.EntityPrinter.toString(EntityPrinter.java:79)
	at org.hibernate.internal.util.EntityPrinter.toString(EntityPrinter.java:121)
	at org.hibernate.event.internal.AbstractFlushingEventListener.logFlushResults(AbstractFlushingEventListener.java:131)
	at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:107)
	at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
	at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1127)
	at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:325)
	at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
	at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
	at persistencia.PerfilHIB.salvar(PerfilHIB.java:19)
	at managedBean.PerfilMB.salvar(PerfilMB.java:35)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.apache.el.parser.AstValue.invoke(AstValue.java:278)
	at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:274)
	at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
	... 51 more
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:164)
	... 75 more

Estou há vários dias encalhado nisso, pesquisei bastante, mas nada. Minha esperança são vocês!

Não sei se ficou claro o problema. Caso precise mostrar mais alguma coisa pra entender melhor, é só dizer.

Obrigado! =)

Você esta adicionando o valor correto para o id do permissão??

Desculpe, não entendi corretamente sua pergunta. Você quis dizer o nome do campo ID da PERMISSAO?
Pra ter uma visão melhor, tá aqui o form do Perfil.

				<h:outputLabel value="Nome:" for="nome" />
				<p:inputText id="nome" value="#{perfilMB.perfil.nome}" label="nome" size="50" maxlength="50" required="true" >
				<p:message for="nome" update="panel"/>
				</p:inputText>

				<h:outputLabel value="Permissões:" />
				<p:selectManyCheckbox value="#{perfilMB.perfil.permissoes}" layout="pageDirection">
					<f:selectItems value="#{permissaoMB.permissoes}" var="permissao"   itemLabel="#{permissao.nome}" itemValue="#{permissao.id}" />
				</p:selectManyCheckbox>

				<f:facet name="footer">
					<p:commandButton value="Salvar" action="#{perfilMB.salvar}" />
					<p:commandButton value="Cancelar" action="#{perfilMB.listar}" immediate="true" />
				</f:facet>

Alguém? :frowning:

Voce esta tentando salvar uma lista ?

Não entendi uma coisa, me mostre onde estão os atributos identificados no referencedColumnName

@ManyToMany @LazyCollection(LazyCollectionOption.FALSE) @JoinTable(name = "PERFIL_PERMISSAO", joinColumns = { @JoinColumn(name = "ID_PERFIL", referencedColumnName="ID_PERFIL")}, inverseJoinColumns={@JoinColumn(name="ID_PERMISSAO", referencedColumnName="ID_PERMISSAO")}) public List<Permissao> getPermissoes() { return permissoes; }
Pois eu só achei

@Id  
    @Column(name="ID_PERMISSAO")  
    public int getId() {  
        return id;  
    }  
//E
 @Id  
    @Column(name="ID_PERFIL")  
    public int getId() {  
        return id;  
    }  

Ou seja, esses referencedColumnName deveriam estar assim

@ManyToMany @LazyCollection(LazyCollectionOption.FALSE) @JoinTable(name = "PERFIL_PERMISSAO", joinColumns = { @JoinColumn(name = "ID_PERFIL", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="ID_PERMISSAO", referencedColumnName="id")}) public List<Permissao> getPermissoes() { return permissoes; }
Ou estou errado?

Isso, quero salvar uma lista de Permissões assiciadas a um Perfil.

No banco de dados ficaria assim:

PERFIL


ID_PERFIL Nome
1 Administrador

PERMISSAO


ID_PERMISSAO Nome
1 Perm1
2 Perm2
3 Perm3

PERFIL_PERMISSAO


ID_PERFIL ID_PERMISSAO
1 1
1 2
1 3

[quote=drsmachado]Não entendi uma coisa, me mostre onde estão os atributos identificados no referencedColumnName

@ManyToMany @LazyCollection(LazyCollectionOption.FALSE) @JoinTable(name = "PERFIL_PERMISSAO", joinColumns = { @JoinColumn(name = "ID_PERFIL", referencedColumnName="ID_PERFIL")}, inverseJoinColumns={@JoinColumn(name="ID_PERMISSAO", referencedColumnName="ID_PERMISSAO")}) public List<Permissao> getPermissoes() { return permissoes; }
Pois eu só achei

@Id  
    @Column(name="ID_PERMISSAO")  
    public int getId() {  
        return id;  
    }  
//E
 @Id  
    @Column(name="ID_PERFIL")  
    public int getId() {  
        return id;  
    }  

Ou seja, esses referencedColumnName deveriam estar assim

@ManyToMany @LazyCollection(LazyCollectionOption.FALSE) @JoinTable(name = "PERFIL_PERMISSAO", joinColumns = { @JoinColumn(name = "ID_PERFIL", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="ID_PERMISSAO", referencedColumnName="id")}) public List<Permissao> getPermissoes() { return permissoes; }
Ou estou errado?[/quote]

Então, com ou sem esse referencedColumnName dá o mesmo erro. E do jeito que você disse também continua a mesma coisa. =/

Estou ficando louco, muito confuso esse erro.

Acredito que o erro esteja aqui:

if (this.perfil.getId() == 0){  
           JSFMensageiro.info("Perfil incluido com sucesso!");  
}

Voce ta passando uma lista para o save, porem ai voce pega o ID, mas nao da lista.

[quote=rof20004]Acredito que o erro esteja aqui:

if (this.perfil.getId() == 0){  
           JSFMensageiro.info("Perfil incluido com sucesso!");  
}

Voce ta passando uma lista para o save, porem ai voce pega o ID, mas nao da lista.
[/quote]

Desculpe, acho que não expliquei direito. ^^
Estou querendo salvar apenas um Perfil, onde neste Perfil contêm uma lista de Permissões.

Ahhhhh…desculpa, heheheh.

Bom, no meu caso, vo da uma pesquisada aqui, se encontrar alguma discrepancia lhe aviso, ate mesmo porque to comecando agora em JSF.

Flw e boa sorte.

Velho coloca o ID do tipo integer, testa e manda o erro devolta o relacionamento ao meu ver está correto.

Mas todos os IDs já são do tipo int.

Ou você quis dizer outra coisa?

Não eles estão do tipo int, e não do tipo da classe Integer, coloca ai Integer id, e renova os métodos get and set dele

É padrão e ja li em alguns lugares que o tipo do ID tem que ser integer, você está utilizando JPQL no applicatonContext-security.xml para fazer a validação de quem tem determinada permissão, não é? Se sim passe essa JQPL para que possamos analisar.

Desculpe a ignorância. ^^
Pra mim era tudo a mesma coisa.
Eu sempre utilizei int ou long, e nunca tive problemas.

Fiz do jeito que você disse e ganhei um NullPointerException:

Dez 24, 2012 4:29:21 PM com.sun.faces.renderkit.html_basic.HtmlBasicRenderer getForComponent
Advertência: Não foi possível encontrar o componente com a ID permissoes na exibição.
Dez 24, 2012 4:29:24 PM com.sun.faces.lifecycle.InvokeApplicationPhase execute
Advertência: #{perfilMB.salvar}: java.lang.NullPointerException
javax.faces.FacesException: #{perfilMB.salvar}: java.lang.NullPointerException
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
	at javax.faces.component.UICommand.broadcast(UICommand.java:315)
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
...

Caused by: javax.faces.el.EvaluationException: java.lang.NullPointerException
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
	... 50 more
Caused by: java.lang.NullPointerException
	at managedBean.PerfilMB.salvar(PerfilMB.java:29)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...

Aí, só para testar, fiz essas alterações no Método salvar do Managed Bean Perfil:
Antes:

	public String salvar(){	
			if (this.perfil.getId() == 0){
				JSFMensageiro.info("Perfil incluido com sucesso!");
			}
			else{
				JSFMensageiro.info("Perfil alterado com sucesso.");  
			}
			new PerfilHIB().salvar(this.perfil);
			this.perfil = new Perfil();
			perfis = new PerfilHIB().listar();
			return "success";
	}

Despois:

	public String salvar(){	
			new PerfilHIB().salvar(this.perfil);
			this.perfil = new Perfil();
			perfis = new PerfilHIB().listar();
			return "success";
	}

E então voltou o mesmo erro:

Advertência: #{perfilMB.salvar}: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entityBean.Permissao.id
javax.faces.FacesException: #{perfilMB.salvar}: org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of entityBean.Permissao.id

Complicado né?

Obrigado por estar me ajudando.

Aqui está o JPQL do aplicationContext.xml

	<sec:authentication-manager>
	    <sec:authentication-provider>
	    <!--<sec:password-encoder hash="md5"/>-->
	        <sec:jdbc-user-service data-source-ref="dataSource"
	            users-by-username-query="SELECT LOGIN, SENHA, 'true' as enable FROM USUARIO WHERE LOGIN=?"
	            authorities-by-username-query="SELECT U.LOGIN, P.ROLE FROM USUARIO U
												inner join perfil_permissao PP
												on U.ID_PERFIL=PP.ID_PERFIL
												inner join permissao P
												on PP.ID_PERMISSAO=P.ID_PERMISSAO
												WHERE LOGIN=?;" />
	    </sec:authentication-provider>
	</sec:authentication-manager>

Então amigo o seu objeto está indo sem um ID, no seu formulário coloca,
<h:inputHidden value="#{bean.propriedade.id}"/>

Só pra ve se vai continua retornando null pointer

Fiz isso e volta o mesmo erro: occurred calling getter of entityBean.Permissao.id

O ID da classe Perfil está com a seguinte annotation: [quote]@GeneratedValue(strategy=GenerationType.AUTO)[/quote]
Já está gerando automaticamente o ID. Será que isso está influenciando? (Apesar de dar na mesma com ou sem isso)

:s

È estranho ajudar um código que é diferente do modo como eu relaciono, mas coloque o inverseColumn dessa maneira, não há necessecidade doo referenced:

joinColumns = @JoinColumn(name = “DonaDaChave”), inverseJoinColumns = @JoinColumn(name = “NaoDona”)

Outra coisa coloque a annotation emcima da propriedade

EX:

@ID
@GeneratedValue
private integer ID;

private Integer getId(){}

private void setId(Integer id){}