2009-09-25 4 views
0

NHibernate의 고유 한 제약 조건에 문제가 발생했습니다.NHibernate unique constraints

나는 사용자 이름 속성에 대한 고유 제한 조건으로 매핑되는 사용자 엔터티가 있습니다. 내가하고 싶은 일은 새로운 사용자가 추가되기 전에 특정 사용자 이름이 존재하는지 확인하고 기존 사용자가 사용자 이름을 업데이트하기 전에 확인할 수 있습니다.

첫 번째 시나리오 (새 사용자 추가)는 정상적으로 작동합니다. 그러나 기존 사용자를 업데이트하기 전에 사용자 이름이 존재하는지 확인하려고하면 제약 조건 위반이 발생합니다. 내 Save 메소드의 코드는 다음과 같습니다.

public void Save<T>(T entity) where T : User 
    { 
     using (var session = GetSession()) 
     using (var transaction = session.BeginTransaction()) 
     { 
      try 
      { 
       CheckIfUsernameExists(entity); 

       session.SaveOrUpdate(entity); 
       session.Flush(); 
       transaction.Commit(); 
      } 
      catch (HibernateException) 
      { 
       transaction.Rollback(); 
       throw; 
      } 
     } 
    } 

제약이 CheckIfUsernameExists() 메소드에 위반하고이처럼 보이는 :

public void CheckIfUsernameExists<T>(T entity) where T : User 
    { 
     var user = GetUserByUsername(entity); 
     if (user != null) 
      throw new UsernameExistsException(); 
    } 

    private T GetUserByUsername<T>(T entity) where T : User 
    { 
     var username = entity.Username; 
     var idToExclude = entity.Id; 

     var session = GetSession(); 

     var user = session.CreateCriteria<T>() 
      .Add(Restrictions.Eq("Username", username)) 
      .Add(Restrictions.Not(Restrictions.IdEq(idToExclude))) 
      .UniqueResult() as T; 

     return user; 
    } 

그것은 그것이 NHibernateException의 결과로 충돌의 원인이되는 session.CreateCriteria() 라인 (SQLiteException) "제약 조건 위반으로 인해 중단되었습니다. 열 사용자 이름이 고유하지 않습니다"라는 메시지가 표시됩니다.

NHibernate 현금과 관련이 있습니까? save 메서드에 전달 된 엔터티는 session.CreateCriteria()가 호출 될 때 원하는 사용자 이름으로 업데이트되었습니다.

어쩌면 나는이 모든 잘못을하고있다. (나는 NHibernate 초보자 다.) 어쩌면 명백한 것을 제시하고 대안을 제안 할 수있다.

도움을 주시면 감사하겠습니다.

답변

0

흠, 문제의 핵심은 확실하지 않지만 사용자가 이미 존재하는지 확인하려는 전략에 대해 왜 ".UniqueResult()"가 필요합니까?

그 사용자 이름과 일치하고 현재 사용자와 동일한 ID를 갖고 있지 않은 사용자 목록을 가져 오는 것으로 가정 할 수는 없습니다 (분명히). 의사 코드 나는이

public bool ExistsUsername(string username, int idToExclude) 
{ 
    IList<User> usersFound = someNHibernateCriteria excluding entries that have id = idToExclude 

    return (usersFound.Count > 0) 
} 
+0

, 내가하지 않습니다. 내가 제안한 코드를 변경했지만 그 결과는 같습니다. 의견을 보내 주셔서 감사합니다! –

+1

ok ...DB의 "username"col에 고유 한 제한 조건을 두었습니다. 또한 나는 당신이 "저장 메소드에 전달 된 엔티티가 세션 시간에 원하는 사용자 이름으로 업데이트되었습니다."라고 읽었습니다. 나는 그렇게하지 않을 것입니다. ExistsUsername을 호출하기 전에 "idToAvoid"를 피할 수도 있습니다. – Juri

+0

예, username에는 db에 제약 조건이 있습니다. 엔터티를 업데이트하기 전에 ExistsUsername을 호출하는 것에 관해서; 그것은 가능성일지도 모르지만 나는 더 조사 할 필요가있을 것이다. 감사. –

0

두 생각처럼 뭔가를 할 것 같은 : 를 - 왜 당신 만 될 saveOrUpdate을하고 당신이 성공하는 경우를 참조하십시오. 그것은 당신의 시나리오에서 가능하지 않습니까? - SQLite에 대해 언급 한 적이 있습니다. 그것은 당신의 실제 생산 시스템이거나 테스트를 위해 사용하는 것입니까? 그렇다면, SQLite가 문제를 일으키는 지 확인해보고 쿼리가 완벽하게 기능하는 DBMS에 대해 작동합니까? - 모든 종류의 제약 조건을 지원하지 않기 때문에 SQLite는 자주 이런 종류의 문제를 만듭니다 ...

+0

토마스, SaveOrUpdate를 호출 할 수 있기를 바랬지 만 어떤 유형의 예외가 발생했는지 확인할 수 없기 때문에 사용자에게 무엇이 잘못되었는지 알릴 방법이 없습니다. 그래서 내 자신의 예외 (UsernameExistsException)를 던지고 싶었습니다. 아니면 알지 못하는 NHibernate 예외를 더 많이 얻을 수있는 방법이 있습니까? SQLite 관련; 예, 우리는 로컬 개발자 팀과 통합 테스트를 위해 SQLite를 사용합니다. 현재 프로덕션 환경을 설정하지 않았습니다. 나는 이것을 염두에 두겠다. 그러나 SQLite가이 간단한 시나리오를 처리하지 못할 것 같아 보인다. –

+0

NH 맵핑에서 hbm2ddl을 통해 SQLite 인스턴스를 생성합니까? 그렇다면 그것은 문제입니다. SQLite는 'ALTER TABLE'문을 지원하지 않습니다. AFAIK는 자동으로 무시됩니다. 프로덕션 환경에서 다른 DBMS를 타켓팅 할 계획이라면 SQLite를 통합 테스트에 사용할 수 없습니다. 완전한 대체는 아닙니다. 빠르고 쉽지만 잘못된 결과를 줄 수 있습니다. 전체 DBMS에 대해이 코드를 테스트하는 데 30 분이 걸릴 것입니다. 테스트가 통과 할 것이라 확신합니다. –

+0

@ 토마스 웰러 : 그렇지 않을까 두렵습니다. db에 테이블을 놓고 스키마 내보내기를 사용하여 새 테이블을 추가합니다. 통합 테스트를위한 SQLite와 관련하여, 귀하의 성명서는 제가 많은 사람들로부터 들었던 것과 정확히 반대입니다. 통합 테스트에 사용할 수없는 이유는 무엇입니까? 의견을 보내 주셔서 감사합니다! –

0

CreateCriteria에서 예외가 발생 했습니까? 왜냐하면 select 문에서 SQLlite 제약 조건 예외를 얻을 수있는 방법을 알지 못하기 때문입니다. 나는

public bool NameAlreadyExists(string name, int? exclude_id) 
{ 
    ICriteria crit = session.CreateCriteria<User>() 
     .SetProjection(Projections.Constant(1)) 
     .Add(Restrictions.Eq(Projections.Property("name"), name)); 

    if (exclude_id.HasValue) 
     crit.Add(Restrictions.Not(Restrictions.IdEq(exclude_id.Value))); 

    return crit.List().Count > 0; 
} 

내가 원인을 알기 위해 생성 된 SQL의 순서에 보일 것이다 ... 거의 같은 일을한다. 해당 엔티티가 해당 세션에서로드 된 경우 쿼리 전에 업데이트 될 수 있습니다.