Как сделать так, чтобы nhibernate cascade сохранил набор объектов с круговыми ссылками? - PullRequest
1 голос
/ 22 июля 2009

Я предполагаю, что nhibernate может справиться с проблемами циклических ссылок, поскольку я не видел, чтобы это упоминалось иначе в документации или в Google (но, возможно, у меня неправильные термины).

Предположим, у меня есть класс, который имеет в качестве члена ссылку на свой экземпляр:

, например

class A
{
   A Other;
}

Затем я создаю 2 объекта и получаю перекрестные ссылки друг на друга

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

a1.Other = a2;
a2.Other = a1;

Я хочу создать набор сопоставлений для этих классов, чтобы, если я попытаюсь сохранить a в сеансе, он также сохранит b таким образом, что ссылка b на a будет сохранена.

На данный момент я создал простое отображение с использованием связи «многие к одному» (на самом деле это генерируется беглым 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 в базе данных. Как я могу заставить это сделать это?

Пример кода, использующего свободный 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 Ответ

4 голосов
/ 22 июля 2009

Всегда выполняйте ВСТАВКУ / ОБНОВЛЕНИЕ внутри транзакции. Вот рабочий пример использования 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();
    }
}
...