@OneToMany without Primary Key

2

Is there a possibility to make a @OneToMany relation without Primary Key in the child table?

Follow my two entities.

Entity Noticia :

@Entity
@Table(name = "NOTICIA")
@NamedQueries({
    @NamedQuery(name = "Noticia.findAll", query = "SELECT n FROM Noticia n"),
    @NamedQuery(name = "Noticia.findAllByDate", query = "SELECT n FROM Noticia n WHERE n.dhCadastro BETWEEN :startDate AND :endDate")
})
public class Noticia implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    //@GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "cdNoticia")
    private Long cdNoticia;

    @Basic(optional = false)
    @Column(name = "cdVeiculo")
    private long cdVeiculo;
    @Column(name = "nmAutor")
    private String nmAutor;
    @Column(name = "cdColunista")
    private Short cdColunista;
    @Basic(optional = false)
    @Column(name = "cdSecao")
    private short cdSecao;
    @Column(name = "dsTitulo")
    private String dsTitulo;
    @Lob
    @Column(name = "dsTexto")
    private String dsTexto;
    @Basic(optional = false)
    @Column(name = "dsURL")
    private String dsURL;
    @Column(name = "cdHash")
    private String cdHash;
    @Basic(optional = false)
    @Column(name = "dtNoticia")
    @Temporal(TemporalType.DATE)
    private Date dtNoticia;
    @Basic(optional = false)
    @Column(name = "hrNoticia")
    @Temporal(TemporalType.TIME)
    private Date hrNoticia;
    @Column(name = "dhCadastro")
    @Temporal(TemporalType.TIMESTAMP)
    private Date dhCadastro;
    @Basic(optional = false)
    @Column(name = "idTipo")
    private String idTipo;
    @Basic(optional = false)
    @Column(name = "idDigitalizada")
    private String idDigitalizada;

    @Column(name = "isTransicao")
    private Integer isTransicao;

    @Column(name = "isElasticSearch")
    private Integer isElasticSearch;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "noticia", orphanRemoval = true)
    private List<Noticiaimagem> noticiaimagemCollection = new ArrayList<Noticiaimagem>();



    public Noticia() {
    }

    public Long getCdNoticia() {
        return cdNoticia;
    }

    public void setCdNoticia(Long cdNoticia) {
        this.cdNoticia = cdNoticia;
    }

    public long getCdVeiculo() {
        return cdVeiculo;
    }

    public void setCdVeiculo(long cdVeiculo) {
        this.cdVeiculo = cdVeiculo;
    }

    public String getNmAutor() {
        return nmAutor;
    }

    public void setNmAutor(String nmAutor) {
        this.nmAutor = nmAutor;
    }

    public Short getCdColunista() {
        return cdColunista;
    }

    public void setCdColunista(Short cdColunista) {
        this.cdColunista = cdColunista;
    }

    public short getCdSecao() {
        return cdSecao;
    }

    public void setCdSecao(short cdSecao) {
        this.cdSecao = cdSecao;
    }

    public String getDsTitulo() {
        return dsTitulo;
    }

    public void setDsTitulo(String dsTitulo) {
        this.dsTitulo = dsTitulo;
    }

    public String getDsTexto() {
        return dsTexto;
    }

    public void setDsTexto(String dsTexto) {
        this.dsTexto = dsTexto;
    }

    public String getDsURL() {
        return dsURL;
    }

    public void setDsURL(String dsURL) {
        this.dsURL = dsURL;
    }

    public String getCdHash() {
        return cdHash;
    }

    public void setCdHash(String cdHash) {
        this.cdHash = cdHash;
    }

    public Date getDtNoticia() {
        return dtNoticia;
    }

    public void setDtNoticia(Date dtNoticia) {
        this.dtNoticia = dtNoticia;
    }

    public Date getHrNoticia() {
        return hrNoticia;
    }

    public void setHrNoticia(Date hrNoticia) {
        this.hrNoticia = hrNoticia;
    }

    public Date getDhCadastro() {
        return dhCadastro;
    }

    public void setDhCadastro(Date dhCadastro) {
        this.dhCadastro = dhCadastro;
    }

    public String getIdTipo() {
        return idTipo;
    }

    public void setIdTipo(String idTipo) {
        this.idTipo = idTipo;
    }

    public String getIdDigitalizada() {
        return idDigitalizada;
    }

    public void setIdDigitalizada(String idDigitalizada) {
        this.idDigitalizada = idDigitalizada;
    }

    public Integer getIsTransicao() {
        return isTransicao;
    }

    public void setIsTransicao(Integer isTransicao) {
        this.isTransicao = isTransicao;
    }

    public Integer getIsElasticSearch() {
        return isElasticSearch;
    }

    public void setIsElasticSearch(Integer isElasticSearch) {
        this.isElasticSearch = isElasticSearch;
    }

    public List<Noticiaimagem> getNoticiaimagemCollection() {
        return noticiaimagemCollection;
    }

    public void setNoticiaimagemCollection(List<Noticiaimagem> noticiaimagemCollection) {
        this.noticiaimagemCollection = noticiaimagemCollection;
    }
}

