2011-10-06 4 views
1

NHibernate를 통합하는 어플리케이션을 작성하고 있습니다.NHibernate는 수천 개의 아이템을 저장합니다.

로딩 시간과 모든 것이 초기 구성에서 비롯된 것 같습니다. 그러나 응용 프로그램에 대한 최악의 시나리오에 대한 스트레스 테스트를 수행하려고합니다. 이 스트레스 테스트는 물체의 수를 현저하게 늘렸고 현재 2400 교차점, 9600 구역 및 5000 대의 차량이라는 3 개의 클래스를 저장하고 있습니다.

처음 실행하면 데이터베이스에 항목을 저장하려고합니다. 나는 모든 항목을 만든 다음 그들을 통해 저장하려고 :

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

그러나, 나는에 걸쳐 실행 계속 :

System.StackOverflowException는 처리되지 않은 메시지였다 시스템 '형식의 처리되지 않은 예외. StackOverflowException '이 System.Data.dll에서 발생했습니다.

루프를 통해 매 2 회 반복을 호출하기 위해 카운터를 추가하려고 시도했습니다. 문제는 차량이 구역 목록을 포함하고 구역이 차량 목록을 포함한다는 것입니다. 모든 차량에는 모든 구역의 목록이 있으며 그 반대로도 마찬가지입니다. 그래서 첫 번째 세션을 호출합니다. 세이브는 첫 번째 차량을 저장하는 것이 아니라 모든 구역의 저장을 대기 상태로 유지합니다. 그러면 모든 차량이 저장됩니다.

나는 Vehicle의 Zone 목록을 inverse = false로 설정했습니다. 따라서 차량 목록에있는 모든 구역이 존재하지 않으면 저장됩니다.

그래서 내가 StackOverflowException을 치지 않도록 NHibernate가 변경 사항을 너무 자주 저지를 수있는 방법이 있습니까? 세션을 10 회 저장 할 때마다 플러시 카운터를 추가하려고했지만 오버플로 예외가 발생하기 전에이를 초과했습니다. 첫 번째 차량을 저장하면 모든 구역이 저장됩니다. 모든 구역에는 모든 차량이 들어 있으므로 모든 차량을 저장합니다. 오버플로 예외가 발생하기 전에 결코 플러시를하지 않았습니다.

저는 Vehicle에서 save를 호출 할 수 있기를 정말로 원합니다. 그리고 Zone에 저장을 계단식으로 연결합니다. 차량을 저장할 때 Zone 목록을 반복하고 Zone을 다시 수정해야하는 번거 로움을 덜어줍니다.

아이디어가 있으십니까?

편집 tx.Commit()에서 질식하는 것으로 보입니다. 이 문제를 해결하는 데 도움을 주시면 대단히 감사하겠습니다.

편집 게시 매핑이 질문대로. 초과 속성과 매핑 된 일부를 잘라내어 메인 클래스에 대한 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> 

제가 너무 차체 측 참 역 세트 그들은 Vehicle을 통해 모든 맵핑이 이루어지기를 희망하지만 100 % 확신 할 수는 없다.

+0

모든 차량에는 모든 영역의 목록이 있고 그 반대의 경우도 있습니다. 그 관계의 요점은 무엇입니까? 그것은 (#vehicles * #zones) 행을 가진 링크 테이블이 될 것입니다. – dotjoe

+0

이것은 스트레스 테스트로 이런 식으로 설계되었습니다. 생산되는 모든 차량에는 반드시 모든 구역이 있어야하는 것은 아닙니다. 그러나 차량과 구역에 대한 링크는 항상 양방향입니다. 나는 이것을 최악의 시나리오에서 강조하기위한 참고 자료로 사용했다. 나는 NHibernate가 많은 객체 컬렉션을 처리 할 수 ​​있는지보고 싶다. 한 번에 많은 항목을 저장하는 것은 거의 이러한 방식으로 수행되지는 않겠지 만 필요가 생기거나 NHibernate를 사용하는 요점이 없다면 사용할 수 있도록해야합니다. –

+1

오, 알다시피, 분명히 many-to-many의 한 면만 캐스케이드해야합니까? 매핑을 게시 할 수 있습니까? – dotjoe

답변

1

실제 데이터베이스 쿼리가 실행되는 곳이므로 tx.Commit()에서 질식합니다.

영역과 차량 사이에 양방향 다 대다 관계가 있고 양면에 계단식이있는 경우 NHibernate가 처리하고 모든 것을 올바르게 저장해야합니다. 그래서 귀하의 문제가 많은 수의 엔티티와 NHibernate가 전체 객체 그래프를 그리고 유지하려고 할 때 발생한다고 가정합니다. 첫 번째 객체의 저장시 전체 저장 작업이 계단식으로 수행되므로 플러시가 도움이되지 않습니다.

비어있는 차량 컬렉션이있는 모든 Zones을 먼저 삽입 한 다음 영역이 할당 된 모든 차량을 삽입하기 위해 저장 작업을 분할하거나이 전체 작업을 위해 NHibernate의 StatelessSession을 사용하는 것이 좋습니다. 상태없는 세션은 개체를 캐시에 보관하거나 캐스케이드를 수행하지 않으므로 일반적으로 사용자와 같은 일괄 업데이트에 더 적합합니다.

+0

무국적 세션을 통해 컬렉션을 저장할 수 있다고 생각하지 못했습니까? –

+1

아, 네 말이 맞아. 양방향 one-to-many에서 작동 할 수 있지만 many-to-many에서는 가능하지 않다. 따라서 수동 반복보다 다른 방법은 없습니다. – NOtherDev

+1

당신이 많은 사람들과 많은 사람들 사이에서 관계를 대표하는 실체를 가졌다면 나는 그것을 사용할 수 있다고 생각할 것입니다. 그것은 일종의 kludgy이지만 작동 할 수도 있습니다. –

1

차량에 점진적으로 추가하고 저장해보십시오. 예를 들어 다음과 같이하면됩니다.

Vehicle newVehicle = new Vehicle(); 

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

이 시나리오는 실제로 얼마나 자주 발생합니까? 새로운 차량을 만들고 점프 거리 바로 앞에서 9600 개의 구역을 추가하는 것이 일반적인 일입니까? 어떤 점에서 당신은 YAGNI에 대해 생각해야합니다.

+0

차량을 만들고 모든 지역을 추가하는 것은 비교적 자주 발생하는 접근 방법 일 수 있습니다. 영역을 만들고 모든 차량에 추가하는 경우에도 마찬가지입니다. 모든 영역이 아니라면 그 중 대부분을 존중하십시오.그러나 당신은 모든 단일 구역에 대한 모든 단일 차량에 대해서만 한 번 반복을하고 있습니다. 스트레스 테스트를 설정하기 위해해야 ​​할 일이며, 지금 나는이 문제를 가지고 있습니다. –

+0

save per transaction 접근 방식을 사용하는 것이 지금까지 도움이 될 것 같습니다. 나중에 성공적으로 완료되면 알 수 있습니다. 나는 수학을했고 그것은 2 개의리스트를 매핑하는 테이블에서 4 천 8 백만 행이 될 것입니다. 그것은 단일 트랜잭션에 대한 약간의 과잉이었고 왜 그것이 죽었는지 설명 할 것입니다. 즉, 저장이 이번에 완료되었는지를 알기 위해이 경우 4 천 8 백만 건의 레코드가 모두 삽입 될 때까지 기다려야합니다. 한편, 지금까지의 조언에 +1. –

+0

tx.Commit()에 대한 각각의 호출이 점차 길어지고 오랫동안 지속되는 이유는 무엇입니까? –

관련 문제