NHibernate考察系列 03 many-to-many 集合映射

   1. 创建关联
    表结构参考 NHibernate考察系列 01。使用TBLPURCHASEGROUP与TBLUSER进行多对多关联,关联表为TBLPURCHASEGROUPUSER。
    PurchaseGroup和User类代码以及映射配置文件在文章最下面。在NHTest项目里面运行下面的测试代码
static   void  Main( string [] args)
{
    ISessionFactory sessionFactory 
=   new  Configuration().Configure().BuildSessionFactory();
    ISession session 
=   null ;
    ITransaction trans 
=   null ;
    
try
    {
        session 
=  sessionFactory.OpenSession();
        trans 
=  session.BeginTransaction();

        User user1 
=   new  User( " user1 " " user1 " new  HashedSet < PurchaseGroup > ());
        session.Save(user1);
        User user2 
=   new  User( " user2 " " user2 " new  HashedSet < PurchaseGroup > ());
        session.Save(user2);
        User user3 
=   new  User( " user3 " " user3 " new  HashedSet < PurchaseGroup > ());
        session.Save(user3);
        PurchaseGroup group1 
=   new  PurchaseGroup( " 01A " " 一采01A " new  HashedSet < User > ());
        group1.Users.Add(user1);
        group1.Users.Add(user2);
        session.Save(group1);
        PurchaseGroup group2 
=   new  PurchaseGroup( " 701 " " 分公司采购组 " new  HashedSet < User > ());
        group2.Users.Add(user2);
        group2.Users.Add(user3);
        session.Save(group2);

        trans.Commit();
    }
    
catch
    {
        
if  (trans  !=   null )
            trans.Rollback();
    }
    
finally
    {
        session.Close();
    }

    sessionFactory.Close();
}
    执行上面的测试代码后,不仅添加了三个User对象和两个PurchaseGroup对象,NHibernate自动根据映射文件中的配置信息,在TBLPURCHASEGROUPUSER关联表中也添加好了关联关系。

    2. 主映射端 反向端
    首先注意到配置文件中的set元素上有个inverse属性,表明关联的方向。在User.hbm.xml文件中inverse属性值为true,说明 User是这个关联的反向端,而PurchaseGroup是主映射端。主映射端与反向端的区别,我们可以用下面的代码运行结果跟上面的运行结果做个比 较:
trans  =  session.BeginTransaction();
User user 
=   new  User( " user4 " " user4 " new  HashedSet < PurchaseGroup > ());
PurchaseGroup group1 
=  session.Get < PurchaseGroup > ( " 701 " );
if  (group1  !=   null )
    user.PurchaseGroups.Add(group1);
session.Save(user);
trans.Commit();
    这段代码的运行结果是user4这个对象被添加到数据库,但是user4和701的关联关系没有被添加到关联表中。从这个比较中可以看出,对映射关系做修 改(新增、删除)必须在主映射端完成,否则无法保存到数据库中。因此,在双向的many-to-many映射中,必须清楚哪个主映射端、哪个是反向端。

    3. 获取关联的集合对象
    现在注意,上面的测试已经在关联表中添加了如下的数据:
   
    接下来看一下怎样获取many-to-many集合对象,首先注意到在User.hbm.xml和Company.hbm.xml文件的set元素下, lasy属性被设成了false。打开SQL Server的SQL Profiler,监控执行的SQL语句。单步执行下面的测试语句,并随时观察SQL Profiler中监控到的SQL。
static   void  Main( string [] args)
{
    ISessionFactory sessionFactory 
=   new  Configuration().Configure().BuildSessionFactory();
    ISession session 
=   null ;

    session 
=  sessionFactory.OpenSession();
    PurchaseGroup group 
=  session.Get < PurchaseGroup > ( " 01A " );
    
if  (group != null   &&  group.Users  !=   null )
    {
        IEnumerator
< User >  enm  =  group.Users.GetEnumerator();
        
while  (enm.MoveNext())
        {
            User user1 
=  enm.Current;
            Console.WriteLine(
" id: {0}\t\tname: {1} " , user1.UserID, user1.UserName);
        }
    }
    Console.WriteLine(
"" );

    User user2 
=  session.Get < User > ( " user2 " );
    
if  (user2  !=   null   &&  user2.PurchaseGroups  !=   null )
    {
        IEnumerator
< PurchaseGroup >  enm  =  user2.PurchaseGroups.GetEnumerator();
        
while  (enm.MoveNext())
        {
            PurchaseGroup group2 
=  enm.Current;
            Console.WriteLine(
" id: {0}\t\tdescription: {1} " , group2.PurchaseGroupID, group2.Description);
        }
    }

    session.Close();
    sessionFactory.Close();
    Console.ReadLine();
}
    上面的测试代码运行正常,我们使用session.Get()方法获取PurchaseGroup或者User对象的时候,NHibernate会自动加 载相关的集合对象,因此我们可以直接枚举group.Users和user2.PurchaseGroups,将集合中的对象打印出来。
    SQL Profiler的结果:在执行 PurchaseGroup group  =  session.Get < PurchaseGroup > ( " 01A " );这一行代码时,会执行6个SQL查询,其余地方的代码再没有SQL。6个SQL查询如下
