我假设nhibernate可以处理循环引用问题,因为我没有在文档或谷歌上看到过这种情况(但也许我的错误条款)。
假设我有一个类,它的成员是对自身实例的引用:
e.g。
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();
}
}
}
}
}
答案 0 :(得分:4)
始终在事务中执行INSERT / UPDATE。这是一个使用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();
}
}