1,我原来的主键采用int自增长方式,中间表主键也采用自增int,运行没有问题。
2,但是因为主键为自增型,对于多数据库的同步有问题,所以改为采用36位的无重复数。
   所有主键都改为36位的无重复数后, 测试报错。 大概意思是 nhibernate生成插入中间表SQL时,并没有生成ID,(从映射的意思来看,nhibernate也的确不知道 中间表主键的生成方式,所以也不会生成中间表ID;自增类型,不需要生成中间表ID,所以没有这个问题) 3,我把中间表的ID去掉后,数据就能保存成功了。但是又会在数据表中产生重复的记录。若把中间表两外键组合为主键时,插入相同数据时,会报关联约束错误的。
以上情况,导致多对多映射,采用32位无重复数ID时,会遇到上述问。请问 前辈们遇到此类情况是怎样处理的?小弟先谢过了!

解决方案 »

  1.   

    下面是数据表和测试方法:数据库表:功能表
    create table A_FunctionMenu (
       ID                   char(36)             not null,
       ParentID             char(36)             null,
       ApplicationID        char(36)             null,
       Level                int                  not null,
       SearchCode           varchar(64)          not null,
       FunctionName         varchar(64)          not null,
       IsMenu               bit                  not null,
       MenuURL              varchar(256)         null,
       Type                 char(2)              not null,
       CreateType           char(2)              not null,
       Sort                 int                  not null,
       AddTime              datetime             null,
       UpdateTime           datetime             null,
       IsShow               bit                  not null,
       constraint PK_A_FUNCTIONMENU primary key (ID)
    )角色表
    create table A_Role (
       ID                   char(36)             not null,
       RoleName              varchar(128)        not null,
       Sort                 int                  not null,
       Re               varchar(512)         null,
       IsShow               bit                  not null,
       constraint PK_A_ROLE primary key (ID)
    )
    go角色和功能的中间表
    create table A_RoleFunctionMenu (
       ID                   char(36)             not null,
       RoleID               char(36)             not null,
       FunctionMenuID       char(36)             not null,
       constraint PK_A_ROLEFUNCTIONMENU primary key (ID)
    )
    映射文件
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="Sight.Model.Admin.RoleEntity, Sight.Model.Admin" table="A_Role" >
        <id name="ID" access="property">
          <column name="ID" sql-type="char(36)" not-null="true"/>
          <generator class="uuid.hex">
            <param name="format">D</param>
            <param name="seperator">-</param>
          </generator>
        </id>
        <property name="RoleName" column="RoleName" type="String" length="128" />
        <property name="Re" column="Re" type="String" length="512" />
        <property name="Sort" column="Sort" type="Int32" length="4" />
        <property name="IsShow" column="IsShow" type="Boolean"/>    <bag name="FunctionMenus" table="A_RoleFunctionMenu" >
          <key column= "RoleID" />
          <many-to-many class="Sight.Model.Admin.FunctionMenuEntity,Sight.Model.Admin" column="FunctionMenuID" />
        </bag>  </class></hibernate-mapping><?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="Sight.Model.Admin.FunctionMenuEntity, Sight.Model.Admin" table="A_FunctionMenu" >
        <id name="ID" access="property">
          <column name="ID" sql-type="char(36)" not-null="true"/>
          <generator class="uuid.hex">
            <param name="format">D</param>
            <param name="seperator">-</param>
          </generator>
        </id>
        
        <!--property name="ParentID" column="ParentID" type="Int32" length="4" /-->
        <property name="Level" column="Level" type="Int32" length="4" />
        <property name="SearchCode" column="SearchCode" type="String" length="64" />
        <property name="FunctionName" column="FunctionName" type="String" length="64" />
        <property name="IsMenu" column="IsMenu" type="Boolean" />
        <property name="MenuURL" column="MenuURL" type="String" length="256" />
        <property name="Type" column="Type" type="String" length="2" />
        <property name="CreateType" column="CreateType" type="String" length="2" />
        <property name="Sort" column="Sort" type="Int32" length="4" />
        <property name="AddTime" column="AddTime" type="DateTime"/>
        <property name="UpdateTime" column="UpdateTime" type="DateTime"/>
        <property name="IsShow" column="IsShow" type="Boolean"/>    <many-to-one name="ParentFunctionMenu" column="ParentID" />    <bag name="ChildFunctionMenus" order-by="Sort desc" cascade="all" lazy="true">
          <key column="ParentID" />
          <one-to-many class="Sight.Model.Admin.FunctionMenuEntity, Sight.Model.Admin" />
        </bag>    <bag name="HasRoles" table="A_RoleFunctionMenu" lazy="true">
          <key column="FunctionMenuID" />
          <many-to-many class="Sight.Model.Admin.RoleEntity,Sight.Model.Admin" column="RoleID" />
        </bag>  </class></hibernate-mapping>运行测试方法:        public void AddFunctionAddRoleTest()
            {
                FunctionMenuBLL functionMenuBLL = new FunctionMenuBLL();
                FunctionMenuEntity functionMenuEntity = new FunctionMenuEntity();
                functionMenuEntity.FunctionName = "权限管理";
                functionMenuBLL.Add(functionMenuEntity);            RoleBLL roleBLL = new RoleBLL();
                RoleEntity role = new RoleEntity();
                role.RoleName = "校长";
                role.IsShow = true;            role.FunctionMenus.Add(functionMenuEntity);            roleBLL.Add(role);  
            }
      

  2.   


    所报错误:------ Test started: Assembly: Sight.Test.Admin.dll ------NHibernate: INSERT INTO A_FunctionMenu (Level, SearchCode, FunctionName, IsMenu, MenuURL, Type, CreateType, Sort, AddTime, UpdateTime, IsShow, ParentID, ID) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11, @p12); @p0 = '0', @p1 = '', @p2 = '权限管理', @p3 = 'False', @p4 = '', @p5 = '01', @p6 = '01', @p7 = '0', @p8 = '1900-1-1 0:00:00', @p9 = '1900-1-1 0:00:00', @p10 = 'True', @p11 = '', @p12 = 'c4400f86-bd15-4aed-9660-a6399d4ea33d'
    NHibernate: INSERT INTO A_Role (RoleName, Re, Sort, IsShow, ID) VALUES (@p0, @p1, @p2, @p3, @p4); @p0 = '校长', @p1 = '', @p2 = '0', @p3 = 'True', @p4 = '6612ad26-306f-4259-8490-96e83bddfa81'
    NHibernate: INSERT INTO A_RoleFunctionMenu (RoleID, FunctionMenuID) VALUES (@p0, @p1); @p0 = '6612ad26-306f-4259-8490-96e83bddfa81', @p1 = 'c4400f86-bd15-4aed-9660-a6399d4ea33d'
    TestCase 'M:Sight.Test.Admin.RoleTest.AddFunctionAddRoleTest'
    failed: could not insert collection: [Sight.Model.Admin.RoleEntity.FunctionMenus#6612ad26-306f-4259-8490-96e83bddfa81]
    NHibernate.ADOException: could not insert collection: [Sight.Model.Admin.RoleEntity.FunctionMenus#6612ad26-306f-4259-8490-96e83bddfa81] ---> System.Data.SqlClient.SqlException: 无法将 NULL 值插入列 'ID',表 'Sight.dbo.A_RoleFunctionMenu';该列不允许空值。INSERT 失败。
    语句已终止。
       在 System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection)
       在 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
       在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
       在 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
       在 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
       在 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async)
       在 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result)
       在 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
       在 System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
       在 NHibernate.Impl.NonBatchingBatcher.AddToBatch(IExpectation expectation)
       在 NHibernate.Persister.Collection.AbstractCollectionPersister.Recreate(IPersistentCollection collection, Object id, ISessionImplementor session)
       --- 内部异常堆栈跟踪的结尾 ---
       在 Sight.Tool.DataAccess.DAL.SessionManager.Add[T](T entity) 位置 D:\project\SightProject\Sight\Tool\DataAccess\DAL\SessionManager.cs:行号 165
       在 Sight.Tool.BusinessService.BaseBLL`2.Add(T entity) 位置 D:\project\SightProject\Sight\Tool\BusinessService\BaseBLL.cs:行号 32
       在 Sight.Test.Admin.RoleTest.AddFunctionAddRoleTest() 位置 D:\project\SightProject\Sight\Test\Admin\RoleTest.cs:行号 690 passed, 1 failed, 0 skipped, took 6.08 seconds.
      

  3.   

     - c#的Guid = Sql的uniqueidentifier
     - 修改你的数据表的schema, 把char(36)修改为uniqueidentifier
     - 修改相应的NHibernate mapping
     -   FunctionMenuBLL   functionMenuBLL   =   new   FunctionMenuBLL();
    functionMenuBLL.Id = Guid.NewGuid();
    ...
    ... 
      

  4.   

    这种方法也是不行的
    这样只是换了一种组件生成的方式,添加时人工给实体附值;我们也没有地方附与中间表ID值,NHibernate也不会知道这个中间表ID的类型;
    假如说,中间表ID为int型,Nhibernate该怎样处理呢?nhibernate不会产生中间表的ID,所以插入是也会会报主键为空的错误。所以,即然Nhibernate在多对多情况,不能指定中间ID的类型;
    那么生成这个ID的凭务只能交给数据库自动产生了所以采用36位无复数做ID,在多对对映射时的这种问题,不知各位是怎样处理的呢?谢谢了
      

  5.   

    http://www.51ini.com/viewthread.php?tid=186&extra=page%3D1&frombbs=1
      

  6.   

    你的mapping table 为什么要自带id?你应该使用复合ID(RoleID + FunctionMenuID)。
    需要例子的话可以给你发一个。
      

  7.   

    1.我测试过用复合ID的这种情况重复添加两次的时候,会报错,提示插入相同主键
    Nhibernate的映射文件是否有测试相同值不插入的选项呢?2.我也尝试过去掉重复主键的情况
    这样插入没问题,但重复插入的时候,在中间表中会产生多条记录。这是我的的测试用例,
            public void AddRoleFunction()
            {
                RoleBLL roleBLL = new RoleBLL();
                FunctionMenuBLL functionMenuBLL = new FunctionMenuBLL();
                
                RoleEntity role = roleBLL.Get("5b58e010-2871-4d2a-bcb3-99e0396f6587");
                FunctionMenuEntity function = functionMenuBLL.Get("d39fe279-1ec0-4191-b513-138c622e9d87");            role.FunctionMenus.Add(function);            roleBLL.Update(role,role.ID);
            }
      

  8.   

    http://www.cnblogs.com/jillzhang/archive/2007/03/23/685750.html
      

  9.   

    @greennetboy
    早就看了你前辈写的那篇文章了,文章中 中间表并没有加主键约束,
    如果我上面的例子也采用中间表不用主键,那么当我两次调用AddRoleFunction()的时候,
    就会在中间表加出现两条记录,这样我再读取Role.FunctionMenus.count 的时候就会出现逻辑上的错误。...呵呵,这种方法其实也是可行的;因为即使我在中间表加上单一主键,再重复调用两次AddRoleFunction(),还是会出现这种的问题。@awen177
    用复合主键是个好方法,但是在插入相同复合主键的时候,Nhibernate会抛出异常,不知nhibernate是否有相应的机制,不用抛出这种异常呢?谢谢
     
      

  10.   

    使用复合ID做主键时,插入重复的记录出现的主键重复异常是一个好正常的事情。为什么楼主这么希望不出现这个异常呢?
    再者,像你所说的。如果此表出现两条(“Admin”,“校长”)这样的角色记录时,就已经是逻辑错误。
    那末出现这个“重复主键的错误插入提示”就是一个合情理的现像。