Mapeamento JPA [RESOLVIDO]

Olá, estou pesquisando mas não consegui encontrar uma solução, tenho o seguinte cenario:

Classes:
Pessoa.java
Cliente.java
Usuario.java
Email.java

Tabelas:
pessoa
cliente
usuario
pessoa_email
cliente_email
usuario_email

Não estou conseguindo fazer o mapeamento para esta estrutura de tabelas, se eu deixo para o JPA gerar as tabelas ele gera da seguinte forma:
pessoa
cliente
usuario
email

sendo que as seguintes sao usadas para o relacionamento de pessoa/cliente/usuario com a tabela email
pessoa_email
cliente_email
usuario_email

Mas o relacionamento que tenho é 1 para muitos (1 pessoa para muitos emails)

Toda as tabelas de email serao iguais, mas quero separar os dados em tabelas diferentes, nao quero colocar todos os dados em uma unica tabela com um campo identificador do tipo de email.
Alguem ja utilizou este tipo de relacionamento?

Pode mostrar as suas classes?


// Email.java

package entity;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import type.EmailType;

@Entity
@Table(name = "email")
public class Email implements Serializable {

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

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date", insertable = false, updatable = false)
    private Date createdDate;

    private String email;

    @Enumerated(EnumType.ORDINAL)
    private EmailType type;

    public Email() {
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public EmailType getType() {
        return type;
    }

    public void setType(EmailType type) {
        this.type = type;
    }

}

// Person.java
package entity;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "person")
public class Person implements Serializable {

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

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date", insertable = false, updatable = false)
    private Date createdDate;

    private String name;