Entity Noticiaimagem :

@Entity
@Table(name = "NOTICIAIMAGEM")
public class Noticiaimagem implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @ManyToOne
    @JoinColumn(name = "cdNoticia", nullable = false)
    @MapsId
    private Noticia noticia;

    @Column
    private String nmImagem;

    @Column(nullable = false)
    private String nmExtensao;

    @Column
    private Float nrTamanho;
    @Column(columnDefinition = "UNSIGNED SMALLINT(5)")
    private Integer nrAltura;
    @Column(columnDefinition = "UNSIGNED SMALLINT(5)")
    private Integer nrLargura;
    @Column(columnDefinition = "UNSIGNED TINYINT(3)")
    private Integer nrOrdem;
    @Column(columnDefinition = "UNSIGNED MEDIUMINT(8)")
    private Integer cdTipoNoticiaImagem;

    public Noticiaimagem() {
    }

    public Noticia getNoticia() {
        return noticia;
    }

    public void setNoticia(Noticia noticia) {
        this.noticia = noticia;
    }

    public String getNmImagem() {
        return nmImagem;
    }

    public void setNmImagem(String nmImagem) {
        this.nmImagem = nmImagem;
    }

    public String getNmExtensao() {
        return nmExtensao;
    }

    public void setNmExtensao(String nmExtensao) {
        this.nmExtensao = nmExtensao;
    }

    public Float getNrTamanho() {
        return nrTamanho;
    }

    public void setNrTamanho(Float nrTamanho) {
        this.nrTamanho = nrTamanho;
    }

    public Integer getNrAltura() {
        return nrAltura;
    }

    public void setNrAltura(Integer nrAltura) {
        this.nrAltura = nrAltura;
    }

    public Integer getNrLargura() {
        return nrLargura;
    }

    public void setNrLargura(Integer nrLargura) {
        this.nrLargura = nrLargura;
    }

    public Integer getNrOrdem() {
        return nrOrdem;
    }

    public void setNrOrdem(Integer nrOrdem) {
        this.nrOrdem = nrOrdem;
    }

    public Integer getCdTipoNoticiaImagem() {
        return cdTipoNoticiaImagem;
    }

    public void setCdTipoNoticiaImagem(Integer cdTipoNoticiaImagem) {
        this.cdTipoNoticiaImagem = cdTipoNoticiaImagem;
    }
}

The database is legacy and I can not change its structure. The NOTICIAIMAGEM table has more than 6 million records in production. The same table does not have Primary Key and neither Foreing Key . The code above was generated automatically by the NetBeans entity generator.

    
asked by anonymous 25.08.2016 / 14:04

1 answer

1

What you are trying to model is the following:

  

News has N figures.

How does this work in the database?

  

The table of figures should have a foreign key of the news.

What's in your modeling?

  

