博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NHibernate初学者指南(8):增删查改
阅读量:6344 次
发布时间:2019-06-22

本文共 9544 字,大约阅读时间需要 31 分钟。

在开始之前有必要说一下会话(session)和事务(transaction)。

session和transaction是什么

session和transaction是NHibernate提供的最重要的两个对象。通过session对象,可以与数据库进行通信以及执行各种操作。transaction对象为我们提供了一个工具,允许以一个单元管理多个操作。

Session

Nhibernate session可以看成是通往数据库的抽象管道。现在,必须创建一个ADOConnection,打开Connection,传递Connection给Command对象,从Command对象创建DataReader的日子一去不复返了。使用NHibernate,只需向sessionFactory请求一个Session对象,就这么简单。通过session对象,可以往数据库里添加数据,更新以及删除数据库中已有的数据,也可以读取数据库中已有的数据。所有的这些操作都可以使用面向对象的方式执行,不必处理SQL字符串和特定数据库的复杂性。session对象允许我们与数据库中的数据通信,而不用管是SQL Server数据库、MySql数据库或者是Oracle数据库等等。NHibernate完全为我们抽象了这些细节。

Transaction

transaction允许我们以一个工作单元执行很多的任务,结果是要么所有的操作都执行成功,要么即使一个任务执行失败,所有的任务都会返回到原来状态。这种工作方式,我们也称之为“原子操作”。

transaction有四个特点:原子性、一致性、隔离性、持久性。我们也使用首字母ACID描述这些特点。事务操作必须满足ACID。

使用session工厂创建session

NHibernate使用一个工厂对象创建session实例。这个工厂对象称为“对话工厂(session factory)”。一个session工厂可以根据需要创建任意多的session对象。session对象的创建是非常廉价的操作。

另一方面,session工厂的创建非常昂贵。根据系统的复杂性,它可以花费相当长的时间创建一个session工厂实例。这就是为什么我们在一个程序整个生命周期内只创建一个session工厂实例的原因。

一个session工厂是特定于数据库的。如果我们的程序只需要与一个单独的数据库通信,那么只需要一个session工厂。另一方面,如果我们的程序需要几个不同的数据库,那么每个数据库都需要一个session工厂。

session工厂是线程安全的(thread-safe)。运行在不同线程上的代码可以使用同一个session工厂创建session对象。相比之下,session对象只能用在单个线程中。换句话说就是:session对象不是线程安全的。

创建NHibernate session非常简单,一旦有了sessionFactory对象,就可以调用OpenSession方法创建它:

var session = sessionFactory.OpenSession();

通常,不管操作的结果如何,我们都想在using语句中打开session以保证session被关闭和释放。我们还想启动一个事务来对操作的结果做更好的预测。

