FluentNHibernate: один к одному
При описании модели предметной области по существующей БД можно запросто встретить отношение между объектами типа один к одному. К примеру это может быть пользователь и дополнительная информация о пользователе.
Особенность этого отношения заключается в том, что сущность UserDetails имеет ключ, которой является не только первичным ключем, но и ссылкой на родительскую запсь User, и соответсвенно равен идентификатору пользователя.
Вот схема таблиц БД:
Классы, описывающие сущности приведены на диаграмме ниже:
А вот код:
public class User { public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual UserDetails Details { get; set; } } public class UserDetails { public virtual int Id { get; set; } public virtual User User { get; set; } public virtual string Address { get; set; } }
Маппинги, которые у меня получились:
public class UserMap : ClassMap<User> { public UserMap() { Id(x => x.Id).GeneratedBy.Native("SEQ_USER"); Map(x => x.Name); HasOne(x => x.Details) .Cascade.All() .ForeignKey("UserDetails"); } } public class UserDetailsMap : ClassMap<UserDetails> { public UserDetailsMap() { Id(x => x.Id).GeneratedBy.Foreign("User"); Map(x => x.Address); HasOne(x => x.User).Constrained(); } }
А теперь проверим, что мы намапили:
[Test] public void CanInsertUser() { var user = new User() { Name = "BatMan", Details = new UserDetails() { Address = "New Vasuki" } }; user.Details.User = user; using (var tx = Session.BeginTransaction()) { Session.Save(user); tx.Commit(); } Session.Clear(); var actual = Session.Get<User>(user.Id); Assert.That(actual, Is.Not.Null); Assert.That(actual.Details, Is.Not.Null); }
Обратите внимание, что запись сущности необходимо проводить в транзакции, для того чтобы NHibernate сбросил свой внутренний буфер, и добавил в БД объект UserDetails (можно было бы просто вызвать Session.Flush). Иначе в БД попадет только пользователь, без дополнительной информации о себе. Кроме того, после сохранения объектов я очищаю сессию, для того чтобы запрос по идентфификатору сгенерировал select, иначе NHibernate возмет объект из кеша.
Выполнение это теста приводит к вставке в таблицы User UserDetails связанных записей:
INSERT INTO [User] (Name) VALUES (@p0); select SCOPE_IDENTITY(); @p0 = 'BatMan' INSERT INTO [UserDetails] (Address, Id) VALUES (@p0, @p1); @p0 = 'New Vasuki', @p1 = 1
Выборка пользователя методом Get генерирует слудущий SQL:
SELECT user0_.Id as Id2_1_, user0_.Name as Name2_1_, userdetail1_.Id as Id1_0_, userdetail1_.Address as Address1_0_ FROM [User] user0_ left outer join [UserDetails] userdetail1_ on user0_.Id=userdetail1_.Id WHERE user0_.Id=@p0; @p0 = 1