NHibernate中Get和Load的区别

NHibernate中的Get和Load方法是我们最常用的加载单个对象实例的方法。如果不了解这两者的区别则会存在随意使用的情况。

主要区别有两个:

1.对于无此POID的情况,Get方法会返回null,而Load方法会抛出异常(异常并不是在调用Load方法时立即抛出的)

2.Get方法是立即从数据库中加载该对象,而Load方法返回的是一个代理对象,没有立即命中数据库,也就是所谓的延迟加载。

其实第二点区别才是最主要的,我们的实际业务中应该根据需要使用Get或者Load。

接下来对上面的两个区别用代码来解释一下:

以下代码使用Get方法获取User对象:

 var sessionFactory = new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
            var session1 
= sessionFactory.OpenSession();

            session1.BeginTransaction();

            
//使用get方法取得对象实例
            Console.WriteLine("----session1.Get方法开始---------");
            var user1 
= session1.Get<Model.User>(new Guid("1bd7e168-3ba6-43ae-aba3-fb121c6088cc"));
            Console.WriteLine(
"----session1.Get方法结束---------");

            Console.WriteLine(
"----开始输出Id---------");
            Console.WriteLine(
"Id:" + user1.Id);

            Console.WriteLine(
"----开始输出Name---------");
            Console.WriteLine(
"Name:"+user1.Name);

            session1.Transaction.Commit();
            session1.Close();

 

上述guid实际存在于数据库中,让我们来看一下NH的SQL语句的执行情况:

从上面的结果可以看到,当调用Get方法时,NH即发出Select语句,立即从数据库中加载数据。

 

再来看看Load的情况:

 var session2 = sessionFactory.OpenSession();

            session2.BeginTransaction();

            
//使用load方法取得对象实例
            Console.WriteLine("----session2.Load方法开始---------");
            var user2 
= session2.Load<Model.User>(new Guid("1bd7e168-3ba6-43ae-aba3-fb121c6088cc"));
            Console.WriteLine(
"----session2.Load方法结束---------");

            Console.WriteLine(
"----开始输出Id---------");
            Console.WriteLine(
"Id:" + user2.Id);

            Console.WriteLine(
"----开始输出Name---------");
            Console.WriteLine(
"Name:" + user2.Name);

            session2.Transaction.Commit();
            session2.Close();

 

以下是控制台输出结果:

这次和前一次的结果明显不同,在调用Load方法时并没有执行Select语句,当我们输出user2.Id属性时依旧没有执行Select语句,因为Id属性的值不需要从数据库中获得,可以通过传入Load方法的参数值取得,这也可以看出NH是会尽最大努力进行延迟加载。当我们输出user2.Name属性时ND发出了相应的SQL语句,并且真正实例化了user2对象。

 

通过上面的分析,我们很容易想到,如果当guid不存在于数据库中时,调用Load方法并不会出现异常,而且输出user2.Id属性时也不会出现异常,只有当真正加载数据时才会出现异常。

对比Get方法,如果对于Get方法返回的对象不作是否为null判断的话,则会在输出user.Id是报出异常。

 

 

什么情况下使用Get? 什么情况下使用Load?

如果我们获取一个对象实例,只是使用到它的POID时就应该使用Load。下面举例说明:

还是接着使用上面的User类,我们再增加一个Department类,User类中有一个对Department对象的引用,也就是存在着many-to-one的关系,这里只做从User到Department的单向关联关系。两个类的代码如下:

 

 

User类和Department类
public class User
    {
        
public virtual Guid Id { getprotected set; }

        
public virtual string Name { getset; }

        
public virtual string City { getset; }

        
public virtual Int16 Age { getset; }

        
public virtual bool Sex { getset; }

        
public virtual Model.Department Dept { getset; }

  
        
//需要重写Equals 和GetHashCode方法,这里略
    }

 
public class Department
    {
        
public virtual Guid Id { get;protected set; }

        
public virtual string Name { getset; }
    }

 

 

下面是两个类对应的映射文件:

映射文件
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
  
<class name="User" table="MyUser">
    
<id name="Id">
      
<generator class="guid" />
    
</id>
    
<property name="Name" not-null="true" />
    
<property name="City" />
    
<property name="Age" />
    
<property name="Sex" />
    
<many-to-one name="Dept" class="Department" column="DeptId"></many-to-one>
  
</class>
</hibernate-mapping>

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Model" namespace="Model">
  
<class name="Department" table="MyDept">
    
<id name="Id">
      
<generator class="guid" />
    
</id>
    
<property name="Name" not-null="true" />
  
</class>
</hibernate-mapping>

 

两个类足够简单,只存在着一个many-to-one的单向关联,但是这也足以说明问题了。

代码也很简单,我们新建了一个新的用户,而该用户属于一个部门,所以需要设置User的Dept属性值,而为了设置Dept属性我们则需要一个Department对象。

 使用Get的情况:

 var session3 = sessionFactory.OpenSession();
            session3.BeginTransaction();

            var newUser 
= new Model.User { Name = "阿六", Age = 36, City = "北京", Sex = false };

            Console.WriteLine(
"----session3.Get方法开始---------");
            var dept1 
= session3.Get<Model.Department>(new Guid("7e441cb5-3c96-43b4-b5e5-7a6567edcadf"));
            Console.WriteLine(
"----session3.Get方法结束---------");

            newUser.Dept 
= dept1;

            session3.Save(newUser);
        
            session3.Transaction.Commit();
            session3.Close();

 

 

 我们得到的控制台输出结果是:

从上面输出结果可以看出为了得到Department对象,NH从数据库了加载了对应的记录,并初始化了dept1变量,所以上述代码一共执行了两条SQL语句,思考一下,是否真的有必要需要两条SQL语句呢?

 

接下来是Load的情况:

 var session4 = sessionFactory.OpenSession();
            session4.BeginTransaction();

            var newUser4 
= new Model.User { Name = "小明", Age = 26, City = "广州", Sex = true };

            Console.WriteLine(
"----session4.Load方法开始---------");
            var dept4 
= session4.Load<Model.Department>(new Guid("7e441cb5-3c96-43b4-b5e5-7a6567edcadf"));
            Console.WriteLine(
"----session4.Load方法结束---------");

            newUser4.Dept 
= dept4;

            session4.Save(newUser4);

            session4.Transaction.Commit();
            session4.Close();

 

以下是控制台输出结果:

仔细看一下,我们就会发现和刚才的Get方法不同的是这里少了一条Select语句,虽然一条Select语句对大部分中小型项目来说也许并不重要,但是对于高并发的大型项目来说对性能上的影响还是挺大的,既然可以做的更好,我们为什么不做呢

 

当然,使用刚才的Load方法也是有缺点的,如果刚才的Department的guid不存在与数据库中,由于NH并不实际发出Select查询语句,所以它并不去验证是否实际存在,当保存User的时候就保存了一个不存在DeptId值,如果数据库中本身有外键关系,则会抛出SQL异常。

 

讲到这里本该结束了,但是上面还少讲到了一点,如果你在文件中显式设置了class的lazy="false"的话,那么Load也就不会延迟加载了,和Get基本一致了。

希望本文能对NH的初学者有所帮助

最后是本文的示例代码:

/Files/szp1118/NHibernateTest.rar

 

 

补充:

刚才有园友回复关于缓存的情况

其实上面的文中的都是指不存在缓存的情况,如果已经存在缓存则会去缓存中查找。

把第一个示例修改一下:代码如下:

 

var sessionFactory = new NHibernate.Cfg.Configuration().Configure().BuildSessionFactory();
            var session1 
= sessionFactory.OpenSession();

            session1.BeginTransaction();

            
//使用get方法取得对象实例
            Console.WriteLine("----session1.Get方法开始---------");
            var user1 
= session1.Get<Model.User>(new Guid("1bd7e168-3ba6-43ae-aba3-fb121c6088cc"));
            Console.WriteLine(
"----session1.Get方法结束---------");

            Console.WriteLine(
"----再次通过Get加载开始---------");
            var user11 
= session1.Get<Model.User>(new Guid("1bd7e168-3ba6-43ae-aba3-fb121c6088cc"));
            Console.WriteLine(
"----再次通过Get加载结束---------");
            Console.WriteLine(
"----通过Load加载开始---------");
            var user12 
= session1.Load<Model.User>(new Guid("1bd7e168-3ba6-43ae-aba3-fb121c6088cc"));
            Console.WriteLine(
"----通过Load加载结束---------");

            Console.WriteLine(
"----开始输出User1 Id---------");
            Console.WriteLine(
"User1 Id:" + user1.Id);

            Console.WriteLine(
"----开始输出User1 Name---------");
            Console.WriteLine(
"User1 Name:"+user1.Name);

            Console.WriteLine(
"----开始输出User11 Id---------");
            Console.WriteLine(
"User11 Id:" + user11.Id);

            Console.WriteLine(
"----开始输出User11 Name---------");
            Console.WriteLine(
"User11 Name:" + user11.Name);

            Console.WriteLine(
"----开始输出User12 Id---------");
            Console.WriteLine(
"User11 Id:" + user12.Id);

            Console.WriteLine(
"----开始输出User12 Name---------");
            Console.WriteLine(
"User12 Name:" + user12.Name);
          
            session1.Transaction.Commit();
            session1.Close();

 

得到的结果如下:

通过上面的输出结果可以看到,三次加载同一个User对象时,只执行一条SQL语句,后两次是从缓存中取得数据。

 

延迟加载和缓存应该说是两个不同的概念, 本文重点在于说明延迟加载

posted @ 2010-12-22 21:23  喆_喆  阅读(3138)  评论(10编辑  收藏  举报