No post passado, Mapeamento One to Many com NHibernate, descrevi como fazer uma mapeamento One to Many (um para muitos), hoje vamos ver como fazer uma mapeamento Many to Many (muitos para muitos).
Nosso exemplo de hoje será uma entidade Livro e outra entidade Autor, onde um livro pode ser escrito por vários autores, e um autor pode escrever vários livros, dessa forma temos um relacionamento entre as entidades de muitos para muitos, dessa forma devemos criar uma tabela auxiliar contendo somente as chaves de cada entidade.
1) Criando as Entidades
Autor.cs
A entidade Autor possui uma lista de Livros
[sourcecode language=”csharp”]
public class Autor
{
public virtual int IdAutor { get; set; }
public virtual string Nome { get; set; }
public virtual DateTime DataNascimento { get; set; }
public virtual IList<Livro> Livros { get; set; }
}
[/sourcecode]
Livro.cs
A entidade Livro possui uma lista de Autores
[sourcecode language=”csharp”]
public class Livro
{
public virtual int IdLivro { get; set; }
public virtual string Nome { get; set; }
public virtual DateTime DataLancamento { get; set; }
public virtual IList<Autor> Autores { get; set; }
}
[/sourcecode]
2) Criando os Mapeamentos
Autor.hbm.xml
Criamos um BAG que onde vamos criar a tabela auxiliar (tb_livro_autor) e criamos uma propriedade muitos para muitos com a tabela livro
[sourcecode language=”xml”]
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ConexaoNHibernate.Entity"
namespace="ConexaoNHibernate.Entity" >
<class name="Autor" table="tb_autor">
<id name="IdAutor" access="property" column="id_autor" type="Int32">
<generator class="native"></generator>
</id>
<property name="Nome" not-null="true" access="property" type="String">
<column name="nome" length="100" />
</property>
<property name="DataNascimento" not-null="true" access="property"
type="DateTime">
<column name="data_nascimento" />
</property>
<bag name="Livros" table="tb_livro_autor" cascade="save-update">
<key column="id_autor" />
<many-to-many class="Livro" column="id_livro" not-found="ignore" />
</bag>
</class>
</hibernate-mapping>
[/sourcecode]
Livro.hbm.xml
Criamos um BAG que onde vamos criar a tabela auxiliar (tb_livro_autor) e criamos uma propriedade muitos para muitos com a tabela autor
[sourcecode language=”xml”]
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ConexaoNHibernate.Entity"
namespace="ConexaoNHibernate.Entity" >
<class name="Livro" table="tb_livro">
<id name="IdLivro" access="property" column="id_livro" type="Int32">
<generator class="native"></generator>
</id>
<property name="Nome" not-null="true" access="property" type="String">
<column name="nome" length="100" />
</property>
<property name="DataLancamento" not-null="true" access="property"
type="DateTime">
<column name="data_lancamento" />
</property>
<bag name="Autores" table="tb_livro_autor" cascade="save-update">
<key column="id_livro" />
<many-to-many class="Autor" column="id_autor" not-found="ignore" />
</bag>
</class>
</hibernate-mapping>
[/sourcecode]
3) Testando
Vamos cadastrar um novo Autor com seus Livros
[sourcecode language=”csharp”]
public ActionResult Create()
{
try
{
IList<Livro> livros = new List<Livro>();
Autor autor = new Autor();
autor.Nome = "Nome do autor";
autor.DataNascimento = DateTime.Now;
Livro livro1 = new Livro();
livro1.Nome = "Nome do livro 1";
livro1.DataLancamento = DateTime.Now;
livros.Add(livro1);
Livro livro2 = new Livro();
livro2.Nome = "Nome do livro 2";
livro2.DataLancamento = DateTime.Now;
livros.Add(livro2);
autor.Livros = livros;
_autorRepository.Salvar(autor);
return View();
}
catch (Exception ex)
{
log.Debug("ERRO", ex);
throw ex;
}
}
[/sourcecode]
Veja o objeto Autor criado com sua lista de Livros
Vamos ver se os registros foram gravados corretamente no BD
Qualquer dúvida, opinião, reclamação mande seu comentário!
Um Abraço!
Bacana cara,
Cogite fazer o mesmo tuto utilizando o fluent. Ajudaria muita gente,
abs,
Vinicius,
Ainda não trabalhei com fluent….. mas quem sabe um dia eu faça…
att,
Leandro Prado
Boa Tarde Leandro, gostei muito dos seus posts de one-to-many e many-to-many, mas acho que esse exemplo que você usou de Autor e Livro não foi muito Feliz, por que acredito que esse relacionamento também seria one-to-many certo ? Um autor tem vários livros mas um livro só pode ter um Autor, não sei se minha lógica está errada, mas é o que eu acho, não estou criticando só estou fazendo um comentário os posts foram ótimos, abraços.
Alex, muito bom seu questionamento…
quando escrevi esse post estava pensando exatamente nesse livro… http://amzn.to/fRb9wG
ai podemos ter vários autores para 1 livro
att,
Leandro Prado
Excelente tutorial.
Livros técnicos, livros jurídicos, livros feitos de compilações de vários textos, livros de domínio público (constituição) tem vários autores, vários revisores e tradutores.
Leandro,
ótimo post. Agora gostaria de tirar uma dúvida com você.
Suponha que eu tenho um relacionamento da tabela “tb_livro_autor” com uma outra tabela qualquer (“tb_x”) e nesta tabela eu tenho as colunas “id_livro” e “id_autor” como chave estrangeira.
Como eu devo montar o mapeamento da tabela “tb_x” se a tabela “tb_livro_autor” não possui mapeamento e na tabela “tb_x” eu tenho as duas chaves “id_livro” e “id_autor”?
Estou com um problema real aqui que é até meio complicado de explicar, rs.
Então se tiver como me ajudar por e-mail ou algo parecido me avise.
Obrigado.
Diego,
Acho q entendi sua pergunta…
mas mesmo assim não passei por uma situação dessa e nesse momento não saberia te responder…
se você conseguir resolver, peço que me avise porque isso é muito comum de acontecer..
att,
Leandro Prado
Dá para você fazer isso de duas formas, ou você cria uma entidade relacional para relacionar livro com autor e esquece o mapeamento many-to-many (aí a entidade x se relaciona com essa nova). Ou você cria uma entidade x que se relacione direto com as entidades livro e autor, mas não com a tabela livro_autor. Você pode definir as propriedades livro e autor da entidade x como not null e as duas juntas devem ser unique.
Fala cara, parabéns pelo seu blog, estou fuçando bastante agora com nHibernate e vi que seu blog tem bastante relevância na pesquisa do google :). E não é a toa, seus posts são excelentes.
valeu Welington.. fique a vontade para questionar..
aproveita e veja os posts sobre Fluent NHibernate
att,
Leandro Prado
tenho o seguinte relacionamento:
Proposta -> PropostaConsultoria <- Acao
PropostaConsultoria <- Area
PropostaConsultoria <- Servico
ou seja, uma proposta pode haver varias acoes assim como varias areas e varios servicoes, como faria esse mapeamento ?
Muito bom o artigo. Ajudou bastante. Gostaria de saber se passou pela seguinte situação e como fazer o mapeamento muitos-muito para este caso:
* Tabela Pessoa: (idPessoa, nome)
* Tabela Endereco: (idEndereco, endereco)
*Tabela Pessoa_Endereco: (idPessoa, idEndereco, fatura, cobranca)
Resumindo, um endereço pode ser fatura para uma pessoa e cobrança para outra. Não sei si fui claro.
Obrigado
e se eu quisesse ter mais campos na tabela tb_livro_autor, como faria? com esse mesmo tipo de relacionamento many-to-many
obrigado