2010-04-29 3 views
1

이것은 하루 종일 괴롭혀 왔으며 끝이 없습니다. 내 PHP 응용 프로그램 사용자가 새 업데이트를 추가하고 문제가 발생하면 복잡한 명령의 복잡한 배치를 실행 취소 할 수 있어야합니다. 그것들은 mysql 업데이트 및 삽입 쿼리, 파일 삭제 및 폴더 이름 변경 및 생성 일 수 있습니다.파일 이동, 폴더 삭제 및 mysql 쿼리 롤백

모든 삽입 명령의 상태를 추적하고 오류가 발생하면 명령을 실행 취소 할 수 있습니다. 그러나 업데이트 문과 함께이 작업을 수행하려면 어떻게해야합니까? 파일 구조와 데이터베이스에서 이러한 변경 사항을 추적하는 스마트 방법 (일부 디자인 패턴?)이 있습니까?

내 데이터베이스 테이블은 MyISAM입니다. 모든 것을 InnoDB로 쉽게 변환 할 수 있으므로 트랜잭션을 사용할 수 있습니다. 그렇게하면 파일 및 폴더 작업 만 처리하면됩니다. 불행히도 모든 클라이언트가 InnoDB를 지원한다고 가정 할 수는 없습니다. 또한 필자는 데이터베이스의 많은 테이블을 InnoDB로 변환해야한다.

+1

당신이 묘사하는 것은 정확히 어떤 거래를위한 것입니까? "고객 지원"이 의미하는 바를 이해하지 못합니까? –

+0

내 소프트웨어 사용자가 서버에서 InnoDB를 사용할 수 있는지 여부를 모르겠다. 나는 그것이 꺼져 있다고 가정 할 필요가있다. – Workoholic

답변

0

PDO의 rowcount()는 업데이트시 유효 행을 반환합니다. mysqli의 afftected_rows는 똑같습니다.

나는 클라이언트를 통해이 어플리케이션을 배치 할 서버의 클라이언트를 의미합니다. 서버에 innoDB를 요구하지 않는다면 MyISAM 테이블의 변경 사항을 롤백하기 위해 코딩을 더해야 할 것이다.

가장 좋은 방법은 함수 (또는 클래스 메소드)에 모든 것을 모듈화하는 것

의사 코드 : 당신의 MyISAM 절대적으로 붙어 경우 코드가 될 수 있다면

function updateThisThing() { 

    if (!updateTable()) { 
     rollbackUpdateTable(); 
     return false; 
    } 

    if (!updateFiles()) { 
     rollbackUpdateFiles(); 
     return false; 
    } 

    // more update statements 

    return true 

} 
0

, 당신은 볼 수 UPDATES가 수행 된 마지막 일이되도록 정렬되어 있습니다. 그 전에 오류가 발생하면 UPDATE가 수행되지 않습니다.

가능하지 않은 경우 관련 테이블을 잠그고 현재 레코드를 가져 와서 업데이트해야합니다. 오류가있는 경우 잡은 레코드로 복원하십시오. 테이블 잠금 해제.

실용적이지 않으므로 InnoDB가 있습니다 (아시다시피).

http://www.deepbluesky.com/blog/-/myisam-transactions_20/

+0

예, 전에 링크를 발견했습니다. 하지만 별칭은 지원하지 않습니다. 일부 쿼리는 이전 쿼리의 mysql 삽입 ID에 의존합니다. 그래서 나는 그들을 움직이는데 한계가있다. – Workoholic

+0

나는 본다. 동일한 잠금, 보류, 업데이트, 복원 방법이 적용됩니다. 잠금은 다른 사람들이이 기간 동안 읽지 않도록해야합니다. 속도를 늦추지 만 사용자 쓰기가 빈번하지 않으면 빠져 나갈 수 있습니다. – webbiedave

0

당신이 Unit of Work 패턴으로 봤어 :

는 나는 당신이 체크 아웃 할 수있다이 모듈의 기준으로 생각?

