#c# #performance #nhibernate #nhibernate-mapping #lazy-loading
#c# #Производительность #nhibernate #nhibernate-отображение #отложенная загрузка
Вопрос:
У меня есть проект с помощью реализации NHibernate и с использованием отложенной загрузки. В этом проекте у меня есть два класса: Person и PersonIdentity. Связь между этими двумя — агрегация, означает, что у человека есть один PersonIdentity.
Сопоставление пользователей — это :
<class name="Person" table="Person_Person" >
<id name="Id" type="Int64" unsaved-value="0">
<generator class="native" />
</id>
<version name="Version" />
<property name="Name" column="Name"
type="String(255)" access="property" not-null="false" />
<one-to-one name="Identity" property-ref="Person"
class="Domain.Entities.PersonIdentity,Domain.Entities" cascade="delete" fetch="select" />
</class>
Сопоставление PersonIdentity является :
<id name="Id" type="Int64" unsaved-value="0" >
<generator class="native" />
</id>
<property name="FirstName" column="FirstName" type="String(255)" access="property" not-null="false" />
<property name="LastName" column="LastName" type="String(255)" access="property" not-null="false" />
<many-to-one name="Person" column="Person_id_fk" uniqe="true" class="Domain.Entities.Person,Domain.Entities"
outer-join="auto" fetch="select" access="property" not-null="true" />
</class>
Моя проблема в производительности. Когда я выполняю запрос только для человека, подобного этому :
var q = SessionInstance.Query<Person>();
IList list = q.ToList<Person>();
Я ожидаю только выполнения
SELECT * FROM Person_Person
Но, кроме того, для каждого пользователя в базе данных выполните запрос, подобный этому :
SELECT * FROM Person_Identiyt WHERE Id = 1;
SELECT * FROM Person_Identiyt WHERE Id = 2;
SELECT * FROM Person_Identiyt WHERE Id = 3;
...
И, согласно ленивому подходу, это нехорошо,
PersonIdentity не должен загружаться до его вызова.
Как я могу загружать только пользователей без PersonIdentity для первой загрузки?
Комментарии:
1. Для человека всегда есть идентификатор
Ответ №1:
Прокси (отложенная загрузка) никогда не используются для необязательных взаимно-однозначных запросов.
Прокси всегда означает, что что-то есть, но в случае взаимно однозначного возможно, что в другой таблице не существует строки. И поскольку прокси-сервер не может удалить себя из свойства owner (и установить для него значение null), прокси-серверы не могут использоваться.
Если ваша база данных гарантирует, что всегда существует идентификатор (внешний ключ от пользователя к идентификатору), вы можете добавить constrained="true"
к взаимно однозначному, и NHibernate будет использовать прокси.
Обходные пути:
- Всегда загружайте идентификатор (с
fetch="join"
), чтобы избежать проблемы выбора n 1. (Примечание: Если я правильно помню, может быть ошибка, которая в этом случае все еще выполняет n 1. Смотрите NHibernate Jira.) - Сопоставьте ссылку как коллекцию и предоставьте свойство в вашем классе Person, которое вызывает
collection.SingleOrDefault()
. Таким образом, вы можете использовать отложенную загрузку.
Комментарии:
1. Я добавил
constrained="true"
отношение «один к одному» для PersonIdentity в Person hbm. Но существуют две проблемы: при вставке и удалении Person возникает ошибка с таким сообщением: свойство not-null ссылается на домен с нулевым или временным значением. Сущности. PersonIdentity. Человек. Но обновление верно. Почему?2. Почему из-за этого изменения, Домен. Сущности. PersonIdentity. Person является временным? Но перед добавлением
constrained="true"
это было правильно? также, когдаGetByID(x)
для Person еще в каждом случае дополнительноSelect * from Person_Person
выполняется второй запрос дляSelect * from Person_identity where ID = x
. Почему?3. @ehsanzeynali Пожалуйста, опубликуйте сопоставление
PersonIdentity
и код, который вы используете для сохранения и загрузки объектов.4. Я добавил сопоставление PersonIdentity к вопросу. Мой код :
Person person = new Person(); person.PersonIdentity.FirstName = "Jack"; SessionInstance.Save(person);
5. @ehsanzeynali Ошибка, подождите. Взаимно однозначное отношение к свойству (а не к первичному ключу)
constrained="true"
не имеет смысла. Как NHibernate должен это вставить?Identity
требуется уже вставленныйPerson
, и вы сказали NHibernate, что он должен быть вставленIdentity
первым (сconstrained="true"
помощью). Это решение не будет работать для вас. Некоторые другие несвязанные моменты: (1)type="String(255)"
тоже не имеет смысла. Я полагаю, вы имеете в видуsql-type="String(255)"
. (2) Вы должны добавитьunique="true"
на стороне «многие к одному» вашего «один к одному», иначе это не настоящее «один к одному».