1. java
  2. android
  3. c#
  4. .net
  5. javascript
  6. php
  7. jquery
  8. html
  9. sql

Merge - Entidade com FK composta

Olá pessoal!

Tenho as seguintes entidades:

Usuario (PK cod_usr, nome, sobrenome, email..) / Permissao (PK cod_per, nome, FK empresa) / Empresa (PK cod_emp, razao, fantasia, cnpj..) /

e esta entidade que utilizo para gerenciar as permissoes de cada usuario de acordo com a empresa que ele esteja.

Usuario_Permissao_Empresa (PK FK usuario, PK FK permissao, PK FK empresa) (Associacao)

Está ocorrendo erro no merge quando adiciono uma nova associacao para o usuario.

Exception in thread "main" java.lang.NullPointerException
    at org.hibernate.type.EntityType.isEqual(EntityType.java:344)
    at org.hibernate.type.ComponentType.isEqual(ComponentType.java:176)
    at org.hibernate.engine.EntityKey.equals(EntityKey.java:119)
    at java.util.HashMap.getEntry(HashMap.java:448)
    at java.util.HashMap.get(HashMap.java:405)
    at org.hibernate.engine.StatefulPersistenceContext.getEntity(StatefulPersistenceContext.java:368)
    at org.hibernate.impl.SessionImpl.getEntityUsingInterceptor(SessionImpl.java:656)
    at org.hibernate.event.def.DefaultLoadEventListener.loadFromSessionCache(DefaultLoadEventListener.java:533)
    at org.hibernate.event.def.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:440)
    at org.hibernate.event.def.DefaultLoadEventListener.load(DefaultLoadEventListener.java:227)
    at org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:285)
    at org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
    at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
    at org.hibernate.impl.SessionImpl.internalLoad(SessionImpl.java:1038)
    at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:630)
    at org.hibernate.type.EntityType.resolve(EntityType.java:438)
    at org.hibernate.type.EntityType.replace(EntityType.java:298)
    at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:508)
    at org.hibernate.type.CollectionType.replace(CollectionType.java:582)
    at org.hibernate.type.TypeHelper.replace(TypeHelper.java:178)
    at org.hibernate.event.def.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:563)
    at org.hibernate.event.def.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:288)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:261)
    at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:84)
    at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:867)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:851)
    at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:855)
    at br.com.prototipo.model.Usuario.main(Usuario.java:115)

Minhas entidades:

Usuario

@OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, mappedBy="usuario")
    private Set<Usuario_Permissao_Empresa> associacoes = new HashSet<>();

@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cod_usr == null) ? 0 : cod_usr.hashCode());
        result = prime * result + ((email == null) ? 0 : email.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Usuario)) return false;

        Usuario outro = (Usuario) obj;
        if(cod_usr != null)
            if (cod_usr.equals(outro.cod_usr)) return true;
        if (email!=null)
            if(email.equals(outro.email)) return true;

        return false;
    }

Empresa

    @OneToMany(cascade={CascadeType.REMOVE, CascadeType.PERSIST, CascadeType.MERGE}, mappedBy="empresa")
    private Set<Usuario_Permissao_Empresa> permissao_usuario_emp = new HashSet<>();


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cod_emp == null) ? 0 : cod_emp.hashCode());
        result = prime * result + ((cnpj == null) ? 0 : cnpj.hashCode());
        result = prime * result + ((email == null) ? 0 : email.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Object)) return false;
        Empresa other = (Empresa) obj;

        if (cod_emp != null) {
            if (cod_emp.equals(other.cod_emp))
                return true;
        }

        if (cnpj == null) {
            if (other.cnpj != null)
                return false;
        } else if (!cnpj.equals(other.cnpj))
            return false;

        if (email == null) {
            if (other.email != null)
                return false;
        } else if (!email.equals(other.email))
            return false;
        return true;
    }

Permissao

    @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}, mappedBy="permissao", orphanRemoval=true)
    private Set<Usuario_Permissao_Empresa> usr_per_emp = new HashSet<Usuario_Permissao_Empresa>();

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((cod_per == null) ? 0 : cod_per.hashCode());
        result = prime * result + ((empresa == null) ? 0 : empresa.hashCode());
        result = prime * result + ((nome == null) ? 0 : nome.hashCode());
        return result;
    }

    @Override
    /** O (codigo) ou (o nome e a empresa) precisa ser igual para ser considerado igual */
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Permissao)) return false;
        Permissao other = (Permissao) obj;

        if (cod_per != null && other.cod_per != null) {
            if (other.cod_per.equals(cod_per)) return true;
        }
        if (empresa == null) {
            if (other.empresa != null)
                return false;
        } else if (!empresa.equals(other.empresa))
            return false;
        if (nome == null) {
            if (other.nome != null)
                return false;
        } else if (!nome.equals(other.nome))
            return false;
        return true;
    }

