Como utilizar @OneToMany - @JoinColumn e outros recursos para mapear objetos relacionados

19 respostas
J

Olá pessoal, possuo um sistema que já está funcionando e ao buscar minhas entidades eu consigo recuperar seus dados, porém eu preciso trazer além dos dados dessa entidade, também trazer os dados das entidades que se relacionam com ela.

Por exemplo:

Possuo uma entidade chamada Produtos e outra chamada Categorias, onde Produtos possuem Categorias.

Para recuperar os dados de Produtos eu tenho o seguinte:

@Entity
@Table(name = "PRODUCTS")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Products.findAll", query = "SELECT p FROM Products p"),
    @NamedQuery(name = "Products.findByProductId", query = "SELECT p FROM Products p WHERE p.productId = :productId"),
    @NamedQuery(name = "Products.findByProductNumber", query = "SELECT p FROM Products p WHERE p.productNumber = :productNumber"),
    @NamedQuery(name = "Products.findByDescription", query = "SELECT p FROM Products p WHERE p.description = :description"),
    @NamedQuery(name = "Products.findByRecordType", query = "SELECT p FROM Products p WHERE p.recordType = :recordType"),
    @NamedQuery(name = "Products.findByBusiness", query = "SELECT p FROM Products p WHERE p.business = :business"),
    @NamedQuery(name = "Products.findByTimeStamp", query = "SELECT p FROM Products p WHERE p.timeStamp = :timeStamp")})
public class Products implements Serializable {
.
.
.
.
.

Como posso fazer para trazer os dados da categoria desse produto na função find por exemplo.

Li algo sobre @OneToMany e @JoinColumn, seria isso?

Alguma sugestão de como utilizar?

obrigado.

19 Respostas

P

Kra, se o seu banco de dados for relacional com as chaves estrangeiras tudo certinho, eh so pedir pra IDE(NETBEANS OU ECLIPSE) gerar as classes de dominio pra vc… as classes de dominio vao ser criadas ja com todas as anotacoes @OneToMany, @ManytoOne, @ManytoMany etc… dessa forma vc nao fica sofrendo em como montar anotacoes pq elas ja veem PRONTAS!

J

E no caso de não estar?

P

Kra se seu banco de dados nao tiver relacional, ai eh complicado utilizar por exemplo frameworks ORM como o HIBERNATE… melhor eh fazer sql e utilizar JDBC mesmo… mas creio que nao eh seu caso pq vc ta kerendo utilizar anotations e tudo mais… provalmente o banco ta certim e quando o banco ta certim pra vc usar anotations corretamente ou vc abre a documentacao do framework que vc vai utilizar e sofre pra entender como usar as anotations nos relacionamentos entre as entidades, ou como alternativa vc pede a IDE pra gerar pra vc… enfim eh isso…

J

pois então, eu pedi p ide gerar p mim, mas as relações não foram criadas, bem provável que as relações não estejam definidas, ou seja, tenho na teoria o que se relaciona com o que, mas não tenho na minha DDL a relação explicita.

R

O que vc pode fazer é o seguinte:

-Vc cria todas as classes tendo como parametro a sua base de dados.
-Depois vc cria os relacionamentos nas classes
-E quando vc conectar na base com o hibernate, ele criara os relacionamentos para vc

Entendeu?

P

Correto. Se nao tiver os relacionamentos e as referencias na DDL fica compplicado mesmo a IDE nao reconhece… o banco eh muito grande? nao da pra vc fazer a modelagem novalmente e criar nao? Dae vc ja evita muitos problemas

J

Então, as classes baseadas na minha base de dados eu já criei, minha dúvida é o passo 2 de como criar esses relacionamentos.

P

Vc tb pode fazer como o Renato_natos falou… mas ai ja tem que saber criar os relacionamentos corretos e depois pedir pro hibernate criar o banco pra vc…

J

vou recriar o banco com os relacionamentos corretos e pedir pra gerar novamente minhas classes, acho q é o caminho mais fácil.

R

Tudo sem muito misterio!

Imagine a seguinte situação:

Voce possue a tabela PESSOA e a tabela CONTATO onde uma pessoa podera ter varios contatos.

Seria mais ou menos assim:

//PESSOA
@Entity//Informa que a classe sera persistente
@Table(name = 'pessoa')//Informa que o nome da tabela devera ser 'pessoa'
public class Pessoa{

@Id//Informa o identificador da tabela
@GeneratedValue(strategy = GenerationType.IDENTITY)//Informa a forma de criacao dos ids
@Column(name = 'usuario_id')//Informa que a coluna na base devera se chamar 'usuario_id'
private Integer id;//Informa que a coluna 'usuario_id' sera tratada como 'id' na aplicacao

//getter e setter

}

//Classe contato
@Entity
@Table(name = 'contato')
public class Contato{

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@ManyToOne//Informa o relacionamento
@JoinColumn(name = 'usuario_id')//Informa que na tabela de contato devera ser criada uma coluna chamada 'usuario_id' que representara o usuario
private Pessoa pessoa;//Informa o objeto do relacionamento

//getter e setter

}

Ficara assim

pessoa
usuario_id - integer primery key

contato
id - integer primary key,
usuario_id integer foreign key

Vc podera ver mais informações na documentação do hibernate!

J

Valeu renato_natos, já ajudou muito!

Mãos a obra agora!

obrigado.

qquer coisa posto aqui!

R

blz, boa sorte, qualquer problema estamos ai …

J

Criei a base novamente com os relacionamentos corretos, e a classe java foi criada também corretamente, porém ao rodar dá a seguinte mensagem de erro:

javax.ws.rs.WebApplicationException: javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: A cycle is detected in the object graph. This will cause infinitely deep XML: entitys.Categorias[ categoriasId=2 ] -> entitys.Categorias[ categoriasId=2 ]]

