代码之家  ›  专栏  ›  技术社区  ›  fostandy

如何使nhibernate cascade保存一组具有循环引用的对象?

  •  1
  • fostandy  · 技术社区  · 16 年前

    我假设nhibernate可以处理循环引用问题,因为我在docs或google上没有看到这一点(但可能我有错误的术语)。

    假设我有一个类,作为一个成员,它引用了自己的一个实例:

    例如

    class A
    {
       A Other;
    }
    

    然后我创建两个对象,让它们相互交叉引用

    A a1 = new A();
    A a2 = new A();
    
    a1.Other = a2;
    a2.Other = a1;
    

    我想为这些类生成一组映射,这样如果我试图在会话中保存A,它也会以保留B对A的引用的方式保存B。

    目前,我已经使用多对一关联生成了一个简单的映射(实际上这是由Fluent NHibernate生成的,但在手动检查时看起来还可以)

        <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="">
      <class name="hibernate.experiment.CircularRefQn+A, hibernate.experiment, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`A`" xmlns="urn:nhibernate-mapping-2.2">
        <id name="Id" type="Int32" column="Id">
          <generator class="identity" />
        </id>
        <many-to-one cascade="all" name="Other" column="Other_id" />
      </class>
    </hibernate-mapping>
    

    但当我保存时,A1不会将对A2的引用保存在数据库中。我怎样才能做到这一点?

    使用fluent nhibernate的示例代码在这里(需要nhibernate、fluent nhibernate和nunit-如果人们想要一个精简版,请告诉我)。

    我还创建了一个引用自身的A3对象,但这并不是我想保存的。

    using System.IO;
    using FluentNHibernate.Cfg;
    using FluentNHibernate.Mapping;
    using NHibernate.Tool.hbm2ddl;
    using NUnit.Framework;
    
    namespace hibernate.experiment
    {
        [TestFixture]
        public class CircularRefQn
        {
            [Test]
            public void Test()
            {
                var file = this.GetType().Name + ".db";
                if (File.Exists(file))
                    File.Delete(file);
    
                var fcfg = Fluently.Configure()
                    .Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration.Standard
                                  .UsingFile(file))
                                  .Mappings(m =>
                                  {
                                      m.FluentMappings.Add(typeof(A.Map));
                                      m.FluentMappings.ExportTo(".");
                                  })
                                  .ExposeConfiguration(cfg => new SchemaExport(cfg).Create(true, true))
                    ;
    
                var sFactory = fcfg.BuildSessionFactory();
    
    
                using (var s = sFactory.OpenSession())
                {
                    A a1 = new A();
                    A a2 = new A();
    
                    a1.Other = a2;
                    a2.Other = a1;
    
                    Assert.NotNull(a1.Other);
                    Assert.NotNull(a2.Other);
    
                    A a3 = new A();
                    a3.Other = a3;
    
                    s.Save(a1);
                    s.Update(a1);
                    s.Save(a3);
                }
    
                using (var s = sFactory.OpenSession())
                {
                    foreach (var a in s.CreateCriteria(typeof(A)).List<A>())
                        Assert.NotNull(a.Other);
                }
            }
    
            public class A
            {
                public virtual int Id { get; set; }
                public virtual A Other { get; set; }
    
                public class Map : ClassMap<A>
                {
                    public Map()
                    {
                        Id(x => x.Id);
                        References(x => x.Other)
                            .Cascade.All();
                    }
                }
            }
    
    
        }
    }
    
    1 回复  |  直到 16 年前
        1
  •  4
  •   Darin Dimitrov    16 年前

    总是在事务中执行插入/更新。下面是一个使用sqlite的工作示例:

    using System;
    using System.Data;
    using System.IO;
    using FluentNHibernate.Cfg;
    using FluentNHibernate.Cfg.Db;
    using FluentNHibernate.Mapping;
    using NHibernate;
    
    class Program
    {
        static void Main(string[] args)
        {
            if (File.Exists("data.db3"))
            {
                File.Delete("data.db3");
            }
    
            using (var factory = CreateSessionFactory())
            {
                // Create schema and insert sample data
                using (var connection = factory.ConnectionProvider.GetConnection())
                {
                    ExecuteQuery("create table users(usr_id integer primary key, other_id int, usr_name string)", connection);
                }
    
                using (var session = factory.OpenSession())
                using (var tx = session.BeginTransaction())
                {
                    User u1 = new User() { Name = "User1" };
                    User u2 = new User() { Name = "User2" };
    
                    u1.Other = u2;
                    u2.Other = u1;
                    session.Save(u1);
                    tx.Commit();
                }
    
                // Verify database state after inserts with ADO.NET
                using (var connection = factory.ConnectionProvider.GetConnection())
                using (var command = connection.CreateCommand())
                {
                    command.CommandText = "select usr_id, other_id, usr_name from users";
                    using (var reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            Console.WriteLine("usr_id: {0}, other_id: {1}, usr_name: {2}", reader.GetInt32(0), reader.GetInt32(1), reader.GetString(2));
                        }
                    }
                }
            }
        }
    
        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure()
                .Database(
                    SQLiteConfiguration.Standard.UsingFile("data.db3").ShowSql()
                )
                .Mappings(
                    m => m.FluentMappings.AddFromAssemblyOf<Program>()
                ).BuildSessionFactory();
        }
    
        static void ExecuteQuery(string sql, IDbConnection connection)
        {
            using (var command = connection.CreateCommand())
            {
                command.CommandText = sql;
                command.ExecuteNonQuery();
            }
        }
    }
    
    public class User
    {
        public virtual int Id { get; set; }
        public virtual string Name { get; set; }
        public virtual User Other { get; set; }
    }
    
    public class UserMap : ClassMap<User>
    {
        public UserMap()
        {
            WithTable("users");
            Id(x => x.Id, "usr_id");
            Map(x => x.Name, "usr_name");
            References(x => x.Other)
                .ColumnName("other_id")
                .Cascade.All();
        }
    }
    
    推荐文章