2012-06-16 3 views
5

squeryl에서 쿼리를 만들면 쿼리 [T] 개체를 반환합니다. 쿼리는 아직 실행되지 않았으며 쿼리 개체 (Query [T]가 Iterable [T]를 확장)를 반복 할 때 실행됩니다.Squeryl : 명시 적으로 쿼리 실행

쿼리 실행 주위에는 트랜잭션 {} 또는 inTransaction {} 블록이 있어야합니다.

나는 단지 SELECT 쿼리에 대해 말하고 트랜잭션은 필요하지 않지만 squeryl 프레임 워크에는 이러한 쿼리가 필요합니다.

내 응용 프로그램의 모델에서 쿼리를 만들고이를 템플릿에있는 뷰 도우미가 반복하고 데이터를 표시하는보기로 직접 전달하고 싶습니다. 이것은 컨트롤러에 트랜잭션 {} 블록을 넣을 때만 가능합니다 (컨트롤러는 템플릿 호출을 포함하므로 반복을 수행하는 템플릿도 내부에 있습니다). 모델이 실제로 쿼리를 실행하지 않기 때문에 트랜잭션 {} 블록을 모델에 넣을 수 없습니다.

그러나 제 이해에는 거래가 컨트롤러와 관련이 없습니다. 어떤 데이터베이스 프레임 워크를 사용할 것인지, 어떻게 사용할 것인지 그리고 어디에서 트랜잭션을 사용할 것인지 모델의 결정입니다. 그래서 트랜잭션 {} 블록이 모델에 있어야합니다.

Query [T] 인스턴스를 반환하는 대신이 Query [T] 객체에 Iterable [T] .toList를 호출 한 다음 생성 된 목록을 반환 할 수 있음을 알고 있습니다. 그러면 전체 쿼리가 모델에서 실행되고 모든 것이 정상입니다. 그러나 데이터베이스에서 요청한 모든 데이터가이 목록에 캐시되어야하므로이 방법이 마음에 들지 않습니다. 이 데이터가 뷰에 직접 전달되는 방식을 선호합니다. 결과가 컸을 때 결과를 스트리밍하는 MySql 기능을 좋아합니다.

가능성은 있습니까? 아마도 요청을 데이터베이스로 보내고, 트랜잭션을 닫을 수 있지만 여전히 MySQL 스트리밍 기능을 사용하고 나머지 (선택된 고정 된) 결과 집합을 수신하는 Query [T] .executeNow() 함수와 같은 것일 수 있습니다. 접근 했어? 결과 집합은 쿼리하는 순간에 고정되므로 트랜잭션을 닫는 것이 문제가되어서는 안됩니다. 나는 여기에서 볼

+0

흥미롭고 놀라운 것을 발견하면 솔루션을 게시하면 좋을 것입니다. –

답변

5

일반적인 문제는 다음과 같은 두 가지 아이디어를 결합하려고한다는 것입니다 :

  • 데이터의 게으른 계산; 여기에 : 데이터베이스 결과

  • 계산이 완료 될 때 트리거되어야하는 사후 처리 작업의 필요성이 숨겨져 있습니다. 여기 : 컨트롤러에서 숨어 또는 계산이 게으른이기 때문에 데이터베이스 세션이

를 폐쇄해야한다고보고 있기 때문에 여기에 맨 끝 (그것을 수행 할 의무가 없습니다 : 전체 결과 집합을 반복), 사후 처리 단계를 트리거 할 수있는 분명한 후크가 없습니다.

Query[T].toList을 호출하는 제안은 계산이 최후까지 수행되고 결과 집합의 마지막 요소를 요청하면 세션을 닫는 트리거로 사용할 수 있으므로이 문제가 발생하지 않습니다.다음과 같이

class IterableQuery[T](val q: Query[T]) extends Iterable[T] { 
    private var lifeCycleState: Int = 0 
    private var session: Session = null 
    private var prevSession: Option[Session] = None 

    def start() { 
    assert(lifeCycleState == 0, "Queries may not be restarted.") 
    lifeCycleState = 1 

    /* Create a new session for this query. */ 
    session = SessionFactory.newSession 

    /* Store and unbind a possibly existing session. */ 
    val prevSession = Session.currentSessionOption 
    if(prevSession != None) prevSession.get.unbindFromCurrentThread 

    /* Bind newly created session. */ 
    session.bindToCurrentThread 
    } 

    def iterator = { 
    assert(lifeCycleState == 1, "Query is not active.") 
    q.toStream.iterator 
    } 

    def stop() { 
    assert(lifeCycleState == 1, "Query is not active.") 
    lifeCycleState = 2 

    /* Unbind session and close it. */ 
    session.unbindFromCurrentThread 
    session.close 

    /* Re-bind previous session, if it existed. */ 
    if(prevSession != None) prevSession.get.bindToCurrentThread 
    } 
} 

클라이언트 쿼리 래퍼를 사용할 수 있습니다 :

내가 가지고 올 수있는 가장 org.squeryl.dsl.QueryDsl._using 내부 코드의 적응 다음이다, 말했다

var manualIt = new IterableQuery(booksQuery) 
manualIt.start() 
manualIt.foreach(println) 
manualIt.stop() 
//  manualIt.foreach(println) /* Fails, as expected */ 

manualIt = new IterableQuery(booksQuery) /* Queries can be reused */ 
manualIt.start() 
manualIt.foreach(b => println("Book: " + b)) 
manualIt.stop() 

manualIt.start()의 호출은 객체가 생성 될 때 (즉, 생성자 IterableQuery의 생성자 내에) 또는 객체가 컨트롤러에 전달되기 전에 이미 수행 될 수 있습니다.

그러나 이러한 경우에 리소스 (파일, 데이터베이스 연결 등)로 작업하는 것은 매우 어려울 수 있습니다. 예외 발생시 사후 처리가 트리거되지 않기 때문입니다. org.squeryl.dsl.QueryDsl._using의 구현을 보면 IterableQuery에서 누락 된 try ... finally 블록을 볼 수 있습니다.

관련 문제