2013-04-24 1 views
21

데이터베이스/sql 및 드라이버 패키지와 Tx를 사용하면 다른 트랜잭션을 시도하지 않고 트랜잭션이 커밋되거나 롤백되었는지 여부를 감지하는 것처럼 보이지 않습니다. 결과를 확인한 다음 오류를 검사하여 오류 유형을 판별하십시오. 나는 Tx 객체에서 커밋 여부를 결정할 수 있기를 원합니다. 물론, Tx를 사용하는 함수에서 다른 변수를 정의하고 설정할 수는 있지만 꽤 많은 수의 변수가 있으며 매번 2 번 (변수와 대입)입니다. 또한 필요한 경우 롤백을 수행하는 지연된 함수가 있으며 bool 변수를 전달해야합니다.

커밋 또는 롤백 후에 Tx 변수를 nil로 설정하면 받아 들일 수 있습니까? GC가 메모리를 복구합니까, 아니면 아니오입니까? 아니면 더 좋은 대안이 있습니까?데이터베이스/SQL Tx - 커밋 또는 롤백 감지

+1

내가 문제를 이해한다면 확실하지. 커밋 또는 롤백 중 하나를 사용하여 트랜잭션을 종료해야하므로 수행 한 작업을 알 수 있지만 추가 변수에서이 작업을 기억하고 싶지는 않습니다. Tx와 bool을 자신의 RememberingTx로 래핑 할 수 있습니다. 이렇게하면 행 수가 약간 줄어 듭니다. GC 질문에 관하여 : 당신이 nil 또는 not로 설정하면 그것은 중요하지 않습니다 : 메모리가 남아 있지 않으면 메모리는 회수 될 것입니다. 그래서 : 네,'var tx * Tx; 한조각; if cond {tx.Commit; tx = nil} else {tx.Rollback}; 한조각; tx == nil {커밋 된 경우} else {롤백 된} '하지만 추한 느낌. – Volker

+0

그게 무슨 일인가지만, Tx가 nil이 아니면 롤백을 수행하는 지연된 func이 있습니다. 트랜잭션이 커밋되면 Tx를 사용할 수 없기 때문에 Tx를 nil로 설정할 계획입니다. 그러나 롤백을 시도하고 오류 메시지를 테스트하는 것은 그리 좋지 않습니다. 문제는 AFAIK가 트랜잭션이 Tx에서 "완료"되었는지 테스트 할 수있는 방법이 없다는 것입니다. 왜 그런 식으로했는지, 아마도 퍼포먼스인지 모르겠습니다. –

답변

73

왜이 작업을 수행해야합니까? Begin()을 호출하는 함수는 Commit() 또는 Rollback()을 호출하고 적절한 오류를 반환해야합니다. 내가 커밋 또는 롤백해야하는지 여부를 확인 error을 확인하고 있습니다 방법

func (s Service) DoSomething() (err error) { 
    tx, err := s.db.Begin() 
    if err != nil { 
     return 
    } 
    defer func() { 
     if err != nil { 
      tx.Rollback() 
      return 
     } 
     err = tx.Commit() 
    }() 
    if _, err = tx.Exec(...); err != nil { 
     return 
    } 
    if _, err = tx.Exec(...); err != nil { 
     return 
    } 
    // ... 
    return 
} 

공지 사항 :

예를 들어,이 코드는 커밋 또는 롤백 오류가 반환 여부에 따라 않습니다. 그러나 위의 예제는 패닉을 처리하지 않습니다.

나는 모든 데이터베이스 루틴에서 커밋/롤백 로직을 사용하지 않기 때문에 대개 트랜잭션 처리기에서 래핑합니다. 이의 라인을 따라 뭔가 :이 저 대신이 작업을 수행 할 수 있습니다

func Transact(db *sql.DB, txFunc func(*sql.Tx) error) (err error) { 
    tx, err := db.Begin() 
    if err != nil { 
     return 
    } 
    defer func() { 
     if p := recover(); p != nil { 
      tx.Rollback() 
      panic(p) // re-throw panic after Rollback 
     } else if err != nil { 
      tx.Rollback() 
     } else { 
      err = tx.Commit() 
     } 
    }() 
    err = txFunc(tx) 
    return err 
} 

: 내 트랜잭션 내에서 아무것도 패닉 경우 자동으로 트랜잭션 처리기에 의해 처리 있다고

func (s Service) DoSomething() error { 
    return Transact(s.db, func (tx *sql.Tx) error { 
     if _, err := tx.Exec(...); err != nil { 
      return err 
     } 
     if _, err := tx.Exec(...); err != nil { 
      return err 
     } 
    }) 
} 

알 수 있습니다.

현실적인 구현에서는 Commit() 또는 Rollback()으로 원하지 않는 호출을 방지하기 위해 * sql.Tx 대신 인터페이스를 전달합니다.

여기 설명하는 간단한 코드 조각 얼마나 defer 작업 (인쇄 4가 아닌 5) :

package main 

func test() (i int) { 
    defer func() { 
     i = 4 
    }() 
    return 5 
} 

func main() { 
    println(test()) 
} 

http://play.golang.org/p/0OinYDWFlx

+0

좋은 답변입니다! 나는 두 번째 doSomething() 구현이 끝날 무렵에 "return nil"을 놓쳤다 고 생각한다. – splinter123

+0

루크, 언제 어떻게 평가 되나요? 문서에 따라 "오류"는 지연 호출에서 처음 선언 될 때 값을 가져야합니다.그래서 이것은 실제로 나에게 다소 혼란 스럽습니다. 연기에 사용 된 "오류"의 값이 바뀌었기 때문입니다. – mirage

+0

지연은 다음과 같이 연기하기 전에 선언됩니다. = (콜론은 같음). anon func이 캡처합니다. Defer는 값이 반환되기 직전에 호출됩니다. 그것은 그것을 설정할 수 있습니다. 패닉이 발생하면 오류가 복구되어 반환됩니다. 오류가 어떤 식 으로든 발생하면 롤백이 발생합니다. 마지막으로 커밋은 에러가없고 err (현재는 nil)이 에러로 인해 리턴 값을 커밋하도록 설정되면 발생합니다. – Luke

관련 문제