terça-feira, 3 de outubro de 2017

Mapeando chaves compostas com JPA - Hibernate

Nesse post eu vou falar sobre o mapeamento de chaves compostas no Hibernate.

É comum aparecer situações aonde precisamos de uma chave composta para garantir a unicidade de um registro, e o Hibernate trabalha isso de uma forma simples, tratando os campos que compõem a chave primária como um objeto separado embutido no objeto principal.

A classe que será embutida no objeto principal e que tem as propriedades que juntas compõem a chave primária deve ser anotada com @Embeddable. Essa classe não pode ser usada separada da classe principal, pois o Hibernate não trata ela como uma @Entity. Na classe principal, ao invés da anotação @id será usada a anotação @EmbeddedId.
 
Vou usar o exemplo da Cidade e UF pois acho simples de compreender. Vamos ver o código:

Vamos começar pela classe principal, que é a classe Município, o sequencial é a classe SeqMunicipio e o equals e hashCode são construídos em cima dele.

@Entity
public class Municipio {

    @EmbeddedId
    private SeqMunicipio seqMunicipio;

    @Column(name = "DSC_MUNICIPIO")
    private String dscMunicipio;

    @Column(name = "CEP")
    private Long cep;

    @ManyToOne
    @JoinColumn(name = "COD_UF", referencedColumnName = "COD_UF")
    private UnidadeFederacao codUF;

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

    @Override
    public boolean equals( Object obj ) {
        if ( this == obj )
            return true;
        if ( obj == null )
            return false;
        if ( getClass() != obj.getClass() )
            return false;
        Municipio other = (Municipio) obj;
        if ( seqMunicipio == null ) {
            if ( other.seqMunicipio != null )
                return false;
        } else if ( !seqMunicipio.equals( other.seqMunicipio ) )
            return false;
        return true;
    }
   
}

Agora vamos a classe que contém a chave composta. Ela contém a serialização, o equals e o hashCode, um construtor vazio, e os métodos get’s and setter’s, além é claro das propriedades que compõem a chave composta.

@Embeddable
public class SeqMunicipio implements Serializable {

    private static final long serialVersionUID = 6576422530023818928L;

    @Column(name = "COD_MUNICIPIO")
    private Integer codMunicipio;

    @Column(name = "COD_UF")
    private Integer CodUnidadeFederacao;

    public SeqMunicipio() {
    }

    public SeqMunicipio( Integer codMunicipio, Integer codUnidadeFederacao ) {
        super();
        this.codMunicipio = codMunicipio;
        CodUnidadeFederacao = codUnidadeFederacao;
    }

    public Integer getCodMunicipio() {
        return codMunicipio;
    }

    public void setCodMunicipio( Integer codMunicipio ) {
        this.codMunicipio = codMunicipio;
    }

    public Integer getCodUnidadeFederacao() {
        return CodUnidadeFederacao;
    }

    public void setCodUnidadeFederacao( Integer codUnidadeFederacao ) {
        CodUnidadeFederacao = codUnidadeFederacao;
    }

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

    @Override
    public boolean equals( Object obj ) {
        if ( this == obj )
            return true;
        if ( obj == null )
            return false;
        if ( getClass() != obj.getClass() )
            return false;
        SeqMunicipio other = (SeqMunicipio) obj;
        if ( CodUnidadeFederacao == null ) {
            if ( other.CodUnidadeFederacao != null )
                return false;
        } else if ( !CodUnidadeFederacao.equals( other.CodUnidadeFederacao ) )
            return false;
        if ( codMunicipio == null ) {
            if ( other.codMunicipio != null )
                return false;
        } else if ( !codMunicipio.equals( other.codMunicipio ) )
            return false;
        return true;
    }
}

A próxima classe é para vermos o uso de uma lista de objetos com id composto.

@Entity
public class UnidadeFederacao {

    @Id
    @Column(name = "COD_UF")
    private Integer codUF;

    @Column(name = "DSC_UF")
    private String dscUF;

    @Column(name = "SIGLA_UF")
    private String siglaUF;

    @OneToMany(mappedBy = "codUF", fetch = FetchType.LAZY)
    private List<Municipio> municipios;
}

Como eu faço para buscar um município? Supondo que o município 512 seja Fortaleza e o Estado 3 seja o Ceará faríamos assim:

SeqMunicipio seq = new SeqMunicipio(512,3);
Municipio m = entityManager.find(Municipio.class, seq);



2 comentários: