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的单向关联关系。两个类的代码如下:

 

 

ExpandedBlockStart.gif User类和Department类
public   class  User
    {
        
public   virtual  Guid Id {  get protected   set ; }

        
public   virtual   string  Name {  get set ; }

        
public   virtual   string  City {  get set ; }

        
public   virtual  Int16 Age {  get set ; }

        
public   virtual   bool  Sex {  get set ; }

        
public   virtual  Model.Department Dept {  get set ; }

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

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

        
public   virtual   string  Name {  get set ; }
    }

 

 

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

ExpandedBlockStart.gif 映射文件
<? 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语句,后两次是从缓存中取得数据。

 

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

转载于:https://www.cnblogs.com/szp1118/archive/2010/12/22/1914156.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值