[NHibernate]用一个实体类对应多个数据库表(二)

上一次我们看到了如何让NHibernate通过写多个mapping“半自动”的实现我们动态映射的需求。

这一次我们看一下如何实现多对一之类的关系,以及如何存取数据。

1 两张XXX表存在多对一之类关系时的映射

  由于只指定Class,NHibernate还是不知道该Class具体应该映射到哪张表,于是需要同样的指定entity name

代码
  <class name="ChildHistoryEntity" entity-name="MFSChildHistoryEntity" table="tblMFSChildHistories">
    
<id name="Id" type="Guid" column="uidChildHistoryKey">
      
<generator class="guid.comb"/>
    
</id>
    ...
    
<many-to-one name="Child" class="ChildEntity" entity-name="MFSChildEntity" column="uidChildKey" not-null="true"/>
    ...
  
</class>

 

2 如何Save或者Update对象

  NHibernate的ISession接口提供了Save(Update等方法同样)的重载方法,可以指定entity name

  但是由于我们已经在Interceptor中拦截了GetEntityName方法,所以这里我们可以像以前一样调用ISession.Save(object entity) 

3 如何检索对象

  由于检索的时候NHibernate无法由类名直接推知表名了,所以我们必须手动的指定entity name了

  在使用ISession.Load的时候我们需要使用ISession.Load(string entityName, object id)方法

  在使用Criteria的时候,我们需要在CreateCriteria的时候传入entityName

  在使用HQL的时候,我们也需要从原来的from ClassName变为from EntityName

 

大概就是这样吧。

接下来的研究方向是通过一套模板的mapping自动生成各个可能的XXX专用mapping,大概思路就是把那些xml反序列化,然后Clone出来几份,replace一些东西之后再序列化,准备接下来看看NHibernate的源码,看看它的hbm2ddl里面有没有什么现成可用的东西~~

 

UPDATE:HQL貌似行不通,至少我还没有找到解决之道。具体如下

忽然发现一个问题:以下的UT代码,忽然发现跑不通了

代码
        [Test]
        
public void SaveChild()
        {
            var child = new ChildEntity
                            {
                                IdNo = "S1234567D",
                                IdType = ChildIdType.NRIC,
                                ...
                            };
            ChildDAO.Save(child);
            
            Assert.AreNotEqual(Guid.Empty, child.Id);
            Assert.IsNotNull(LoadByIdTypeAndNo(child.Centre.BU, child.IdType, child.IdNo));
        }

        
public ChildEntity LoadByIdTypeAndNo(string bu, ChildIdType idType, string idNo)
        {
            
return ChildDAO.FindByIdTypeAndNo(bu, idType, idNo);
        }

 

看了一下NH生成出的sql,吓了一跳 

代码
NHibernate: INSERT INTO tblMFSChildren ...
NHibernate: select ... from tblMFSChildren mfschilden0_ where mfschilden0_.chrIdType=@p0 and mfschilden0_.strIdNo=@p1;@p0 = 'N'@p1 = 'S1234567D'
NHibernate: select ...from tblCCChildren ccchildent0_ where ccchildent0_.chrIdType=@p0 and ccchildent0_.strIdNo=@p1;@p0 = 'N'@p1 = 'S1234567D'
NHibernate: select ... from tblLSHChildren lshchilden0_ where lshchilden0_.chrIdType=@p0 and lshchilden0_.strIdNo=@p1;@p0 = 'N'@p1 = 'S1234567D'

 

这其中MFS、CC、LSH是我的XXX的三种可能的取值

回头看一下DAO的写法,并没有select三个表

代码
        public ChildEntity FindByIdTypeAndNo(string bu, ChildIdType idType, string idNo)
        {
            
var query = string.Format("select c from {0}ChildEntity as c where c.ChrIdType=:idType and c.IdNo=:idNo", bu);
            
return Session.CreateQuery(query)
                
//.SetEnum("idType", idType)
                .SetCharacter("idType", (char)idType)
                .SetAnsiString("idNo", idNo)
                .SetMaxResults(1)
                .UniqueResult<ChildEntity>();

        }

 

回想一下从上次跑通UT到现在UT失败,做过的工作正是把mapping从一份(MFS)增加到了上述的三份(关于如何做到这一点还请期待下文)。

于是问题大概出在NH在翻译HQL的时候,把from entityName的部分首先翻译成了className,然后发现有三个entity对应着同一个className,于是生成出三条sql语句。但是如果直接指定成from className的话,又会报错说没有className对应的mapping。

找了半天IQuery的方法,也没有什么能够手动指定entityName的地方,无奈放弃HQL。。。哪位知道方法还望不吝赐教 

不过反正也早就瞅HQL不顺眼了~~又不能获得编译器的检查,又容易写错(例如少写一个空格之类的)。但是Criteria API我还是不会用(这玩意可读性也太差了吧。。。)没关系,祭出早就想试一试的LinqToNhibernate~~

代码
        public ChildEntity FindByIdTypeAndNo(string bu, ChildIdType idType, string idNo)
        {
            
return Session.Linq<ChildEntity>(bu + ChildEntity.StaticEntityClassName)
                .Where(child 
=> child.ChrIdType == (char)idType
                                
&& child.IdNo.Equals(idNo, StringComparison.OrdinalIgnoreCase))
                .FirstOrDefault();
        }

 

恩~~简单顺眼~~UT的结果也正确,就是不知性能如何~~~~以后再说吧~咳咳~~

P.S.1 ChrIdType本来在我的ChildEntity是protect virtual的因为我不希望外接能直接操作这个char字段,而是希望通过一个枚举来访问,但是现在不得不public出来了。解决办法么,一个是考虑到.Net世界里印象中提供一种FriendsAssembly机制可以让我的DAO的Assembly内部访问ModelAssembly中的类的internal字段,可以用这种方法解决(话说我都想吐槽我自己了,上述所说都是在正常的良好分层的架构下的考虑,而这次应客户的要求DAO(包括interface和impl)和Model是定义在同一个叫做DataAccess的Assembly,只需要protect internal就可以了(光internal还不行,NH不干)。。。客户要求多原来还有这种好处orz)。另一个更好的方向是直接使用NH提供的EnumStringType,这个今天刚看到,还没有尝试。

P.S.2 在初次使用LinqToNhibernate的时候发现NHibernate.Linq.dll竟然没有强命名,这是何等的师太= = 

Google了一下,找到Signer这个东西,给创建出的新的强命名过的dll就好了

 

最后吐槽一下Nhibernate,官方网站上挂的那个说明文档太老了吧!NH 2.1的特性就没怎么提啊~~~ 

posted @ 2010-07-14 18:40  jiaxingseng  阅读(1902)  评论(0编辑  收藏  举报