Usuario_Permissao_Empresa -> Associacao

    @Id
    @ManyToOne()
    @JoinColumn(name="cod_usr")
    @ForeignKey(name="Usuario_cod_usr_FK")
    private Usuario usuario;

    @Id
    @ManyToOne()
    @JoinColumn(name="cod_per")
    @ForeignKey(name="Permissao_cod_per_FK")
    private Permissao permissao;

    @Id
    @ManyToOne()
    @JoinColumn(name="cod_emp")
    @ForeignKey(name="Empresa_cod_emp_FK")
    private Empresa empresa;


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((empresa == null) ? 0 : empresa.hashCode());
        result = prime * result + ((permissao == null) ? 0 : permissao.hashCode());
        result = prime * result + ((usuario == null) ? 0 : usuario.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Usuario_Permissao_Empresa)) return false;
        Usuario_Permissao_Empresa other = (Usuario_Permissao_Empresa) obj;

        if (empresa == null) {
            if (other.empresa != null)
                return false;
        } else if (!empresa.equals(other.empresa))
            return false;
        if (permissao == null) {
            if (other.permissao != null)
                return false;
        } else if (!permissao.equals(other.permissao))
            return false;
        if (usuario == null) {
            if (other.usuario != null)
                return false;
        } else if (!usuario.equals(other.usuario))
            return false;
        return true;
    }

O teste que eu fiz para gerar este erro

    public static void main(String[] args) {
        SessionFactory factory = new Configuration().configure().buildSessionFactory();
        Session session = factory.openSession();

        session.beginTransaction();

        Usuario usuario = (Usuario) session.get(Usuario.class, 1);
        Permissao permissao = (Permissao) session.get(Permissao.class, 2);
        Empresa empresa = (Empresa) session.get(Empresa.class, 1);

        Usuario_Permissao_Empresa assoc_permissaoTeste = new Usuario_Permissao_Empresa(usuario, permissao, empresa);

        usuario.getAssociacoes();
        usuario.getAssociacoes().add(assoc_permissaoTeste);
//        permissao.getAssociacoes().add(assoc_permissaoTeste);
//        empresa.getPermissao_usuario_emp().add(assoc_permissaoTeste);

//        session.update(usuario);
        session.merge(usuario);
//        session.persist(assoc_permissaoTeste); //Somente funciona se eu persistir
        session.getTransaction().commit();

        session.close();

        //Informacoes usuario
        System.out.println("Informacoes Usuario");
        System.out.println(usuario.getCod_usr());
        System.out.println(usuario.getNome());
        System.out.println(usuario.getSobrenome());
        System.out.println(usuario.getCpf());
        System.out.println(usuario.getEmail());
        System.out.println();

        //Informacoes permissao
        System.out.println("Informacoes Permissao");
        System.out.println(permissao.getCod_per());
        System.out.println(permissao.getNome());
        System.out.println(permissao.getDescricao());
        System.out.println("Cod Empresa: "+permissao.getEmpresa().getCod_emp());
        System.out.println("Empresa: "+permissao.getEmpresa().getRazao());
        System.out.println("Fantasia: "+permissao.getEmpresa().getFantasia());
        System.out.println("CNPJ: "+permissao.getEmpresa().getCnpj());
        System.out.println();

        //Informacoes empresa
        System.out.println("Informacoes Empresa");
        System.out.println(empresa.getCod_emp());
        System.out.println(empresa.getRazao());
        System.out.println(empresa.getFantasia());
        System.out.println(empresa.getCnpj());
        System.out.println(empresa.getEmail());

    }

Bom, ele funciona apenas se eu persisto a associação diretamente, mas isto não me serve, porque no momento eu não irei saber qual a associação que ainda não existe no conjunto para persistir. Minha versão do hibernate é a 3.6.10, procurando em outros lugares encontrei este bugfix do hibernate BugFix, mas ainda está aberto. Consigo fazer funcionar se ao invés de deixar as 3 entidades estrangeiras como PK composta, deixar um id auto incrementado para elas, mas dessa forma não acho certo.

