zoukankan      html  css  js  c++  java
  • NHibernate初学者指南(16):条件查询

    非类型化的条件查询

    我们从定义条件查询的根开始,代码如下所示:

    var query = session.CreateCriteria<Product>();

    CreateCriteria方法返回一个实现了ICriteria接口的对象。如果获取所有产品的列表,那么我们需要使用接口ICriteria的List<T>方法,如下面的代码所示:

    var products = session.CreateCriteria<Product>().List<Product>();

    List<Product>()方法返回IList<Product>。和LINQ to NHibernate相比,当List方法被调用时查询立即执行。

    当然还有一个非泛型的List法国法定义在ICriteria接口上。这个方法返回一个IList类型的对象。

    限制返回的记录数

    限制查询返回的记录数,可以使用SetMaxResults方法。从数据库中获得前10个产品,代码如下:

    var first10Products = session.CreateCriteria<Product>()
                           .SetMaxResults(10)
                           .List<Product>();

    筛选记录集

    如果筛选出下架的产品,代码如下所示:

    var discontinuedProducts = session.CreateCriteria<Product>()
                               .Add(Restrictions.Eq("Discontinued", true))
                               .List<Product>();

    通过给查询添加一个或多个限制条件筛选就完成了。如果我们想获得所有需要再订购的在架产品列表,我们可以使用下面的代码:

    var discontinuedProducts = session.CreateCriteria<Product>()
                               .Add(Restrictions.Eq("Discontinued", false))
                               .Add(Restrictions.GeProperty("ReorderLevel", "UnitsOnStock"))
                               .List<Product>();

    尽管这样很灵活,但是和LINQ to NHibernate相比也是非常容易出错的。假如把UnitsOnStock写成了UnitSonStock,只有在运行时才会意识到这个错误。

    另外,Restrictions这个静态类还有很多定义筛选条件的方便的方法。

    映射记录集

    现在让我们讨论如何映射记录集。这也称为投影。使用Criteria API没有使用LINQ方便,我们必须首先定义想要投影的字段。然后,还要定义一个transformer,它将这些值放入到所需的目标类型中,如下面的代码所示:

    var productsLookup = session.CreateCriteria<Product>()
                        .SetProjection(Projections.ProjectionList()
                        .Add(Projections.Property("Id"))
                        .Add(Projections.Property("Name"))
                        )
                        .SetResultTransformer(
                        new AliasToBeanResultTransformer(typeof(NameID)))
                        .List<NameID>();

    在上面的代码中,我们使用SetProjection方法定义映射。我们选择product的Id和Name属性,并将它们放入到NameID类型的对象中。类NameID定义如下:

    public class NameID
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    我们使用AliasToBeanResultTransformer把查询结果转换为NameID对象的列表。注意目标对象的属性名称必须与投影属性的名称匹配。如果不匹配,ProjectionList有一个Add的重载方法,我们可以定义一个别名。这个别名与目标对象的名称相匹配。

    排序结果集

    排序结果集非常简单。我们只需添加另外一个条件,如下面的代码所示:

    var sortedProducts = session.CreateCriteria<Product>()
                        .AddOrder(Order.Asc("Name"))
                        .List<Product>();

    以相反的顺序排列产品列表,只需使用Order类的Desc方法。我们还可以根据多个属性排序,只需为每个字段添加一个排序条件即可。

    分组记录集

    分组在LINQ提供程序中是单独的方法,但是在criteria API中是投影的一部分。假设我们想根据Category分组产品和统计每个类别的产品数,我们可以使用下面的查询:

    var productsGrouped = session.CreateCriteria<Product>()
                         .SetProjection(Projections.ProjectionList()
                         .Add(Projections.GroupProperty("Category"))
                         .Add(Projections.RowCount(), "Num")
                         )
                        .List();

    如果必须根据用户的选择动态生成查询,criteria API是最合适的。除此之外,LINQ to NHibernate更具有可读性以及从长远来看更有可维护性。

    强类型的条件查询

    NHibernatet 3引入了一个新的功能就是可以使用强类型定义条件查询。为此,QueryOver<T>加入到了ISession接口中。这里,泛型参数T表示我们想查询的实体类型。

    使用QueryOver API,我们指定查询的根,如下面的代码所示:

    var query = session.QueryOver<Product>();

    简单的获取所有产品的列表,使用如下所示的查询:

    var products = session.QueryOver<Product>().List();

    与Criteria API相比,QueryOver不需要指定返回类型。

    限制返回的记录数

    限制查询返回的记录数,我们可以使用Take方法。这个查询和LINQ to NHibernate的查询相似,如下面所示:

    var first10Products = session.QueryOver<Product>()
                          .Take(10)
                          .List();

    筛选记录集

    筛选记录集使用Where方法。获得所有下架产品的列表,使用下面的代码:

    var discontinuedProducts = session.QueryOver<Product>()
                               .Where(p => p.Discontinued)
                               .List();

    当然,我们还可以组合多个筛选,例如,获取所有在架上且需要再次订购的产品列表,如下面的代码所示:

    var productsToReorder = session.QueryOver<Product>()
                            .Where(p => p.Discontinued == false)
                            .Where(p => p.ReorderLevel >= p.UnitsOnStock)
                            .List();

    我们还可以使用下面的方式筛选:

    var productsToReorder = session.QueryOver<Product>()
                            .Where(p => p.Discontinued == false &&p.ReorderLevel >= p.UnitsOnStock)
                            .List();

    排序结果集

    使用QueryOver API排序和LINQ非常相似,只是LINQ定义OrderBy和OrderByDescending来定义升序和降序,而QueryOver API只定义了一个OrderBy方法。然而,这个方法必须和Asc或者Desc组合使用。当根据多个字段排序时,这两个API都有ThenBy(LINQ还有一个ThenByDescending)方法。

    获取根据Name升序和UnitPrice降序排列的产品列表,代码如下所示:

    var sortedProducts = session.QueryOver<Product>()
                        .OrderBy(p => p.Name).Asc
                        .ThenBy(p => p.UnitPrice).Desc
                        .List();

    投影结果集

    使用QueryOver API定义映射也是最难的一部分。如果只想检索所有产品的Id和Name,并将它们填充到NameID对象的数组中,可以使用下面的代码完成:

    var productsLookup = session.QueryOver<Product>()
                        .Select(p => p.Id, p => p.Name)
                        .TransformUsing(Transformers.AliasToBean<NameID>())
                        .List<NameID>();

    注意我们是如何使用Select方法定义我们想投影的属性列表。每个属性都由一个拉姆达表达式定义,例如p=>p.Name投影Name属性。然后,我们使用TransformUsing方法声明NHibernate如何转换投影结果。在前面的例子中我们选择AliasToBean转换器声明NameID作为目标转换类型。NHibernate还定义了其他的转换器,甚至可以定义自己的转换器。静态类Transformers给出了我们可用转换器的列表。最后调用List<NameID>结束。这里我们声明目标类型,否则,NHibernate会认为目标类型仍然是Product。

    分组记录集

    当我们使用投影转换数据时,还可以分组记录集以及给字段应用聚合函数。按照Category分组所有的产品,然后统计每个类别下产品的个数,还可以计算每个类别下的平均单价以及每个类别下的库存量总和。如下面的代码所示:

    var productsGrouped = session.QueryOver<Product>()
                        .Select(Projections.Group<Product>(p => p.Category),
                                Projections.Avg<Product>(p => p.UnitPrice),
                                Projections.Sum<Product>(p => p.UnitsOnStock),
                                Projections.RowCount())
                        .List<object[]>();

    为了简单,上面的代码中我们没有定义转换,只是让NHibernate返回结果集的行数。

    使用QueryOver检索数据

    在这个例子中,我们添加一些产品到数据库,然后使用QueryOver方法检索这些产品。

    同时,我们使用Loquacious配置和ConfOrm映射,就当复习前面的内容了。

    ConfOrm映射在NHibernate初学者指南(6):映射模型到数据库之方式二

    Loquacious配置在NHibernate初学者指南(14):配置的三种方式

    下面正式开始我们的例子。

    1. 在SSMS中创建一个数据库:QueryOverSample。

    2. 在Visual Studio中创建一个控制台应用程序:QueryOverSample。

    3. 为项目添加对NHibernate.dll,NHibernate.ByteCode.Castle.dll和ConfOrm.dll程序集的引用。

    4. 在项目中添加一个类文件Category.cs,添加如下代码定义Category实体:

    public class Category
    {
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }
        public virtual string Description { get; set; }
    }

    5. 在项目中添加一个类文件Product.cs,添加如下代码定义Product实体:

    public class Product
    {
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }
        public virtual Category Category { get; set; }
        public virtual decimal UnitPrice { get; set; }
        public virtual bool Discontinued { get; set; }
        public virtual int ReorderLevel { get; set; }
        public virtual int UnitsOnStock { get; set; }
    }

    6. 在Program类中,添加一个静态方法使用Loquacious配置创建一个NHibernate的Configuration对象:

    private static Configuration GetConfiguration()
    {
        var cfg = new Configuration();
        cfg.SessionFactory()
        .Proxy
        .Through<ProxyFactoryFactory>()
        .Integrate
        .LogSqlInConsole()
        .Using<MsSql2008Dialect>()
        .Connected
        .Through<DriverConnectionProvider>()
        .By<SqlClientDriver>()
        .Using(new SqlConnectionStringBuilder
        {
            DataSource = @".",
            InitialCatalog = "QueryOverSample",
            IntegratedSecurity = true
        });
        return cfg;
    }

    7. 在Program类中添加一个静态方法为实体定义映射,代码如下所示:

    private static void AddMappings(Configuration configuration)
    {
        var types = new[] { typeof(Category), typeof(Product) };
        var orm = new ObjectRelationalMapper();
        orm.TablePerClass(types);
        var mapper = new Mapper(orm);
        var hbmMappings = mapper.CompileMappingFor(types);
        configuration.AddDeserializedMapping(hbmMappings, "MyDomain");
    }

    8. 在Program类中添加一个静态方法创建或重新创建数据库架构,如下所示:

    private static void BuildSchema(Configuration configuration)
    {
        new SchemaExport(configuration).Execute(true, true, false);
    }

    9. 在Program中添加另外一个静态方法创建数据,如下面的代码所示:

    private static void BuildSchema(Configuration configuration)
    {
        new SchemaExport(configuration).Execute(true, true, false);
    }
    
    private static void AddProductsAndCategories(ISessionFactory sessionFactory)
    {
        var categories = new List<Category>();
        var products = new List<Product>();
        var random = new Random((int)DateTime.Now.Ticks);
        for (var i = 1; i <= 5; i++)
        {
            var category = new Category
            {
                Name = string.Format("Category {0}", i)
            };
            categories.Add(category);
            var count = random.Next(10);
            for (var j = 1; j <= count; j++)
            {
                var product = new Product
                {
                    Name = string.Format("Product {0}", i * 10 + j),
                    Category = category,
                    UnitPrice = (decimal)random.NextDouble() * 10m,
                    Discontinued = random.Next(10) > 8,
                    UnitsOnStock = random.Next(100),
                    ReorderLevel = random.Next(20)
                };
                products.Add(product);
            }
        }
        using (var session = sessionFactory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            foreach (var category in categories)
            {
                session.Save(category);
                foreach (var product in products)
                {
                    session.Save(product);
                }
            }
            tx.Commit();
        }
    }

    现在我们使用ISession接口的QueryOver方法创建一些数据报表。

    10. 创建一个静态方法,创建一个session和transaction,用来调用报表创建报表方法,如下所示:

    private static void PrintReports(ISessionFactory sessionFactory)
    {
        Console.WriteLine();
        Console.WriteLine("---------------------");
        Console.WriteLine("| Prining Reports |");
        Console.WriteLine("---------------------");
        using (var session = sessionFactory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
            PrintListOfCategories(session);
            tx.Commit();
        }
    }

    11. 我们需要实现PrintListOfCategories方法,代码如下:

    private static void PrintListOfCategories(ISession session)
    {
        Console.WriteLine("\r\nList of categories:\r\n");
        var categories = session.QueryOver<Category>()
        .OrderBy(c => c.Name).Asc
        .List();
        foreach (var category in categories)
        {
            Console.WriteLine("Category: {0}", category.Name);
        }
    }

    12. 在Program类中,创建一个静态字段,如下所示:

    private static ISessionFactory sessionFactory;

    13. 在Main方法中添加如下代码,创建配置,添加映射,创建或重新创建数据库架构,创建session工厂,创建和存储category和product实体,最后调用PrintReports方法,如下所示:

    var configuration = GetConfiguration();
    AddMappings(configuration);
    BuildSchema(configuration);
    sessionFactory = configuration.BuildSessionFactory();
    AddProductsAndCategories(sessionFactory);
    PrintReports(sessionFactory);
    Console.Write("\r\n\nHit enter to exit:");
    Console.ReadLine();

    14. 运行程序,结果如下图所示:

    15. 添加另一个报表方法,检索没有下架和需要再次订购的所有产品列表。产品列表应该先按category名称排序,再按product名称排序,代码如下所示:

    private static void PrintProductsToReorder(ISession session)
    {
        Console.WriteLine("\r\nList of products to reorder:\r\n");
        Product productAlias = null;
        Category categoryAlias = null;
        var products = session.QueryOver<Product>(() => productAlias)
        .JoinAlias(() => productAlias.Category, () => categoryAlias)
        .Where(() => productAlias.Discontinued == false)
        .Where(() => productAlias.ReorderLevel >= productAlias.UnitsOnStock)
        .OrderBy(() => categoryAlias.Name).Asc
        .ThenBy(() => productAlias.Name).Asc
        .List();
        Console.WriteLine();
        foreach (var product in products)
        {
            Console.WriteLine(
            "Category: {0}, Product: {1} (Units on stock: {2})",
            product.Category.Name, product.Name,
            product.UnitsOnStock);
        }
    }

    16. 在PrintReports方法中调用PrintProductsToReorder方法。

    17. 再次运行程序,结果如下图所示:

    注意,由于使用的是随机生成的数字,结果会不一样,我开始运行了两次,上图中的矩形框中都没有结果。

    总结

    首先我们讲解了条件查询的两种方式,即:非类型化的和强类型的。然后通过一个简单的例子,将理论知识得以应用,在例子中穿插着复习了前面的知识:使用Loquacious配置和ConfOrm映射。

  • 相关阅读:
    HTML基础 meta refresh 网页定时刷新
    HTML基础 meta name author 添加网页作者的信息
    HTML基础 mate refresh 5秒钟后,页面自动跳转
    HTML基础 marquee div块实现循环跑马灯的效果
    微服务jar包启动脚本
    怎么实现将word中的公式导入(或粘贴)到在线编辑中
    怎么实现将word中的公式导入(或粘贴)到网页编辑中
    Nginx实现浏览器端大文件分块上传
    javascript实现浏览器端大文件分块上传
    js实现浏览器端大文件分块上传
  • 原文地址:https://www.cnblogs.com/nianming/p/2265180.html
Copyright © 2011-2022 走看看