using (var session = sessionFactory.OpenSession()) { using (var transaction = session.BeginTransaction()) { // create, update, delete, or read data transaction.Commit(); } }

添加数据

创建一个新的实体,可以调用session对象的Save方法持久化到数据库:

var newProductId = (int)session.Save(newProduct);

注意Save方法返回新生成记录的ID。因为有不同的策略生成ID(int,long或GUID),所以返回类型为object类型,我们必须转换结果到预期的类型。我们还可以访问刚刚持久化的实体的ID属性获得新生成ID的值来代替使用Save方法的返回值并转换到正确的类型。

var newProductId = newProduct.Id;

作为Save方法的替代方法,我们也可以使用session对象的Persist方法。Persist方法没有返回值,我们可以直接使用实体的ID属性获取新生成实体ID的值。这种情况下没有类型转换。

查询数据

根据指定的主键值从数据库中读取单条记录,可以使用session对象的Get或者Load方法。下面的代码从数据库中读取ID为1的Product实体:

var product = session.Get
(1);

使用Get或Load,我们必须知道要获取实体的ID。

如果想获取给定类型的实体列表,可以使用session对象的Query方法。这个方法是LINQ to NHibernate驱动程序的一部分。

获取所有的类别列表,可以使用下面的代码:

var allCategories = session.Query
().ToList();

注意语句后边调用ToList()方法。LINQ to NHibernate返回IQuery<Category>类型的列表,,它是延迟加载,如果想NHibernate提前加载所有的记录,就要通过调用ToList()强制完成。

Get和Load

如果延迟加载启动的话,Get和Load有很大的区别。

如果想加载Product实体,可以使用下面的代码:

var product = session.Get
(…?);

Get方法需要加载的实体的主键值作为参数,所以加载ID为1的Product,可以使用下面的代码:

var product = session.Get
(1);

如果请求的实体在数据库中存在,Get方法会物理的从数据库中检索它。如果给定ID的实体在数据库中不存在,那么返回NULL。

如果使用Load方法,NHibernate不会从数据库检索实体,而是创建一个代理对象。这个代理实体唯一填充数据的属性是ID,如下面的代码所示:

var product = session.Load
(1);

通过上面的代码,获得了一个ID为1的代理Product实体。此时,我们的代码会访问除ID以外的任何属性,NHibernate从数据库加载实体。实际上,这就是我们所谓的“延迟加载”。

那么什么情况下使用Load方法呢?需要访问和操作实体时,我们使用session对象的Get方法,那么不需要真的修改和访问实体的详细信息时使用Load方法。我们看个简单的例子。假设我们想更新一个存在Product实体改变它的类别。一件产品属于一个类别,因此,Product实体有一个Category类型的属性。然而,当操作products时,我们不想同时修改类别,只是使用它们。下面的代码能够实现:

var product = session.Get
(productId); product.Category = session.Load
(newCategoryId);

NHibernate会生成一个SELECT语句加载一个product,但是不会加载category。它只是创建一个代理实体,分配一个新的类别给产品。

更新数据

因为NHibernate session对象跟踪对加载的对象任何修改,所以不用显示的调用update或其他方法持久化修改到数据库。此时session对象被刷新,修改会由NHibernate自动持久化。下面的代码可以达到目的:

using (var session = sessionFactory.OpenSession())using (var tx = session.BeginTransaction()){    var product = session.Get
(1); product.UnitPrice = 10.55m; // new unit price tx.Commit();}

删除数据

删除数据库中存在的实体,必须首先加载它,然后将它传给session对象的Delete方法,如下面的代码所示:

var productToDelete = session.Load
(productId);session.Delete(productToDelete);

注意为了避免不必要的数据库往返,可以使用Load方法来代替Get方法。当刷新session时,上面代码结果就是一个简单的删除SQL语句送到数据库。但是这并适用于要删除的实体依赖其他实体或子实体的集合。这种情况下,必须加载实体到内存中。

实战时间

在所有理论后,让我们实现一个例子。在这个例子中,我们定义一个简单的模型,并使用自动映射来映射这个模型。然后我们创建一个session工厂,用它来创建session对象。

1. 在SMSS中新建一个空的数据库:NHibernateSessionSample。

2. 打开VS,创建一个Console Application项目:NHibernateSessionSample。

3. 为项目添加NHibernate,NHibernate.ByteCode.Castle和FluentNHibernate程序的引用。

4. 在项目中添加一个Domain文件夹,在该文件夹中添加一个Order类。

public class Order{    public virtual int Id { get; set; }    public virtual Customer Customer { get; set; }    public virtual DateTime OrderDate { get; set; }    public virtual IList
LineItems { get; set; }}

5. 在Order类中添加一个默认构造函数,初始化LineItems集合,如下面的代码所示:

public Order(){    LineItems = new List
();}

6. 在Order类中添加一个虚拟方法用来添加line item,如下面的代码所示:

public virtual void AddLineItem(int quantity, string productCode){    var item = new LineItem    {        Order = this,        Quantity = quantity,        ProductCode = productCode    };    LineItems.Add(item);}

7. 在Domain文件夹中分别添加LineItem类和Customer类,如下所示:

public class LineItem{    public virtual int Id { get; set; }    public virtual Order Order { get; set; }    public virtual int Quantity { get; set; }    public virtual string ProductCode { get; set; }}

 

public class Customer{    public virtual int Id { get; set; }    public virtual string CustomerName { get; set; }}

8. 在项目添加一个类文件:OrderingSystemConfiguration。添加下面的代码定义映射哪些类:

namespace NHibernateSessionSample{    public class OrderingSystemConfiguration : DefaultAutomappingConfiguration    {        public override bool ShouldMap(Type type)        {            return type.Namespace == typeof(Customer).Namespace;        }    }}

9. 在Program类中为session工厂定义一个静态变量,如下所示:

private static ISessionFactory sessionFactory;

10. 在Program类中添加一个静态方法ConfigureSystem。这个方法配置NHibernate使用SQL Server作为数据库,以及使用自动映射来映射模型和自动创建数据库架构:

private static void ConfigureSystem()        {            const string connString ="server=.;database=NHibernateSessionSample;" +"integrated security=SSPI;";            var cfg = new OrderingSystemConfiguration();            var model = AutoMap.AssemblyOf
(cfg); var configuration = Fluently.Configure() .Database(MsSqlConfiguration.MsSql2008 .ConnectionString(connString) .ShowSql ) .Mappings(m => m.AutoMappings.Add(model)) .BuildConfiguration(); var exporter = new SchemaExport(configuration); exporter.Execute(true, true, false); sessionFactory = configuration.BuildSessionFactory(); }

注意上面代码中.ShowSql的调用。它配置NHibernate将发送到数据库的所有SQL语句输出到控制台

11. 在Program类中添加一个静态方法CreateCustomers。这个方法创建两个customer对象,并使用session对象将它们存储到数据库中,如下面的代码所示:

private static void CreateCustomers(){    var customerA = new Customer { CustomerName = "Microsoft" };    var customerB = new Customer { CustomerName = "Apple Computer" };    using (var session = sessionFactory.OpenSession())    using (var transaction = session.BeginTransaction())    {        session.Save(customerA);        session.Save(customerB);        transaction.Commit();    }}

12. 在Main方法中,添加下面的代码:

static void Main(string[] args){    ConfigureSystem();    CreateCustomers();    Console.Write("\r\nHit enter to exit:");    Console.ReadLine();}

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

SQL命令的前几行显示了如果架构已经存在就将它们移除。通过执行上面的命令,在数据库中自动完成了数据库的架构,并在Customer表中插入了两条数据。

下面,我们创建一个订单。

14. 在Program类中添加一个静态方法CreateOrder,代码如下所示:

private static int CreateOrder(){    var customer = new Customer { CustomerName = "Intel" };    var order = new Order    {        Customer = customer,        OrderDate = DateTime.Now,    };    order.AddLineItem(1, "Apple");    order.AddLineItem(5, "Pear");    order.AddLineItem(3, "Banana");    int orderId;    using (var session = sessionFactory.OpenSession())    using (var transaction = session.BeginTransaction())    {        session.Save(customer);        orderId = (int)session.Save(order);        transaction.Commit();    }    return orderId;}

15. Main方法中,在CreateCustomers()之后,添加CreateOrder();如下面的代码所示:

static void Main(string[] args){    ConfigureSystem();    CreateCustomers();    //添加在这里    var orderId = CreateOrder();    Console.Write("\r\nHit enter to exit:");    Console.ReadLine();}

16. 如果这时运行程序的话会出现异常,因为默认情况下,auto-mapper配置HasMany关系为none-inverse和不级联的。为了配置我们想要的映射,在ConfigureSystem方法中添加如下代码(在 var model = AutoMap.AssemblyOf<Customer>(cfg);之后):

model.Override
(map => map.HasMany(x => x.LineItems).Inverse().Cascade.AllDeleteOrphan());

17. 这时再运行程序,一切OK,如下图所示:

注意,3个order的line items自动地插入到了数据库中。

下面加载一个订单,删除它的一个line item,并添加一个新的:

18.  在Program类中添加一个UpdateOrder静态类,代码如下:

private static void UpdateOrder(int orderId){    using (var session = sessionFactory.OpenSession())    using (var transaction = session.BeginTransaction())    {        var order = session.Get
(orderId); order.LineItems.RemoveAt(0); order.AddLineItem(2, "Apricot"); transaction.Commit(); }}

19. 在Main方法中调用  UpdateOrder(orderId);

20. 运行程序,如下图所示:

第一个select语句加载order。第二个select语句加载订单的line items。然后,一个insert语句,添加一个新的line item到数据库。最后,一个delete语句,从数据库中移除line item。

最后让我们删除订单。

21. 在Program类中添加一个DeleteOrder静态方法,代码如下:

private static void DeleteOrder(int orderId){    using (var session = sessionFactory.OpenSession())    using (var transaction = session.BeginTransaction())    {        var order = session.Load
(orderId); session.Delete(order); transaction.Commit(); }}

22. 在Main方法中调用DeleteOrder(orderId);

23. 运行程序,控制台的输出如下:

第一个select语句加载order,第二个加载order的line item。在订单本身删除之前,它的所有line item先删除。

总结

这篇文章首先介绍了什么是会话和事务,紧接着介绍了使用NHibernate进行增删查改的一些理论知识,最后通过一个控制台的例子,进行了实际的增删查改操作,并通过控制台的输出,可以看到生成的SQL语句。相信,通过这篇文章,你对NHibernate的增删查改有了一定的理解。

转载于:https://www.cnblogs.com/nianming/archive/2011/11/16/2251368.html

你可能感兴趣的文章
Oracle用户被锁定解决方法
查看>>
485总线的概念
查看>>
我的友情链接
查看>>
web前端笔记
查看>>
import 路径
查看>>
使用optimizely做A/B测试
查看>>
finally知识讲解
查看>>
Matplotlib绘图与可视化
查看>>
openstack ocata版(脚本)控制节点安装
查看>>
【微信公众号开发】获取并保存access_token、jsapi_ticket票据(可用于微信分享、语音识别等等)...
查看>>
在开发中处理海量数据的方法 思路
查看>>
datatable 获取最大值
查看>>
sqlserver2012一直显示正在还原(Restoring)和从单用户转换成多用户模式(单用户连接中)...
查看>>
spark复习总结02
查看>>
李瑞红201771010111《第九周学习总结》
查看>>
[译]ZOOKEEPER RECIPES-Barriers
查看>>
navicat下载安装和激活一分钟完成
查看>>
6_5 一些有用网址
查看>>
NFC 鏈表操作
查看>>
pymongo模块
查看>>