The table of figures has the primary key the foreign news key.

Since the foreign key is also a primary key without the participation of any other fields, you end up creating a model that in the database prevents more than one image associated with a news, because the key of the two is the it. The fact that you put a @ManyToOne will not make Hibernate able to resolve this, because your database model is stuck.

So, I suggest that the solution is simply to separate the concepts: The primary key of the image must be independent of the foreign key for the news. This will solve your problems. Here is the corresponding code:

@Entity
@Table(name = "NOTICIAIMAGEM")
public class NoticiaImagem implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cdImagem", nullable = false)
    private Long cdImagem;

    @ManyToOne
    @JoinColumn(name = "cdNoticia", nullable = false)
    private Noticia noticia;

    // Resto do código ...
}

Note that I changed the class name to NoticiaImagem instead of Noticiaimagem .

The inverse relationship in class Noticia seems to be correct:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "noticia", orphanRemoval = true)
private List<Noticiaimagem> noticiaimagemCollection = new ArrayList<Noticiaimagem>();

But I would change the name of it:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "noticia", orphanRemoval = true)
private List<NoticiaImagem> imagens = new ArrayList<>(10);

There are still other problems in your code. You use in some places the annotation @Column without specifying anything in it, which is not going to do anything. You also specify in some places @Basic(optional = true) when it would be best and easiest to place nullable = false in the corresponding @Column .

Avoid using setters to separate entire collections and use getters that return mutable collections. The reason for this is that this allows your encapsulation to be easily violated, since its Noticia class rather than being managing and encapsulating the images that are part of the news, is actually giving up doing this task and blindly entrusting it to others. The best thing would be to do something like this in your Noticia class:

public List<NoticiaImagem> getImagens() {
    return Collections.unmodifiableList(new ArrayList<>(imagens));
}

public void adicionarImagem(NoticiaImagem imagem) {
    Noticia outra = imagem.getNoticia();
    if (outra != this) {
        if (outra != null) outra.removerImagem(imagem);
        imagem.setNoticia(this);
    } else if (!imagens.contains(imagem)) {
        imagens.add(imagem);
    }
}

public void removerImagem(NoticiaImagem imagem) {
    imagens.remove(imagem);
    if (imagem.getNoticia() == this) imagem.setNoticia(null);
}

And this in your class NoticiaImagem :

public void setNoticia(Noticia noticia) {
    this.noticia = noticia;
    if (noticia != null) noticia.adicionarImagem(this);
}

public Noticia getNoticia() {
     return noticia;
}

The idea here is that when using getImagens() , the returned list is immutable, so trying to modify it would be a wrong way to try adding or deleting a picture to the list, so we have Collections.unmodifiableList . On the other hand, if the list is modified by means of adicionarImagem(NoticiaImagem) or removerImagem(NoticiaImagem) methods, sudden side effects anywhere that have previously called getImagens() are undesired, which is why getImagens() makes a copy of the list (with new ArrayList<>(imagens) ), because so changes in the original list will not cause side effects in the list returned by the getter. Note that there is no setter, so it is not possible to corrupt the list of images by assembling a malformed list and putting it in Noticia .

Since adicionarImagem(NoticiaImagem) , removerImagem(NoticiaImagem) and setNoticia(Noticia) methods call each other to ensure that if, and only if, an image belongs to a news item then such news will contain this image in its list and no other news will contain this image in your list. It is also necessary to ensure that the same image only appears once in the list of images of the news in which it appears.

The adicionarImagem(NoticiaImagem) method in particular is the most complex to do correctly, since it needs to be designed by remembering that it can indirectly call itself recursively by setNoticia(Noticia) .

Finally, going back to your original question:

  

Is there a possibility to make a @OneToMany relation without Primary Key in the child table?

The answer is that maybe it even exists, but even if it exists, that would be a bad idea and would result in a bored, confusing, and trouble-filled model. Do not try to do this. Instead of trying to do this, fix your template and then you will not even need to think about doing it.

    
25.08.2016 / 19:19