Edit Solução

Como sugerido pelo @Utluiz, criar uma outra classe para informar a chave composta da mesma forma como embedded

public class UsuarioPermissaoEmpresaPK implements Serializable {
    private static final long serialVersionUID = -5595352804172460750L;

    private Usuario usuario;
    private Permissao permissao;
    private Empresa empresa;
    public Usuario getUsuario() {
        return usuario;
    }
    public void setUsuario(Usuario usuario) {
        this.usuario = usuario;
    }
    public Permissao getPermissao() {
        return permissao;
    }
    public void setPermissao(Permissao permissao) {
        this.permissao = permissao;
    }
    public Empresa getEmpresa() {
        return empresa;
    }
    public void setEmpresa(Empresa empresa) {
        this.empresa = empresa;
    }
..Equals e HashCode

**E anotar a classe principal com o @IdClass(UsuarioPermissaoEmpresaPK)

@IdClass(UsuarioPermissaoEmpresaPK.class)
public class Usuario_Permissao_Empresa implements Serializable{
    private static final long serialVersionUID = -8273150299057853820L;
    @Id
    @ManyToOne()
    @JoinColumn(name="cod_usr")
    @ForeignKey(name="Usuario_cod_usr_FK")
    private Usuario usuario;

    @Id
    @ManyToOne()
    @JoinColumn(name="cod_per")
    @ForeignKey(name="Permissao_cod_per_FK")
    private Permissao permissao;

    @Id
    @ManyToOne()
    @JoinColumn(name="cod_emp")
    @ForeignKey(name="Empresa_cod_emp_FK")
    private Empresa empresa;
..getters, setters, Equals, HashCode
  • Dê uma olhada aqui amigo e veja se lhe ajuda: http://www.guj.com.br/java/204454-resolvido--mapeamento-de-chave-composta-no-hibernate-usando-annotation Só mais um detalhe, coloque seu código de acordo com a convenção de código Java, isso ajuda a mantyer uma leitura uniforme por parte de todos. O que me incomodou foi o Nome_Da_Classe_Assim ao invés de NomeDaClasseAssim. Coloque também os nomes de atributos sem underscore e sim com camelCase. Abs ;)

    adriano_si   12 de nov de 2013
  • Obrigado @Adriano_si, dei uma olhada nesse post anteriormente também, só que este não serve para o meu caso, pois eu estou utilizando 3 entidades, neste ele só utiliza 2 como outro mapeamento que tenho feito aqui e funcionando sem problemas, da mesma forma, praticamente, que utilizei com este

    Mickdark   12 de nov de 2013
  • Mas qual foi a dificuldade? Basta na sua ClassePK você fazer os 3 relacionamentos. O que deu errado nessa abordagem?

    adriano_si   12 de nov de 2013
  • Uma outra solução seria você criar então uma Entidade chamada PermissaoEmpresa, então sua Entidade UsuarioPermissaoEmpresa teria um Usuario e uma PermissaoEmpresa como relacionamento.

    adriano_si   12 de nov de 2013
  • Opa @Adriano_si, primeira pergunta: Acontece o mesmo erro, no bugfix que eu mostrei o cara utiliza tambem desta mesma forma e ocorre o mesmo erro, deveria funcionar, mas não funciona :(

    segunda sugestão: Pensei em colocar um relacionamento @ManyToMany anteriormente também, mas eu preciso da Empresa no relacionamento também, pois nem sempre uma permissão vai pertencer a apenas 1 usuario de uma empresa, estas Permissoes são as permissoes Default que poderão ser utilizadas por todos os usuarios, e dessa forma só posso distinguir uma permissão de outra através da empresa!

    Mickdark   12 de nov de 2013
Mostrar todos os 6 comentários>
  1. Você vai ver essas setas em qualquer página de pergunta. Com elas, você pode dizer se uma pergunta ou uma resposta foram relevantes ou não.
  2. Edite sua pergunta ou resposta caso queira alterar ou adicionar detalhes.
  3. Caso haja alguma dúvida sobre a pergunta, adicione um comentário. O espaço de respostas deve ser utilizado apenas para responder a pergunta.
  4. Se o autor da pergunta marcar uma resposta como solucionada, esta marca aparecerá.
  5. Clique aqui para mais detalhes sobre o funcionamento do GUJ!

1 resposta

Não é a resposta que estava procurando? Procure outras perguntas com as tags hibernate sql ou faça a sua própria pergunta.