Alguma idéia?

J

Então pessoal…

Criei as foreign keys corretamente para informar os relacionamentos e mandei gerar o código das minhas entidades novamente, porém ao realizar as buscas, estou recuperando apenas dados da entidade principal, mas não da entidade que se relaciona com ela, por exemplo, minha classe produto não retorna os dados da categoria que ele pertence. Vou postar aqui a minha classe para ver se alguém pode ajudar.

package entitys;

import java.io.Serializable;
import java.math.BigDecimal;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlRootElement;


@Entity
@Table(name = "PRODUTOS")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Produtos.findAll", query = "SELECT p FROM Produtos p"),
    @NamedQuery(name = "Produtos.findByProdutosId", query = "SELECT p FROM Produtos p WHERE p.produtosId = :produtosId"),
    @NamedQuery(name = "Produtos.findByProdutosNome", query = "SELECT p FROM Produtos p WHERE p.produtosNome = :produtosNome")})
public class Produtos implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "PRODUTOS_ID")
    private BigDecimal produtosId;
    @Size(max = 4000)
    @Column(name = "PRODUTOS_NOME")
    private String produtosNome;
    @JoinColumn(name = "FK_CATEGORIAS_ID", referencedColumnName = "CATEGORIAS_ID")
    @ManyToOne
    private Categorias fkCategoriasId;

    public Produtos() {
    }

    public Produtos(BigDecimal produtosId) {
        this.produtosId = produtosId;
    }

    public BigDecimal getProdutosId() {
        return produtosId;
    }

    public void setProdutosId(BigDecimal produtosId) {
        this.produtosId = produtosId;
    }

    public String getProdutosNome() {
        return produtosNome;
    }

    public void setProdutosNome(String produtosNome) {
        this.produtosNome = produtosNome;
    }

    public Categorias getFkCategoriasId() {
        return fkCategoriasId;
    }

    public void setFkCategoriasId(Categorias fkCategoriasId) {
        this.fkCategoriasId = fkCategoriasId;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (produtosId != null ? produtosId.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        
        if (!(object instanceof Produtos)) {
            return false;
        }
        Produtos other = (Produtos) object;
        if ((this.produtosId == null && other.produtosId != null) || (this.produtosId != null && !this.produtosId.equals(other.produtosId))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "entitys.Produtos[ produtosId=" + produtosId + " ]";
    }
    
}

Taí a classe da minha entidade Produtos, mesmo estando ali qual é sua chave relacionada, ainda não consigo pegar os dados da categoria a qual o produto se refere.

Se alguém puder ajudar!

Obrigado

R

O objeto categoria esta nulo?

Tenta isso:

@ManyToOne(fetch = FetchType.EAGER)
  • Lembrando que quando vc não informa este parametro o default é EAGER mas não custa tentar.

Qual o nome da chave primaria da tabela de categorias?

J

Chama-se CATEGORIAS_ID

R

Não quero ser chato mas na escolha dos nomes das tabelas e campo procure sempre colocar no singular.
Por exemplo:
Categoria, categoria_id

Deixar no plural apenas os nomes de objetos referente a listas de objetos

Tente tirar o atributo ‘name’ do @JoinColumn

J

Não é chatice não, é sempre bom implementar utilizando as melhores convenções, mas quanto ao problema eu consegui resolver da seguinte maneira:

  • Implementei a interface CycleRecoverable
  • Sobrescrevi o método onCycleDetected e resolveu, segue:
public class Categorias implements Serializable,CycleRecoverable {
.
.
.
.

    @Override
    public Object onCycleDetected(Context cntxt) {
        Categorias categoria = new Categorias(this.categoriasId);
        return categoria;
    }    

}//fim classe Categorias

O mesmo fiz na classe Produtos e resolveu!

J

[RESOLVIDO]

Criado 10 de fevereiro de 2012
Ultima resposta 13 de fev. de 2012
Respostas 19
Participantes 3