SQL1: exec sp_executesql N '
    SELECT purchasegr0_.PURCHASE_GROUP_ID as PURCHASE1_1_0_, purchasegr0_.DESCRIPTION as DESCRIPT2_1_0_ 
    FROM TBLPURCHASEGROUP purchasegr0_ 
    WHERE purchasegr0_.PURCHASE_GROUP_ID=@p0
' , N ' @p0 nvarchar(3) ' @p0   =  N ' 01A '
SQL2: exec  sp_executesql N '
    SELECT users0_.PURCHASE_GROUP_ID as PURCHASE1___1_, users0_.USER_ID as USER2_1_, 
    user1_.USER_ID as USER1_3_0_, user1_.USER_NAME as USER2_3_0_ 
    FROM TBLPURCHASEGROUPUSER users0_ 
    left outer join TBLUSER user1_ on users0_.USER_ID=user1_.USER_ID 
    WHERE users0_.PURCHASE_GROUP_ID=@p0
' , N ' @p0 nvarchar(3) ' @p0   =  N ' 01A '
SQL3: exec  sp_executesql N '
    SELECT purchasegr0_.USER_ID as USER2___1_, purchasegr0_.PURCHASE_GROUP_ID as PURCHASE1_1_, 
    purchasegr1_.PURCHASE_GROUP_ID as PURCHASE1_1_0_, purchasegr1_.DESCRIPTION as DESCRIPT2_1_0_ 
    FROM TBLPURCHASEGROUPUSER purchasegr0_ 
    left outer join TBLPURCHASEGROUP purchasegr1_ on purchasegr0_.PURCHASE_GROUP_ID=purchasegr1_.PURCHASE_GROUP_ID 
    WHERE purchasegr0_.USER_ID=@p0
' , N ' @p0 nvarchar(5) ' @p0   =  N ' user2 '
SQL4: exec  sp_executesql N '
    SELECT users0_.PURCHASE_GROUP_ID as PURCHASE1___1_, users0_.USER_ID as USER2_1_, 
    user1_.USER_ID as USER1_3_0_, user1_.USER_NAME as USER2_3_0_ 
    FROM TBLPURCHASEGROUPUSER users0_ 
    left outer join TBLUSER user1_ on users0_.USER_ID=user1_.USER_ID 
    WHERE users0_.PURCHASE_GROUP_ID=@p0
' , N ' @p0 nvarchar(3) ' @p0   =  N ' 701 '
SQL5: exec  sp_executesql N '
    SELECT purchasegr0_.USER_ID as USER2___1_, purchasegr0_.PURCHASE_GROUP_ID as PURCHASE1_1_, 
    purchasegr1_.PURCHASE_GROUP_ID as PURCHASE1_1_0_, purchasegr1_.DESCRIPTION as DESCRIPT2_1_0_ 
    FROM TBLPURCHASEGROUPUSER purchasegr0_ 
    left outer join TBLPURCHASEGROUP purchasegr1_ on purchasegr0_.PURCHASE_GROUP_ID=purchasegr1_.PURCHASE_GROUP_ID 
    WHERE purchasegr0_.USER_ID=@p0
' , N ' @p0 nvarchar(5) ' @p0   =  N ' user3 '
SQL6: exec  sp_executesql N '
    SELECT purchasegr0_.USER_ID as USER2___1_, purchasegr0_.PURCHASE_GROUP_ID as PURCHASE1_1_, 
    purchasegr1_.PURCHASE_GROUP_ID as PURCHASE1_1_0_, purchasegr1_.DESCRIPTION as DESCRIPT2_1_0_ 
    FROM TBLPURCHASEGROUPUSER purchasegr0_ 
    left outer join TBLPURCHASEGROUP purchasegr1_ on purchasegr0_.PURCHASE_GROUP_ID=purchasegr1_.PURCHASE_GROUP_ID 
    WHERE purchasegr0_.USER_ID=@p0
' , N ' @p0 nvarchar(5) ' @p0   =  N ' user1 '
    SQL1: 获取PurchaseGroup对象01A
    SQL2: 自动根据TBLPURCHASEGROUPUSER关联表获取01A对象关联到哪些User对象,以填充01A的Users集合。
    SQL3: 在SQL2中得到了两个用户user1和user2,并且这两个用户的属性在SQL2中都已经取出来了。但NHibernate还必须知道user1和 user2分别关联到哪些PurchaseGroup对象,这样才能生成完整的user1、user2两个对象,加入01A的Users集合中。这个语句 主要就是从 TBLPURCHASEGROUPUSER中查询user2关联的PurchaseGroup对象。
    接下来的SQL都是类似上面的方式,嵌套的处理,直到关联关系终止的地方。
    在执行
User user2  =  session.Get < User > ( " user2 " );这一行代码时,因为在前面已经创建过user2这个对象(并且包括了完整的user2.PurchaseGroups集合对象),因此此处不再需要执行SQL查询了(参考NHibernate考察系列 01中提到的session缓存)。
    几点结论:1. 自动获取集合对象,使用了join语句。2. 自动填充集合对象,因此多对多关联中,关联的记录不要过多,否则每次需要加载过多数据。
2. 双向的many-to-many关联,会嵌套处理填充集合对象,特别注意这一点。

    4. lasy 延迟加载
    然后看一下lasy属性,我们把
User.hbm.xml 和Company.hbm.xml文件中set元素的lasy都设置成true。另外需要注意的地方,使用延迟加载的实体类,属性必须声明为 virtual的才行,否则NHibernate会丢出一个异常,这是因为NHibernate使用代理对象实现延迟加载。运行下面的测试代码,同样使用 SQL Profiler监控。
static   void  Main( string [] args)
{
    ISessionFactory sessionFactory 
=   new  Configuration().Configure().BuildSessionFactory();
    ISession session 
=   null ;

    session 
=  sessionFactory.OpenSession();
    PurchaseGroup group 
=  session.Get < PurchaseGroup > ( " 01A " );
    
int  i  =  group.Users.Count;
    IEnumerator
< User >  enm1  =  group.Users.GetEnumerator();
    
while  (enm1.MoveNext())
    {
        User user1 
=  enm1.Current;
        Console.WriteLine(
" id: {0}\t\tname: {1} " , user1.UserID, user1.UserName);
    }

    Console.WriteLine(
"" );

    User user2 
=  session.Get < User > ( " user2 " );
    
int  j  =  user2.PurchaseGroups.Count;
    IEnumerator
< PurchaseGroup >  enm2  =  user2.PurchaseGroups.GetEnumerator();
    
while  (enm2.MoveNext())
    {
        PurchaseGroup group2 
=  enm2.Current;
        Console.WriteLine(
" id: {0}\t\tdescription: {1} " , group2.PurchaseGroupID, group2.Description);
    }
    session.Close();
    sessionFactory.Close();

    Console.ReadLine();
}
    这个测试代码跟上一个差不多,就是多了 int  i  =  group.Users.Count;和 int  j  =  user2.PurchaseGroups.Count;这样两行代码。
    完成的内容跟
上一个 测试代码一样,而执行的SQL语句要少,只有3个SQL,并且3个语句执行时机跟上面也不一样了。
SQL1: exec  sp_executesql N '
    SELECT purchasegr0_.PURCHASE_GROUP_ID as PURCHASE1_1_0_, purchasegr0_.DESCRIPTION as DESCRIPT2_1_0_ 
    FROM TBLPURCHASEGROUP purchasegr0_ 
    WHERE purchasegr0_.PURCHASE_GROUP_ID=@p0
' , N ' @p0 nvarchar(3) ' @p0   =  N ' 01A '
SQL2: exec  sp_executesql N '
    SELECT users0_.PURCHASE_GROUP_ID as PURCHASE1___1_, users0_.USER_ID as USER2_1_, 
    user1_.USER_ID as USER1_3_0_, user1_.USER_NAME as USER2_3_0_ 
    FROM TBLPURCHASEGROUPUSER users0_ 
    left outer join TBLUSER user1_ on users0_.USER_ID=user1_.USER_ID 
    WHERE users0_.PURCHASE_GROUP_ID=@p0