    @Temporal(TemporalType.DATE)
    private Date birthday;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "person_email", joinColumns = {@JoinColumn(name = "person_id")}, inverseJoinColumns = {@JoinColumn(name = "email_id")})
    private List<Email> emails;

    public Person() {
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public List<Email> getEmails() {
        return emails;
    }

    public void setEmails(List<Email> emails) {
        this.emails = emails;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Vou ter tambem outras entidades tipo Customer, User, todas vao ter uma lista de Email, mas no banco quero uma tabela para cada lista de email.

Desta maneira o JPA gera as tabelas:
email
person
person_email (tabela de relacionamento entre person e email contendo somente id do email e id do person)

Estas sao as tabelas que eu quero mapear com JPA

create table if not exists person (
    id bigint unsigned not null auto_increment unique,
    name varchar(255),
    birthday date,
    sex char,
    created_date timestamp default current_timestamp,
    primary key(id)
);

create table if not exists customer (
    id bigint unsigned not null auto_increment unique,
    name varchar(255),
    birthday date,
    sex char,
    created_date timestamp default current_timestamp,
    primary key(id)
);

create table if not exists person_email (
    id bigint unsigned not null auto_increment unique,
    email varchar(255) not null,
    person_id bigint unsigned not null,
    created_date timestamp default current_timestamp,
    primary key(id),
    foreign key(person_id) references person(id)
);


create table if not exists user_email (
    id bigint unsigned not null auto_increment unique,
    email varchar(255) not null,
    user_id bigint unsigned not null,
    created_date timestamp default current_timestamp,
    primary key(id),
    foreign key(user_id) references user(id)
);

// Email.java

package entity;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import type.EmailType;

@Entity
@Table(name = "email")
public class Email implements Serializable {

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

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date", insertable = false, updatable = false)
    private Date createdDate;

    private String email;

    @Enumerated(EnumType.ORDINAL)
    private EmailType type;

    public Email() {
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public EmailType getType() {
        return type;
    }

    public void setType(EmailType type) {
        this.type = type;
    }

}

// Person.java
package entity;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "person")
public class Person implements Serializable {

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

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date", insertable = false, updatable = false)
    private Date createdDate;

    private String name;

    @Temporal(TemporalType.DATE)
    private Date birthday;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "person_email", joinColumns = {@JoinColumn(name = "person_id")}, inverseJoinColumns = {@JoinColumn(name = "email_id")})
    private List&lt;Email&gt; emails;

    public Person() {
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public List&lt;Email&gt; getEmails() {
        return emails;
    }

    public void setEmails(List&lt;Email&gt; emails) {
        this.emails = emails;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Vou ter tambem outras entidades tipo Customer, User, todas vao ter uma lista de Email, mas no banco quero uma tabela para cada lista de email.

Desta maneira o JPA gera as tabelas:
email
person
person_email (tabela de relacionamento entre person e email contendo somente id do email e id do person)

Estas sao as tabelas que eu quero mapear com JPA

create table if not exists person (
    id bigint unsigned not null auto_increment unique,
    name varchar(255),
    birthday date,
    sex char,
    created_date timestamp default current_timestamp,
    primary key(id)
);

create table if not exists customer (
    id bigint unsigned not null auto_increment unique,
    name varchar(255),
    birthday date,
    sex char,
    created_date timestamp default current_timestamp,
    primary key(id)
);

create table if not exists person_email (
    id bigint unsigned not null auto_increment unique,
    email varchar(255) not null,
    person_id bigint unsigned not null,
    created_date timestamp default current_timestamp,
    primary key(id),
    foreign key(person_id) references person(id)
);


create table if not exists user_email (
    id bigint unsigned not null auto_increment unique,
    email varchar(255) not null,
    user_id bigint unsigned not null,
    created_date timestamp default current_timestamp,
    primary key(id),
    foreign key(user_id) references user(id)
);

Sugiro que vc mapeie as classes Pessoa.java, Cliente.java e Usuario.java na classe Email.java com o OneToMany, sem passar nada para o parâmetro mappedBy.

Tipo assim:

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	private List&lt;Pessoa&gt; listPessoas;

Delete as tabelas, e execute novamente.

Não tenho certeza se funciona, mas já fiz algo parecido, sem passar o mappedBy, e acidentalmente aconteceu de criar uma tabela intermediária indesejada.

Espero ter ajudado.

[quote=linngallo]Sugiro que vc mapeie as classes Pessoa.java, Cliente.java e Usuario.java na classe Email.java com o OneToMany, sem passar nada para o parâmetro mappedBy.

Tipo assim:

	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	private List&lt;Pessoa&gt; listPessoas;

Delete as tabelas, e execute novamente.

Não tenho certeza se funciona, mas já fiz algo parecido, sem passar o mappedBy, e acidentalmente aconteceu de criar uma tabela intermediária indesejada.

Espero ter ajudado.[/quote]

Ok, vou testar, mas não quero que crie uma tabela intermediaria, quero que a estrutura do banco tenha as tabelas:
pessoa
usuario
cliente

pessoa_email
usuario_email
cliente_email

sendo que as tabelas para os dados de email terão suas estruturas identicas, so quero separar cada tipo de email (Pessoa, Cliente, Usuario) em uma tabela diferente,
mas nas classes java eu tenho uma so entidade para representar os tabelas de email

Pessoa.java
Cliente.java
Usuario.java
Email.java // esta classe representa as seguintes tabelas: pessoa_email, cliente_email, usuario_email

Nao quero ter uma tabela de relacionamento contendo somente id de duas tabelas e uma tabela que guarda todos os dados “misturados”.
E tambem nao queria criar no java classes identicas para representar cada tabela, Ex: (PessoaEmail.java, ClienteEmail.java, UsuarioEmail.java)

Hmmm, entendi.

Bem, acho que o que lhe falei acima lhe conduzirá à tabela com duas chaves estrangeiras, apenas.

Mas, existe uma configuração chamada “@ElementCollection(targetClass = algumaClasse.class)”, acho que isso pode te ajudar, se você usar em associação com o @JoinTable.

Dá uma pesquisada nisso.

Eu tenho um caso que é bem semelhante ao que você quer, dá uma olhada:


@ElementCollection(targetClass = UsuarioSenhaVO.class)
	@JoinTable(schema = "NomeDoBancoAqui", name = "senhasUsuario", joinColumns = {
			@JoinColumn(name = "idUsuario", referencedColumnName = "id"),
			@JoinColumn(name = "OutroId", referencedColumnName = "OutroId") })
	private List&lt;UsuarioSenhaVO&gt; senhasUsuario;

[quote=linngallo]Hmmm, entendi.

Bem, acho que o que lhe falei acima lhe conduzirá à tabela com duas chaves estrangeiras, apenas.

Mas, existe uma configuração chamada “@ElementCollection(targetClass = algumaClasse.class)”, acho que isso pode te ajudar, se você usar em associação com o @JoinTable.

Dá uma pesquisada nisso.

Eu tenho um caso que é bem semelhante ao que você quer, dá uma olhada:


@ElementCollection(targetClass = UsuarioSenhaVO.class)
	@JoinTable(schema = "NomeDoBancoAqui", name = "senhasUsuario", joinColumns = {
			@JoinColumn(name = "idUsuario", referencedColumnName = "id"),
			@JoinColumn(name = "OutroId", referencedColumnName = "OutroId") })
	private List&lt;UsuarioSenhaVO&gt; senhasUsuario;

[/quote]Caso não funcione com o @JoinTable, tente com @CollectionTable. Pelo menos o livro de JPA 2 que estou lendo só mostrou exemplo utilizando ColletcionTable.

[quote=linngallo]Hmmm, entendi.

Bem, acho que o que lhe falei acima lhe conduzirá à tabela com duas chaves estrangeiras, apenas.

Mas, existe uma configuração chamada “@ElementCollection(targetClass = algumaClasse.class)”, acho que isso pode te ajudar, se você usar em associação com o @JoinTable.

Dá uma pesquisada nisso.

Eu tenho um caso que é bem semelhante ao que você quer, dá uma olhada:


@ElementCollection(targetClass = UsuarioSenhaVO.class)
	@JoinTable(schema = "NomeDoBancoAqui", name = "senhasUsuario", joinColumns = {
			@JoinColumn(name = "idUsuario", referencedColumnName = "id"),
			@JoinColumn(name = "OutroId", referencedColumnName = "OutroId") })
	private List&lt;UsuarioSenhaVO&gt; senhasUsuario;

[/quote]

Muito Obrigado linngallo e jakefrog, consegui resolver com a ajuda de voces, vlw mesmo.

O mepeamento ficou assim:


// Person.java

package entity;

import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "person")
public class Person implements Serializable {

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

    private String name;

    @Temporal(TemporalType.DATE)
    private Date birthday;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date")
    private Date createdDate;

    @ElementCollection(targetClass = Email.class)
    @CollectionTable(name = "person_email", joinColumns = @JoinColumn(name = "person_id"))
    //@JoinTable(name = "person_email", joinColumns = {@JoinColumn(name = "person_id", referencedColumnName = "id")})
    private List&lt;Email&gt; emails;

    public Person() {
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public List&lt;Email&gt; getEmails() {
        return emails;
    }

    public void setEmails(List&lt;Email&gt; emails) {
        this.emails = emails;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

// Email.java

package entity;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Email implements Serializable {

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

    private String email;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_date")
    private Date createdDate;

    public Email() {
    }

    public Date getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(Date createdDate) {
        this.createdDate = createdDate;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

}

Se eu deixar o JPA gerar as tabelas ele gera uma Email tambem e dai ocorre o seguinte erro:

Caused by: org.apache.openjpa.lib.jdbc.ReportingSQLException: Incorrect table definition; there can be only one auto column and it must be defined as a key {stmnt 905804980 CREATE TABLE person_email (person_id BIGINT, id BIGINT AUTO_INCREMENT, created_date DATETIME, email VARCHAR(255)) ENGINE = innodb} [code=1075, state=42000]

O erro acontece porque não foi definido o campo Primary Key da tabela.

Mas eu gerei as tabelas por script SQL e desta maneira o mapeamento funcionou corretamente.

Muito Obrigado

Disponha. =]