다음은 실제로 시작하는 방법에 대한 근본적인 예입니다.

기본 UnitOfWork 컨테이너입니다.

class UnitOfWork 
{ 
    protected $entities = array(); 
    protected $completed = array(); 

    final public function addEntity(IWorkUnitEntity $entity) 
    { 
    $this->entities[] = $entity; 
    } 

    final public function execute() 
    { 
    try { 
     foreach ($this->entities as $entity) 
     { 
     $entity->execute(); 
     $completed[] = $entity; 
     } 
    } 
    catch (UnitOfWorkRollbackException $e) 
    { 
     $this->rollbackCompleted(); 
    } 

    return $this->commitAll(); 
    } 

    protected function rollbackCompleted() 
    { 
    while ($entity = array_pop($this->completed)) 
    { 
     $entity->rollback(); 
    } 
    } 

    protected function commitAll() 
    { 
    try { 
     foreach ($this->entities as $entity) 
     { 
     $entity->commit(); 
     } 
    } 
    catch (UnitOfWorkRollbackException $e) 
    { 
     $this->rollbackCompleted(); 
     return false; 
    } 
    return true; 
    } 
} 

몇 엑스트라는 모두 함께 퍼팅 지금

class UnitOfWorkRollbackException extends Exception {}; 

interface IWorkUnitEntity 
{ 
    public function execute(); 
    public function rollback(); 
} 

함께 작업 개체

class FileMoverEntity implements IWorkUnitEntity 
{ 
    protected 
     $source 
    , $destination 
    , $newName 
    ; 

    public function __construct($source, $destination, $newName = null) 
    { 
    $this->source = $source; 
    $this->destination = dirname($destination); 
    $this->newName = $newName; 
    } 

    public function execute() 
    { 
    if (is_readable($this->source) && is_writable($this->destination)) 
    { 
     return true; 
    } 
    throw new UnitOfWorkRollbackException('File cannot be moved'); 
    } 

    public function commit() 
    { 
    $filename = (null === $this->newName) 
     ? basename($this->source) 
     : $this->newName 
    ; 
    if (!rename($this->source, $this->destination . DIRECTORY_SEPARATOR . $filename)) 
    { 
     throw new UnitOfWorkRollbackException('File move failed'); 
    } 
    } 

    public function rollback() 
    { 
    // Nothing to do here since the file doesn't actually move until commit() 
    } 
} 

의 예를이를 수 있도록 도와줍니다.클라이언트 스크립트가 해당 정보에 액세스 할 수 있도록 예외가 발생 된 추적을 유지하는 등의 - -

$UoW = new UnitOfWork(); 

$UoW->addEntity(new FileMoverEntity('/tmp/foo', '/home/me', 'profile.jpg')); 
$UoW->addEntity(new FileMoverEntity('/tmp/bar', '/root', 'profile.jpg')); 

if ($UoW->execute()) 
{ 
    // all operations successful 
} 

는 지금, 나는 여기에해야하는 몇 가지 일을하지 않았다하지만 난 당신이 아이디어를 얻을 생각합니다. 물론 모든 작업 (DB 업데이트, API 호출 등)을위한 작업 항목을 만들 수 있습니다.

트랜잭션 안전 테이블이없는 데이터베이스에 연결하는 관점에서 통찰력은 없습니다.

+0

고맙습니다. 매우 흥미로 웠습니다. 조사해 봐야 할 것입니다. 문제 : 일부 쿼리는 이전 쿼리에서 생성 된 삽입 ID를 사용합니다. 이것이이 패턴으로 가능한지 확실하지 않습니다. – Workoholic

+0

execute(), commit(), rollback() 구조로 작업하는 방법을 알아낼 수있는 것은 무엇이든 작동합니다. 문제는 원시 트랜잭션을 지원하지 않는 RDBMS에 대한 연결 안전 롤백을 수행하는 방법을 해결하는 것입니다. 다시 한 번 통찰력이 없습니다. –