' , N ' @p0 nvarchar(3) ' @p0   =  N ' 01A '
SQL3: exec  sp_executesql N '
    SELECT purchasegr0_.USER_ID as USER2___1_, purchasegr0_.PURCHASE_GROUP_ID as PURCHASE1_1_, 
    purchasegr1_.PURCHASE_GROUP_ID as PURCHASE1_1_0_, purchasegr1_.DESCRIPTION as DESCRIPT2_1_0_ 
    FROM TBLPURCHASEGROUPUSER purchasegr0_ 
    left outer join TBLPURCHASEGROUP purchasegr1_ on purchasegr0_.PURCHASE_GROUP_ID=purchasegr1_.PURCHASE_GROUP_ID 
    WHERE purchasegr0_.USER_ID=@p0
' , N ' @p0 nvarchar(5) ' @p0   =  N ' user2 '
    SQL1是由 PurchaseGroup group  =  session.Get < PurchaseGroup > ( " 01A " );这一行代码产生的;
    SQL2是由
int  i  =  group.Users.Count;这一行代码产生;
    SQL3是由
int  j  =  user2.PurchaseGroups.Count;这一行代码产生。
    综合延迟加载和session缓存,很容易得出为什么会是这样一个执行状况:第一次访问集合时(包括调试过程中通过监视窗口或者是鼠标移动到集合属性上查看资料时),NHibernate才会加载数据生成这个集合对象。
    从上面可以看出lasy特性在数据加载量方面带来了改善。

    5. cascade 级联更新
    cascade属性的用法。为PurchaseGroup.hbm.xml的set元素添加cascade="delete"属性,执行下面的测试语句,然后把
cascade="delete"删除再次执行这个测试
session  =  sessionFactory.OpenSession();

tran 
=  session.BeginTransaction();
PurchaseGroup group 
=   new  PurchaseGroup( " 01B " " 01B " new  HashedSet < User > ());
User user 
=   new  User( " RicCC " " Richie " new  HashedSet < PurchaseGroup > ());
session.Save(user);
group.Users.Add(user);
session.Save(group);
tran.Commit();

tran 
=  session.BeginTransaction();
session.Delete(group);
tran.Commit();
    上面测试先创建一个用户,一个采购组,并把他们关联起来;然后删除采购组。在没有指定 cascade属性情况下,删除操作先删除01B的多对多关联关系,然后删除01B对象;指定 cascade="delete"时,删除操作最后会将用户对象RicCC也删除掉。

    6. 集合
排序
    接下来测试一下集合排序

    第一种方式,我们用sort属性实现。
    sort属性实现排序的原理,并不是通过数据库的ORDER BY子句实现,而是当集合对象被加入到集合类中时,通过我们提供的一个IComparer对象,使得对象在加入到集合后按照
IComparer对象实现的比较大小的方法按照顺序排列的。 我用set元素为PurchaseGroup类实现Users集合,当指定sort属性之后,NHibernate将使用Iesi.Collections.SortedSet来存储 Users集合对象。
    在这个示例里面,我希望
PurchaseGroup的 Users集合是按照UserName属性排序的,首先我们准备好测试数据。注意到现在数据库里面的关联关系如下:
   
    所以为01A对象取出的Users集合是按照user1、user2这样的顺序,在使用UserName排序之后我希望顺序跟这个不一样,以看到我指定的 排序是起作用的,所以在数据库里面直接将TBLUSER表user2的USER_NAME修改为user0,这样01A中的Users按照 UserName排序后应当就是user2、user1这个顺序了(数据库中记录的顺序仍然没有变化)。
   
    接下来是实现和测试排序结果。首先实现一个UserNameComparer类
public   class  UserNameComparer : System.Collections.Generic.IComparer < User >
{
    
public   int  Compare(User x, User y)
    {
        
// null > not null
         if  (x  ==   null   &&  y == null )
            
return   0 ;
        
if  (x ==   null   &&  y  !=   null )
            
return   1 ;
        
if  (x  !=   null   &&  y  ==   null )
            
return -1 ;
        
return  x.UserName.CompareTo(y.UserName);
    }
}
    然后修改PurchaseGroup默认构造器如下,其它的地方不用修改
public  PurchaseGroup()
{
    _users 
=   new  SortedSet < User > ( new  UserNameComparer());
}
    在PurchaseGroup.hbm.xml的set元素中添加这个属性配置:sort="NH12.MyExample.Domain.UserNameComparer, Domain"。
    这样就已经实现按照UserName排序了,现在用下面的代码测试一下
