2009-10-26 5 views
2

NHibernate에서 수식을 사용하여 계산 된 속성을 정의 할 때 쿼리 제한에 따라 수식의 결과가 달라지는 경우의 의미는 무엇입니까? 특히 쿼리 캐싱과 관련하여?NHibernate 캐싱에 대한 영향 계산 결과 값이 공식 (예 : rank)으로 매핑 된 결과 포함

public class Entity 
{ 
    public Entity() { } 
    public virtual int Id { get; protected set; } 
    public virtual string Key { get; protected set; } 
    public virtual string Value { get; protected set; } 
    public virtual int Rank { get; protected set; } 
} 

다음과 같은 간단한 NHibernate에 매핑과 매핑 :

은보다 구체적으로, 다음과 같은 간단한 C# 클래스 고려

<class name="Entity" mutable="false"> 
    <id name="Id"> 
     <generator class="native"> 
    </id> 
    <property name="Key"/> 
    <property name="Value"/> 
    <property name="Rank" formula="row_number() over(order by value)"> 
</class> 

true로 설정 hibernate.cache.use_query_cache 옵션으로 세션 팩토리와 실행을하고, 쿼리 다음과 같은 방식으로 이루어집니다.

ICriteria criteria = session.CreateCriteria(typeof(Entity)); 
criteria.SetCacheable(true); 
criteria.SetCacheRegion("SearchResults"); 
IList<Entity> queryResult1 = criteria.List<Entity>(); 

criteria = session.CreateCriteria(typeof(Entity)); 
criteria.SetCacheable(true); 
criteria.SetCacheRegion("SearchResults"); 
criteria.Add(Restrictions.Like("Key", "a", MatchMode.Anywhere)); 
IList<Entity> queryResult2 = criteria.List<Entity>(); 

Entity directResult = session.Load<Entity>(id); 

NHibernate는 반환 된 엔티티에 대해 합리적인 방식으로 동작합니까? 또는 한 캐시 된 쿼리의 "순위"값이 쿼리 캐시로 인해 다른 쿼리의 순위 값을 오염시킬 수 있습니까? NHibernate 매핑에서 그러한 수식을 사용할 때 다른 문제가 있습니까?

편집 :

그것은 또한 나의 특별한 경우에, "엔티티"는 일류 비즈니스 엔티티 아님을 주목할 필요가 있지만, 일종의 메타 개체 수 있습니다. 다른 일류 엔터티보다 인덱싱 된 데이터베이스 뷰에 매핑되며 검색 전용으로 사용됩니다 (session.Load (id) 호출은 인위적으로 이루어 지므로 실제로 실제로는 발생하지 않아야 함).

그리고 이 캐싱에 미치는 영향은 의심 스러울지라도 잠재적 인 문제를 피하기 위해 비슷한 사용 사례에 대해 어떤 대안이있을 수 있습니까?

+0

질문에 대한 답변이 아니지만 NHibernate.Lucene을보고 싶을 수도 있습니다. 구현하기가 훨씬 쉬우 며 시도하는 방식으로 검색하는 데 훨씬 빠른 실행 시간을 제공합니다. – Paco

+0

이 예제는 단지 설명의 목적이었습니다. 우리는 실제로 SQL Server의 전체 텍스트 검색을 사용하고 있으며 기능 또는 확장 성 제한이있는 경우 Lucene이 목록의 최상위에 있습니다. – iammichael

+0

Rank 속성은 실제로 엔티티의 지속 값이 아닙니다. 실행 된 특정 쿼리에서 엔티티가 반환 된 결과입니다. IMHO 귀하의 도메인 클래스에 그것을 가지고있는 것이 옳지 않습니다. 또한 단일 세션 내에서 서로 "간섭"하거나 쿼리를 놓으면 서로를 "오염"시킬 수도 있습니다. –

답변

2

추가 실험 후 : 예, 일치하지 않는 결과를 초래할 수있는 캐시 관련 사항이 있습니다. NHibernate는 수식이 동일한 식별자를 가진 엔티티 결과에 대한 쿼리간에 값을 변경할 수 있다는 것을 자동으로 알 수 없습니다.

질문에 클래스 매핑을 사용하면 나머지 엔티티 데이터와 함께 순위가 저장됩니다. 이렇게하면 후속 쿼리가 실행되는 쿼리가 아닌 다른 쿼리에서 순위 값을 반환하게되어 결국 예상대로 순차적이지 않은 순위를 가질 수 있습니다.

NHibernate는 별도의 query과 엔티티 캐시를 가지고있다. 실제로 엔티티 캐시는 두 개의 엔티티 캐시 (session cachesecond level entity cache)가 있으며 영향은 어떤 것이 사용되는지에 달려있다.

동일한 세션 내에서 서로 다른 순위의 결과를 공유하는 두 개의 서로 다른 쿼리를 사용하면 쿼리 캐시를 사용할 수 없으면 잘못된 순위 값을받을 수 있습니다. 이 경우 동일한 세션의 두 x 째 조회는 첫 x 째 조회의 세션에 이미있는 엔티티 데이터를 대체하지 않으므로 (해당 작업 단위에 대해 변경되었을 수 있기 때.에) 리턴 된 순위 값은 첫 x ​​째 조회와 동일합니다 두 번째 쿼리의 실제 순위보다는 오히려 첫 번째 쿼리 의 결과를 축출하는 것은이 문제를 방지해야한다 (그러나 권장되는 해결책이 아니다; 아래 참조)

때 쿼리 캐시 같은 쿼리 후 반복 할 때, 잘못된 순위 값도 수신 할 수 있습니다 사용할 수입니다 결과가 다른 순위의 다른 쿼리가 실행되었습니다.이 경우 첫 번째 쿼리 실행은 결과 식별자를 쿼리 캐시와 엔터티 (순위가있는)에 엔터티 캐시에 추가합니다. 인터리브 된 쿼리 (다른 세션에서 실행될 때)는 엔터티 캐시에 엔터티와 함께 ​​저장된 순위 값을 변경할 수 있습니다. 첫 번째 쿼리가 다시 실행되면 캐시 된 식별자가 캐시 된 엔터티를 조회하는 데 사용됩니다 (변경된 순위로).


엔티티의 지속 값 (즉, 순위 제외) 만 포함하도록 엔티티를 변경하면이 문제를 완전히 해결할 수 있습니다. 그리고, 검색어에 대한 상기 식별자 및 검색어에 대한 순위 추출 돌출부를 사용 등급이 값 유형이기 때문에,이 경우

ICriteria criteria = session.CreateCriteria(typeof(Entity)); 
criteria.SetCacheable(true); 
criteria.SetCacheRegion("SearchResults"); 
criteria.SetProjection 
    (Projections.Id(), 
    Projections.SqlProjection("row_number() over(order by value) as Rank", 
           new[] { "Rank" }, 
           new[] { NHibernateUtil.Int32 })); 

를 쿼리 캐시 측 질의 따라 순위를 저장할 그 결과에 대한 쿼리의 결과 식별자. 그런 다음 두 번째 쿼리를 사용하여 투영 된 식별자를 사용하여 엔터티 값을 조회합니다. 까다로운 부분은 엔터티 쿼리를 수행 할 때 N+1 형식 문제를 피하기 위해서이고 Entity과 그 쿼리와 관련된 순위를 가진 다른 데이터 구조를 만들어야합니다.

단일 쿼리가 아닌 두 개의 별도 쿼리를 사용해야하는 것은 다소 귀찮은 일이지만 캐시를 적절한 방식으로 사용하는 유일한 방법 인 것 같습니다.