Erro ao excluir objeto com relacionamento ManyToMany

Pessoal, gostaria de uma ajuda de vcs no seguinte problema:
Tenho uma classe Perfil que possui relacionamento ManyToMany com Funcionalidade, tendo uma tabela associativa chamada PERFIL_FUNCIONALIDADE. Quando tento excluir o objeto perfil, preciso que os dados da tabela associativa também sejam excluidos, porém a tabela Funcionalidade permaneça intacta porque é uma tabela de domínio e seus dados nunca são apagados. Já usei o CascadeType mas o erro anexado abaixo bloqueia a exclusão logo de cara. Alguém poderia me ajudar?


Classe Perfil.java:

package entidades;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name = "PERFIL")
public class Perfil implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@SequenceGenerator(sequenceName = "SEQ_PERFIL", name = "SEQ_PERFIL", allocationSize=1)
	@GeneratedValue(generator = "SEQ_PERFIL", strategy = GenerationType.SEQUENCE)
	@Column(name = "ID_PERFIL")
	private Long idPerfil;

	@Column(name = "TX_NOME_PERFIL", length = 40, nullable = false)
	private String nomePerfil;
	
	@ManyToMany
	@JoinTable(name = "PERFIL_FUNCIONALIDADE", 
				joinColumns = { @JoinColumn(name = "ID_PERFIL", nullable = false) },
				inverseJoinColumns = { @JoinColumn(name = "CD_FUNCIONALIDADE", nullable = false) })
	private List<Funcionalidade> funcionalidades = new ArrayList<Funcionalidade>();
	...

}


Classe Funcionalidade.java:

package entidades;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

/**
 * Tabela de dominio
 * */
@Entity
@Table(name = "FUNCIONALIDADE")
public class Funcionalidade implements Serializable {

	private static final long serialVersionUID = 1L;
	
	@Id
	@Column(name = "CD_FUNCIONALIDADE")
	private Long cdFuncionalidade;
	
	@Column(name = "TX_NOME_FUNCIONALIDADE", nullable = false, length = 60)
	private String nomeFuncionalidade;
	
	@Column(name = "TX_URL", nullable = true, length = 2048)
	private String url;
	
	@ManyToMany(mappedBy = "funcionalidades")
	private List<Perfil> perfis = new ArrayList<Perfil>();
...
}

Erro no console:

10:04:15,824 WARN  [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-localhost-127.0.0.1-8080-6) SQL Error: -532, SQLState: 23001
10:04:15,824 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (http-localhost-127.0.0.1-8080-6) A parent row cannot be deleted because the relationship "DESENV.PERFIL_FUNCIONALIDADE.FK_PERFIL_FUNCIONALIDADE_PERFIL" restricts the deletion.. SQLCODE=-532, SQLSTATE=23001, DRIVER=4.12.79
10:04:15,847 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http-localhost-127.0.0.1-8080-6) #{perfilBean.excluir}: 
excecao.DaoExcecao: excecao.DaoExcecao: Erro ao tentar excluir o perfil.: javax.faces.FacesException: #{perfilBean.excluir}: excecao.DaoExcecao: excecao.DaoExcecao: Erro ao tentar excluir o perfil.
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118) [jsf-impl-2.1.7-jbossorg-2.jar:]
	at javax.faces.component.UICommand.broadcast(UICommand.java:315) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
	at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81) [jsf-impl-2.1.7-jbossorg-2.jar:]
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.7-jbossorg-2.jar:]
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) [jsf-impl-2.1.7-jbossorg-2.jar:]
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]


Classe PerfilEjb.java:

package ejbs;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;


@Local(IPerfilEjb.class)
@Stateless
public class PerfilEjb {
	
	@PersistenceContext
	private EntityManager manager;
	