ISessionFactory sessionFactory  =   new  Configuration().Configure().BuildSessionFactory();
ISession session 
=   null ;
session 
=  sessionFactory.OpenSession();
PurchaseGroup group 
=  session.Get < PurchaseGroup > ( " 01A " );
IEnumerator
< User >  enm1  =  group.Users.GetEnumerator();
while  (enm1.MoveNext())
{
    User user1 
=  enm1.Current;
    Console.WriteLine(
" id: {0}\t\tname: {1} " , user1.UserID, user1.UserName);
}

Console.ReadLine();
    运行结果如下,证明按照UserName排序成功了。
   

    第二种方式,使用order-by属性实现。
    order-by属性实现排序的原理,是在SQL查询语句中追加指定的ORDER BY子句实现。首先,order-by属性是针对数据库字段,而不是实体属性;其次,order-by中指定的字段必须在关联表中存在,这给使用 order-by方式带来一定限制。而使用sort属性,可以基于整个User对象的属性来进行。
    同样为了看出测试效果,我们让PurchaseGroup 01A对象的Users集合按照USER_ID降序排列。为了进行下面的测试,先把上一步测试中的排序方法给移除掉:删除 PurchaseGroup.hbm.xml的set元素中sort属性、注释掉 PurchaseGroup类默认构造器中的 _users = new SortedSet<User>(new UserNameComparer());这一行代码。
    另外一点,当为set元素指定了order-by属性后,NHibernate将使用Iesi.Collections.ListSet实现集合对象,因为我在这个例子中使用了范型的ISet接口,而NHibernate在内部
没有 与Iesi.Collections.ListSet相对应的 范型实现,所以如果在这个示例中直接加上order-by属性,会出现异常。对于使用非范型的 ISet接口,这样做是不会有问题的。为了继续测试,我把集合由set元素改成bag元素,按照下面的步骤进行就可以。
    为Domain工程添加引用using System.Collections.Generic;,把PurchaseGroup类中所有的ISet接口替换成IList,其它的不要改。把 PurchaseGroup.hbm.xml中的set元素改成bag元素,在 PurchaseGroup.hbm.xml的bag元素上添加 order-by="USER_ID desc"。
    运行下面的代码测试
ISessionFactory sessionFactory  =   new  Configuration().Configure().BuildSessionFactory();
ISession session 
=   null ;
session 
=  sessionFactory.OpenSession();
PurchaseGroup group 
=  session.Get < PurchaseGroup > ( " 01A " );
IEnumerator
< User >  enm1  =  group.Users.GetEnumerator();
while  (enm1.MoveNext())
{
    User user1 
=  enm1.Current;
    Console.WriteLine(
" id: {0}\t\tname: {1} " , user1.UserID, user1.UserName);
}

Console.ReadLine();
    从Console的输出可以看到跟上一个测试一样的效果,即集合中Users对象的顺序为user2、user1。从监控的SQL语句可以看到在SQL中使用了ORDER BY。

    还有其它的方式实现集合排序,例如使用list、array集合映射元素,使用 子映射元素 index,但他们需要在关联表中指定一个整型或者是其它用于排序的索引、键值字段,这种方式使用起来总有种不舒服不自然的感觉,可能是本人SQL方面用惯了,比较在意数据库结构,或者是喜欢比较直接的事情的缘故。
    另外,不同的集合映射元素与接口对应关系:<list> - IList, <map> -
IDictionary, < bag> - IList, < set> - ISet, <array> - IList。当指定了order-by或者sort属性后,使用的具体类会有一些变化,例如指定order-by后: <map> - Specialized.ListDictionary, <set> - ListSet;指定sort后: <map> - SortedList, <set> - SortedSet

    关于集合、集合排序的效率问题,NHibernate文档中有一些说明,关键还得参考NHibernate对集合的内部实现,以及.Net framework中相关集合操作的效率。如果集合数据量比较大而对性能要求又比较高,使用有序集合总归不是一件好事。
   
    8. 附 实体类 映射配置文件
    User类:
#region  User
public   class  User
{
    
private   string  _userID;
    
private   string  _userName;
    
private  ISet < PurchaseGroup >  _purchaseGroups;  //  = new HashedSet<PurchaseGroup>();

    
public  User( string  id,  string  name, ISet < PurchaseGroup >  groups)
    {
        _userID 
=  id;
        _userName 
=  name;
        _purchaseGroups 
=  groups;
    }

    
public  User()
    {
    }

    
public   virtual   string  UserID
    {
        
get  {  return  _userID; }
        
set  { _userID  =  value; }
    }

    
public   virtual   string  UserName
    {
        
get  {  return  _userName; }
        
set  { _userName  =  value; }
    }

    
public   virtual  ISet < PurchaseGroup >  PurchaseGroups
    {
        
get  {  return  _purchaseGroups; }
        
set  { _purchaseGroups  =  value; }
    }

    
#region  override
    
public   override   bool  Equals( object  obj)
    {
        
if  ( this   ==  obj)  return   true ;
        
if  (obj  ==   null   ||  obj.GetType()  !=   this .GetType())
            
return   false ;
        User user 
=  obj  as  User;
        
return  user  !=   null   &&  user.UserID  ==  _userID;
    }

    
public   override   int  GetHashCode()
    {
        
return  _userID.GetHashCode();
    }

    
public   override   string  ToString()
    {
        
return  _userID;
    }
    
#endregion
}
#endregion

    PurchaseGroup类:
#region  PurchaseGroup
public   class  PurchaseGroup
{
    
private   string  _purchaseGroupID;
    
private   string  _description;
    
private  ISet < User >  _users;  //  = new HashedSet<User>();

    
public  PurchaseGroup( string  id,  string  desc, ISet < User >  users)
    {
        _purchaseGroupID 
=  id;
        _description 
=  desc;
        _users 
=  users;
    }

    
public  PurchaseGroup()
    {
    }

    
public   virtual   string  PurchaseGroupID
    {
        
get  {  return  _purchaseGroupID; }
        
set  { _purchaseGroupID  =  value; }
    }

    
public   virtual   string  Description
    {
        
get  {  return  _description; }
        
set  { _description  =  value; }
    }

    
public   virtual  ISet < User >  Users
    {
        
get  {  return  _users; }
        
set  { _users  =  value; }
    }

    
#region  override
    
public   override   bool  Equals( object  obj)
    {
        
if  ( this   ==  obj)  return   true ;
        
if  (obj  ==   null   ||  obj.GetType()  !=   this .GetType())
            
return   false ;
        PurchaseGroup group 
=  obj  as  PurchaseGroup;
        
return  group  !=   null   &&  group._purchaseGroupID  ==  _purchaseGroupID;
    }

    
public   override   int  GetHashCode()
    {
        
return  _purchaseGroupID.GetHashCode();
    }

    
public   override   string  ToString()
    {
        
return  _purchaseGroupID;
    }
    
#endregion
}
#endregion

    配置文件PurchaseGroup.hbm.xml和User.hbm.xml
<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping  xmlns ="urn:nhibernate-mapping-2.2"  namespace ="NH12.MyExample.Domain"  assembly ="Domain" >
  
< class  name ="PurchaseGroup"  table ="TBLPURCHASEGROUP" >
    
< id  name ="PurchaseGroupID"   >
      
< column  name ="PURCHASE_GROUP_ID"  sql-type ="nvarchar"  length ="3"  not-null ="true" />
      
< generator  class ="assigned"   />
    
</ id >

    
< property  name ="Description" >
      
< column  name ="DESCRIPTION"  length ="50"  sql-type ="nvarchar"  not-null ="true"   />
    
</ property >

    
< set  name ="Users"  table ="TBLPURCHASEGROUPUSER"  inverse ="false"  lazy ="false" >
      
< key  column ="PURCHASE_GROUP_ID" />
      
< many-to-many  class ="User"  column ="USER_ID" />
    
</ set >
  
</ class >
</ hibernate-mapping >

<? xml version="1.0" encoding="utf-8"  ?>
< hibernate-mapping  xmlns ="urn:nhibernate-mapping-2.2"  namespace ="NH12.MyExample.Domain"  assembly ="Domain" >
  
< class  name ="User"  table ="TBLUSER" >
    
< id  name ="UserID"   >
      
< column  name ="USER_ID"  sql-type ="nvarchar"  length ="18"  not-null ="true" />
      
< generator  class ="assigned"   />
    
</ id >

    
< property  name ="UserName" >
      
< column  name ="USER_NAME"  length ="40"  sql-type ="nvarchar"  not-null ="false" />
    
</ property >

    
< set  name ="PurchaseGroups"  table ="TBLPURCHASEGROUPUSER"  inverse ="true"  lazy ="false" >
      
< key  column ="USER_ID" />
      
< many-to-many  class ="PurchaseGroup"  column ="PURCHASE_GROUP_ID" />
    
</ set >
  
</ class >
</ hibernate-mapping >
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值