@Onetomany without Primary Key

Asked

Viewed 397 times

2

There is the possibility of making a relationship @OneToMany without Primary Key on the daughter 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’t change its structure. The table NOTICIAIMAGEM has more than 6 million records in production. The same table does not have Primary Key and neither Foreing Key. The above code was automatically generated by the Netbeans Entity Generator.

  • The field nmImagem of Noticiaimagem is what? If you give a SELECT x.c FROM (SELECT COUNT(*) as c FROM noticiaimagem ni GROUP BY ni.cd_noticia, ni.nm_imagem) x WHERE x.c > 1, it returns some result?

  • @Victorstafusa, is the image path; returns.

1 answer

1

What you’re trying to model is the following:

A news has N figures.

How this works in the database?

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

What’s in your modeling?

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

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

Therefore, I suggest that the solution is simply to separate the concepts: The primary key of the image should be independent of the foreign key to 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 reverse 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 his name:

@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 will do nothing. You also specify in some places @Basic(optional = true) when it would be better and easier to put the nullable = false in @Column correspondent.

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

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 by using the getImagens(), the returned list is immutable, because trying to modify it would be a wrong way to try to add or delete a figure to the list, and so we have the Collections.unmodifiableList. On the other hand, if the list is modified by the methods adicionarImagem(NoticiaImagem) or removerImagem(NoticiaImagem), sudden side effects anywhere that have called the getImagens() previously are unwanted, which is why the getImagens() make a copy of the list (with the new ArrayList<>(imagens)), so changes in the original list will not cause side effects on the list returned by the getter. Note that there is no Setter, and therefore it is not possible to corrupt the image list by mounting a badly formed list and putting it on Noticia.

Already the methods adicionarImagem(NoticiaImagem), removerImagem(NoticiaImagem) and setNoticia(Noticia) call on each other to ensure that if, and only if, an image belongs to a news then such news will contain this image in your 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 method adicionarImagem(NoticiaImagem) in particular it is the most complex to do correctly, as it is necessary for it to be designed remembering that it may end up calling itself in an indirect recursive way through the setNoticia(Noticia).

Finally, returning to your original question:

There is the possibility of making a relationship @OneToMany without Primary Key on the daughter table?

The answer is that maybe there is, but even if there is, that would be a terrible idea and would result in a flawed, confusing, problem-ridden model. Don’t try this. Instead of trying to do this, fix your model and then you won’t even have to think about doing this.

  • So I forgot to describe a great detail. First of all, the bank is legacy. The NOTICIAIMAGEM table has more than 6 million records. The same table has no Primarykey and no Foreingkey.... About the class name, Basic annotation and the Getters and Setters, I used the Netbeans entity generator. Column, I did not specify the name of the fields, because the variables follow the same name of the table fields....

  • I’ve been trying to solve this problem for three days... I’ve come to the conclusion that by modelling the bank, it’s more viable to use JDBC. I found it very interesting; The explanation of not using getters and setters for collections, I found it very interesting....

Browser other questions tagged

You are not signed in. Login or sign up in order to post.