	private IPerfilDAO perfilDAO  = new PerfilDAO();
	private ILoginDAO loginDAO = new LoginDAO();

public List<String> excluirPerfis(Map<Long, Boolean> ids, Long idEntidade) throws DaoExcecao {
		
		List<String> listaMensagens = new ArrayList<String>();
		
		perfilDAO.setEntityManager(manager);
		loginDAO.setEntityManager(manager);
		
		List<Long> idsExcluir = new ArrayList<Long>();
		
		Set<Entry<Long, Boolean>> idsSet = ids.entrySet();
		
		for (Entry<Long, Boolean> e : idsSet) {
			if (e.getValue()) {
				if (loginDAO.verificarAssociacaoLoginPerfil(e.getKey(), idEntidade)) {
					Perfil perfil = new Perfil();
					perfil = perfilDAO.buscarPorId(e.getKey());
					listaMensagens.add("Não foi possivel excluir o(s) perfil(s) " + perfil.getNomePerfil() + ". Existem usuários associados a esse perfil.");
				} else {
					idsExcluir.add(e.getKey());
				}
				
			}
		}
		
		if(!idsExcluir.isEmpty()) {
		
			try{
				perfilDAO.excluirPerfis(idsExcluir);
	  		
			}catch(Exception e){
				throw new DaoExcecao(e);
			}
		}
		return listaMensagens;
	}

}

Classe PerfilDAO.java:

package dao;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.AliasToBeanResultTransformer;


public class PerfilDAO  extends GenericDAO<Perfil, Serializable> implements IPerfilDAO {

public void excluirPerfis(List<Long> ids) throws DaoExcecao {
		boolean primeiro = true;
		try{
			StringBuilder query = new StringBuilder();
			query.append("DELETE FROM Perfil p ");
			query.append(" WHERE p.idPerfil IN (");
			
			for(Long id:ids){
				
				if(!primeiro){
					query.append(",");
				}else{
					primeiro = false;
				}			
				query.append(id);			
			}
			
			query.append(")");
			
			((Session) getEntityManager().getDelegate()).createQuery(query.toString()).executeUpdate();
		} catch (HibernateException e) {
			throw new DaoExcecao("Erro ao tentar excluir o perfil.", e);
		}
		
	} 

}

Funcionalidade é a entidade dominante, certo? Por que diabos está com

@ManyToMany(mappedBy = "funcionalidades") 

Coloque o mapeamento completo neste atributo e remova do que está na entidade Perfil.
Isso deve resolver o problema.

Você está fazendo um relacionamento bidirecional…

Tente fazer assim, imagine que temos pontaA e pontaB e você quer excluir pontaB.

pontaA.getPontas().remove(pontaB);
pontaB.getPontas().clear();
entityManager.remove(pontaB);

Neste post explica como funciona o cascade: JPA: Mini Livro - Primeiros passos e conceitos detalhados.

dsrmachado,
Troquei os mapeamentos como vc orientou mas o erro continua.

package entidades;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name = "PERFIL")
public class Perfil implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@SequenceGenerator(sequenceName = "SEQ_PERFIL", name = "SEQ_PERFIL", allocationSize=1)
	@GeneratedValue(generator = "SEQ_PERFIL", strategy = GenerationType.SEQUENCE)
	@Column(name = "ID_PERFIL")
	private Long idPerfil;

	@Column(name = "TX_NOME_PERFIL", length = 40, nullable = false)
	private String nomePerfil;
	
	@ManyToMany(mappedBy = "perfis")
	private List<Login> logins = new ArrayList<Login>();
	
	@ManyToMany(mappedBy = "perfis")
	private List<Funcionalidade> funcionalidades = new ArrayList<Funcionalidade>();
}

package entidades;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

/**
 * Tabela de dominio
 * */
@Entity
@Table(name = "FUNCIONALIDADE")
public class Funcionalidade implements Serializable {

	private static final long serialVersionUID = 1L;
	
	@Id
	@Column(name = "CD_FUNCIONALIDADE")
	private Long cdFuncionalidade;
	
	@Column(name = "TX_NOME_FUNCIONALIDADE", nullable = false, length = 60)
	private String nomeFuncionalidade;
	
	@Column(name = "TX_URL", nullable = true, length = 2048)
	private String url;
	
	@ManyToMany
	@JoinTable(name = "PERFIL_FUNCIONALIDADE", 
	joinColumns = { @JoinColumn(name = "CD_FUNCIONALIDADE", nullable = false) },
	inverseJoinColumns = { @JoinColumn(name = "ID_PERFIL", nullable = false) })
	private List<Perfil> perfis = new ArrayList<Perfil>();
}
	

Hebert, entendi a sua explicação. Primeiro removi o perfil de cada funcionalidade, desfazendo a associação, e depois exclui o perfil.
Vlw pela ajuda.