Laravel : DB :: transaction ()과 함께 try… catch 사용


85

우리는 모두 DB::transaction()다중 삽입 쿼리에 사용 합니다. 그 안에을 try...catch넣거나 포장해야합니까? 문제가 try...catch발생하면 트랜잭션이 자동으로 실패 할 때 를 포함해야 합니까?

try...catch트랜잭션 래핑 샘플 :

// try...catch
try {
    // Transaction
    $exception = DB::transaction(function() {

        // Do your SQL here

    });

    if(is_null($exception)) {
        return true;
    } else {
        throw new Exception;
    }

}
catch(Exception $e) {
    return false;
}

반대로 DB::transaction()try ... catch :

// Transaction
$exception = DB::transaction(function() {
    // try...catch
    try {

        // Do your SQL here

    }
    catch(Exception $e) {
        return $e;
    }

});

return is_null($exception) ? true : false;

또는 단순히 try ... catch없이 트랜잭션

// Transaction only
$exception = DB::transaction(function() {

    // Do your SQL here

});

return is_null($exception) ? true : false;

답변:


186

코드를 통해 트랜잭션을 수동으로 '종료'해야하는 경우 (예외를 통하거나 단순히 오류 상태를 확인하는 경우) 사용해서는 안되며 DB::transaction()대신 코드를 DB::beginTransactionDB::commit/로 래핑해야합니다 DB::rollback().

DB::beginTransaction();

try {
    DB::insert(...);
    DB::insert(...);
    DB::insert(...);

    DB::commit();
    // all good
} catch (\Exception $e) {
    DB::rollback();
    // something went wrong
}

트랜잭션 문서를 참조하십시오 .


다시 살펴 보니 이것이 제가 찾고 있던 답입니다. :)
enchance

@alexrussell-데이터베이스가 다른 것을 생성하지 \Exception않습니까? 이 일반적인 \Exception? 그렇다면 좋습니다!
Artur Mamedov

DB::beginTransaction()과 의 차이점은 무엇입니까 DB:transaction()?
Hamed Kamrava

2
간단한 질문 : 예외 후 롤백을 수행하지 않거나 예외를 포착하지 않으면 어떻게됩니까? 스크립트 종료 후 자동 롤백?
neoteknic

2
@HengSopheak이 질문은 Laravel 4 데이터베이스에 관한 것이기 때문에 내 대답이 더 이상 5.3에 맞지 않을 가능성이 있습니다. 올바른 커뮤니티 지원을 받으려면 Laravel 5.3 태그로 새로운 질문을하는 것이 좋습니다.
alexrussell

25

PHP7을 사용하는 경우 사용자 예외 및 치명적인 오류를 포착 하기 위해 Throwable in catch을 사용하십시오 .

예를 들면 :

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

코드가 PHP5와 호환되어야하는 경우 Exception및 사용하십시오 Throwable.

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Exception $e) {
    DB::rollback();
    throw $e;
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

DB :: beginTransaction ()도 \ Exception을 던질 수 있다는 사실은 어떻습니까? try / catch에 포함되어야합니까?
Michael Pawlowsky

4
트랜잭션이 시작되지 않은 경우 아무것도 롤백 할 필요가 없습니다. 또한 catch블록 에서 시작되지 않은 트랜잭션을 롤백하는 것은 좋지 않습니다 . 따라서 좋은 장소 DB::beginTransaction()try블록 앞 입니다.
mnv

12

당신이 깊은 내부를 보면 당신은 ,, 여기, 내가 laravel 5에 사용되는 내 예제 코드를 리버스도시켜 try..catch를 통해 거래를 포장 또는 수 DB:transaction()Illuminate\Database\Connection당신처럼 같은 수동 트랜잭션을 작성하는 것이 있습니다.

라 라벨 거래

public function transaction(Closure $callback)
    {
        $this->beginTransaction();

        try {
            $result = $callback($this);

            $this->commit();
        }

        catch (Exception $e) {
            $this->rollBack();

            throw $e;
        } catch (Throwable $e) {
            $this->rollBack();

            throw $e;
        }

        return $result;
    }

따라서 이와 같은 코드를 작성하고 플래시를 통해 양식으로 메시지를 다시 던지거나 다른 페이지로 리디렉션하는 것과 같은 예외를 처리 할 수 ​​있습니다. REMEMBER return inside Closure는 transaction ()에서 반환되므로 반환 redirect()->back()하면 즉시 리디렉션되지 않습니다. 트랜잭션을 처리하는 변수에서 반환 되었기 때문입니다.

랩 트랜잭션

$result = DB::transaction(function () use ($request, $message) {
   try{

      // execute query 1
      // execute query 2
      // ..

      return redirect(route('account.article'));

   } catch (\Exception $e) {
       return redirect()->back()->withErrors(['error' => $e->getMessage()]);
    }
 });

// redirect the page
return $result;

그런 다음 대안은 부울 변수를 던지고 트랜잭션 함수 외부에서 리디렉션을 처리하거나 트랜잭션이 실패한 이유를 검색해야하는 경우 $e->getMessage()내부 에서 가져올 수 있습니다.catch(Exception $e){...}


나는 시도-catch 블록없이 트랜잭션을 사용하고 너무 잘 작동
hamidreza의 samsami을

@hamidrezasamsami 예, 데이터베이스 자동 롤러 다시,하지만 언젠가 당신이 알아야 할 쿼리가 모두 성공 여부 .. 있습니다
Angga 아리 자야

7
"랩 트랜잭션"예제가 잘못되었습니다. 모든 예외가 트랜잭션 콜백 내에서 포착되기 때문에 쿼리 중 하나가 실패하더라도 항상 커밋됩니다. DB :: transaction 외부에 try / catch를 넣고 싶습니다.
redmallard

3

복잡한 try-catch 블록보다 더 간단한 구문을 사용하여 해결할 수 있다고 생각하기 때문에이 질문에 대한 답을 제공하기로 결정했습니다. Laravel 문서는이 주제에 대해 매우 간단합니다.

try-catch를 사용하는 대신 DB::transaction(){...}다음과 같은 래퍼를 사용할 수 있습니다 .

// MyController.php
public function store(Request $request) {
    return DB::transaction(function() use ($request) {
        $user = User::create([
            'username' => $request->post('username')
        ]);

        // Add some sort of "log" record for the sake of transaction:
        $log = Log::create([
            'message' => 'User Foobar created'
        ]);

        // Lets add some custom validation that will prohibit the transaction:
        if($user->id > 1) {
            throw AnyException('Please rollback this transaction');
        }

        return response()->json(['message' => 'User saved!']);
    });
};

그러면 사용자와 로그 레코드가 서로 없이는 존재할 수 없음을 확인해야합니다.

위의 구현에 대한 몇 가지 참고 사항 :

  • 콜백 내에서 반환을 return사용할 수 있도록 트랜잭션 을 확인하십시오 response().
  • throw트랜잭션을 롤백하려면 예외 를 확인하십시오 (또는 Eloquent 내의 SQL 예외와 같이 자동으로 예외를 발생시키는 중첩 함수가 있음).
  • id, updated_at, created_at및 기타 필드는 사용할 수 AFTER 생성됩니다 $user(이 트랜잭션 (transaction)의 기간) 객체입니다. 트랜잭션은 보유한 모든 생성 로직을 통해 실행됩니다. 그러나를 AnyException던지면 전체 레코드가 삭제됩니다 . 이것은 예를 들어 자동 증가 열 id이 실패한 트랜잭션에서 증가한다는 것을 의미 합니다.

Laravel 5.8에서 테스트 됨

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.