NHibernate экономит тысячи предметов - PullRequest
1 голос
/ 06 октября 2011

Я пишу приложение, которое включает NHibernate.

Время загрузки и все в порядке, по сравнению с моей первоначальной конфигурацией. Тем не менее, я пытаюсь выполнить стресс-тест для более худшего сценария приложения. Этот стресс-тест существенно увеличил количество объектов, и теперь я сохраняю 3 класса: 2400 пересечений, 9600 зон и 5000 транспортных средств.

Моя проблема, с которой я сталкиваюсь, - попытка сохранить элементы в базе данных в первый раз. Я создаю все элементы, а затем пытаюсь сохранить их с помощью:

using (var tx = session.BeginTransaction())
{
    foreach (Vehicle veh in Program.data.Vehicles.list)
    {
        session.Save(veh);
    }
    //  Commit transactions
    tx.Commit();
}

Однако я продолжаю натыкаться на:

System.StackOverflowException не обработан Сообщение: необработанное исключение типа «System.StackOverflowException» произошло в System.Data.dll

Я попытался добавить счетчик для вызова Flush каждые 2 итерации цикла. Проблема в том, что Транспортное средство содержит список Зон, а Зона содержит список Транспортных Средств. Так уж получилось, что у каждого автомобиля есть список каждой зоны и наоборот. Так называется первый сеанс. Спасение - это не только сохранение первого транспортного средства, но и постановка в очередь сохранения каждой зоны, что, в свою очередь, сохраняет все транспортные средства.

У меня в списке Зон в Транспортном средстве установлено значение inverse = false; поэтому он сохраняет все зоны, которые есть в списке транспортных средств, если они не существуют.

Есть ли какой-нибудь способ сказать NHibernate о том, что изменения будут происходить так часто, чтобы я не нажимал исключение StackOverflowException? Я попытался добавить счетчик для очистки сеанса каждые 10 сохранений, но я столкнулся с исключением переполнения, прежде чем он достигнет этого. Когда он сохраняет первое транспортное средство, он сохраняет каждую зону. Поскольку каждая зона содержит каждое транспортное средство, оно спасает каждое транспортное средство. Я никогда не ударил флеш, пока не получил исключение переполнения.

Мне бы очень хотелось иметь возможность просто вызвать функцию сохранения на транспортном средстве, и она каскадно сохранит данные в Зоне. Это избавит меня от необходимости многократно повторять список зон, а затем снова ретушировать зону, когда я сохраню автомобиль.

Есть идеи?

EDIT Кажется, он задыхается от tx.Commit (). Любая помощь в решении этой проблемы будет принята с благодарностью.

EDIT Размещение отображений в соответствии с запросом. Я вырезал некоторые из избыточных свойств и сопоставляемых вещей и просто сохранил 3 списка для основных классов.

Транспортные средства и перекрестки являются производными от устройства и отображаются следующим образом:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`">
    <joined-subclass name="EMTRAC.Intersections.Intersection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="all-delete-orphan" access="field" fetch="join" inverse="false">
          <key>
            <column name="Zone_PK" />
          </key> 
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>
    </joined-subclass>
    <joined-subclass name="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="save-update" access="field" table="VehicleZones" inverse="true">
          <key>
            <column name="veh_id" not-null="true"/>
          </key>
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>     
    </joined-subclass>
  </class>
</hibernate-mapping>

И отображение зоны выглядит следующим образом:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Zone`">
    <id name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK"/>
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </property>    
    <component name="Vehicles" access="property">
      <bag name="_list" cascade="save-update" access="field" table="VehicleZones">
        <key>
          <column name="veh_id" not-null="true"/>
        </key>
        <many-to-many class="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </bag>
    </component>
  </class>
</hibernate-mapping>

Я установил обратное значение true на стороне Транспортного средства, чтобы, надеюсь, все они были сопоставлены через Транспортное средство, но не уверен на 100%, правильно ли это сопоставлено.

Ответы [ 2 ]

1 голос
/ 07 октября 2011

Вы можете попробовать постепенно добавлять свои зоны к вашему автомобилю и сохранять их.Например, вы бы сделали что-то вроде этого:

Vehicle newVehicle = new Vehicle();

foreach(Zone newZone in myNewZonesList)
{
    using (var tx = session.BeginTransaction())
    {
        newVehicle.AddZone(newZone);
        session.SaveOrUpdate(newVehicle);
        tx.Commit();
    }
}

Как часто этот сценарий действительно происходит?Является ли это обычным делом для создания нового транспортного средства и добавления к нему 9600 зон прямо с улицы?В какой-то момент вы должны подумать о YAGNI по моему мнению.

1 голос
/ 06 октября 2011

Это задыхается от tx.Commit(), поскольку это место, где выполняются реальные запросы к базе данных.

Если у вас правильные двусторонние отношения «многие ко многим» между зонами и транспортными средствами, и у вас есть каскады с обеих сторон, NHibernate должен позаботиться об этом и сохранить все правильно. Поэтому я предполагаю, что ваша проблема вызвана большим количеством сущностей и NHibernate, пытающихся нарисовать и сохранить весь граф объектов. Очистка здесь не поможет, поскольку вся операция сохранения выполняется каскадами при первом сохранении объекта.

Боюсь, что вам нужно либо разделить операцию сохранения, чтобы сначала вставить все Zones с пустыми коллекциями автомобилей, а затем вставить все автомобили с назначенными зонами, либо, возможно, рассмотреть возможность использования StatelessSession в NHibernate * для этого целого операция. Сеанс без сохранения состояния не хранит объекты в каком-либо кэше и не выполняет каскады - обычно он больше подходит для пакетных обновлений, подобных вашему.

...