¿Consultar una colección de varios a varios o cómo incluir una tabla de varios a varios en una consulta de criterios?

StackOverflow https://stackoverflow.com/questions/5392292

Pregunta

Soy bastante nuevo en el mundo de NHibernate y parece que no puedo hacer que esto funcione con el uso de una consulta de criterios: consultar una relación de muchos a muchos o consultar una colección (conjunto / bolsa) en un entidad. Busqué en Internet y revisé todos los libros de NHibernate que tenemos, pero no puedo encontrar una respuesta específica a mi "desafío".

He hecho un ejemplo simplificado del problema que estoy tratando de resolver. Tengo una mesa con libros, una mesa con categorías y una mesa de muchos a muchos con las categorías por libro. Estos son algunos de los aspectos técnicos:

estructura de datos:

create table tableBook
(
    BkId       integer   not null default autoincrement,    
    BkTitle    char(40)  not null,
    BkWriter   char(40)  not null,
    primary key (BkId)
);

create table tableCategory
(
    CatId       integer   not null default autoincrement,    
    CatCode     char(3)   not null,
    CatDesc     char(40),
    primary key (CatId)
);

create table tableCategoriesPerBook
(
    CpbId        integer         not null default autoincrement,
    CpbBkId      integer         not null, /*foreign key to tableBook*/
    CpbCatId     integer         not null, /*foreign key to tableCategory*/
    primary key (CpbId)
);

alter table tableCategoriesPerBook add foreign key FK_CpbBkId (CpbBkId) references tableBook (BkId) on update Restrict on delete Cascade;
alter table tableCategoriesPerBook add foreign key FK_CpbCatId (CpbCatId) references tableCategory (CatId) on update Restrict on delete Cascade;

create unique index idx_CpbCatId_CpbBkId on tableCategoriesPerBook (CpbCatId, CpbBkId);

Clases de C #:

public class BookEntity
{
    public virtual Int32 BookId { get; set; }
    public virtual string BookTitle { get; set; }
    public virtual string BookWriter { get; set; }

    private readonly IEnumerable<CategoryEntity> _categories = new ObservableCollection<CategoryEntity>();
    public virtual IEnumerable<CategoryEntity> Categories
    {
        get { return _categories; }            
    }
}

public class CategoryEntity
{
    public virtual Int32 CategoryId { get; set; }
    public virtual string CategoryCode { get; set; }
    public virtual string CategoryDesc { get; set; }
}

Asignaciones de NHibernate:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Domain" assembly="Domain" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Domain.BookEntity" table="tableBook">
    <id name="BookId" column="BkId" type="Int32">
      <generator class="native" />
    </id>
    <property name="BookTitle" column="BkTitle" type="string" length="40"/>
    <property name="BookWriter" column="BkWriter" type="string" length="40"/>    
    <idbag name="_categories" access="field" table="tableCategoriesPerBook">        
        <collection-id type="Int32" column="CpbId">
          <generator class="native"/>
        </collection-id>        
        <key column="CpbBkId" property-ref="BkId"/>        
        <many-to-many column="CpbCatId" class="Domain.CategoryEntity, Domain" />
    </idbag>
  </class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Domain" assembly="Domain" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Domain.CategoryEntity" table="tableCategory">    
    <id name="CategoryId" column="CatId" type="Int32">
      <generator class="native" />
    </id>
    <property name="CategoryCode" column="CatCode" type="string" length="3" />
    <property name="CategoryDesc" column="CatDesc" type="string" length="40" />    
  </class>
</hibernate-mapping>

Mi pregunta: ¿es posible consultar (usando ICriteria y / o criterios separados) la base de datos de tal manera que obtengo los libros que están en una de las categorías que especifico (por ejemplo: en catA o catB, podría ser "y" también)? Quiero optimizar esto en la consulta, no en C # (ya que necesito leer todos los libros de la base de datos antes de poder filtrar los objetos según su colección de etiquetas). Si escribiera el SQL a mano, produciría algo como esto:

SELECT * FROM tableBook                                                                                                                           
WHERE EXISTS 
    (
     SELECT 1 
     FROM   tableCategoriesPerBook 
            INNER JOIN tableCategory on (CpbCatId = CatId and CpbBkId = BkId) 
     WHERE  CatCode in ('001', '002')
    )

Dado que no tengo una entidad para tableCategoriesPerBook, no veo una forma de llegar a esta tabla con una consulta de criterios. Y prefiero no agregar alguna pieza escrita a mano de expresiones SQL usando:

criteria.Add(Expression.Sql("exists(.....)");

Un último factor importante: estoy usando una base de datos abandonada, ¡así que no puedo cambiar la estructura! esto es con lo que tendré que trabajar con base de datos.

¿Fue útil?

Solución

Esto es bastante sencillo.Puede utilizar criterios separados.

DetachedCriteria bookCategoryCriteria = DetachedCriteria.For<BookEntity>("bookCat");
bookCategoryCriteria
    .CreateAlias("Categories", "cat", JointType.LeftOuterJoin)
    .Add(Restrictions.In("cat.CategoryCode", categories)
    .Add(Restrictions.Eq("bookCat.BookId", "book.BookId")
    .SetProjection(Projections.Id());

Session.CreateCriteria<BookEntity>("book")
   .Add(Subqueries.Exists(bookCategoryCriteria));
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top