1

これは一日中私を悩ませており、終わりが見えません. 私の php アプリケーションのユーザーが新しい更新を追加し、何か問題が発生した場合、混合コマンドの複雑なバッチを元に戻すことができる必要があります。それらは、mysql の更新と挿入のクエリ、ファイルの削除、フォルダーの名前変更と作成です。

すべての挿入コマンドのステータスを追跡し、エラーが発生した場合は元に戻すことができます。しかし、更新ステートメントでこれを行うにはどうすればよいですか? ファイル構造とデータベースの両方でそのような変更を追跡するためのスマートな方法 (設計パターン?) はありますか?

私のデータベース テーブルは MyISAM です。トランザクションを使用できるように、すべてを InnoDB に変換するのは簡単です。そうすれば、ファイルとフォルダーの操作を処理するだけで済みます。残念ながら、すべてのクライアントが InnoDB をサポートしているとは限りません。また、データベース内の多くのテーブルを InnoDB に変換する必要がありますが、これはためらっています。

4

3 に答える 3

0

MyISAM に完全に行き詰まっている場合は、UPDATES が最後に実行されるようにコードを調整できるかどうかを確認する必要があります。それ以前にエラーが発生した場合、UPDATE は行われません。

それが不可能な場合は、関連するテーブルをロックし、現在のレコードを取得して更新する必要があります。エラーの場合は、取得したレコードで復元します。テーブルのロックを解除します。

あまり実用的ではないため、InnoDB が存在します (ご存じのとおり)。

それが、チェックアウトできるこのモジュールの基礎だと思います。

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

于 2010-04-29T19:35:53.600 に答える
0

PDO の rowcount() は、更新時に影響を受けた行を返します。mysqli の affected_rows は同じことを行います

クライアントとは、このアプリケーションを配置するサーバーのクライアントを意味します。サーバーで innoDB を必要としない場合は、MyISAM テーブルの変更をロールバックするために、さらにコーディングを行う必要があります。

最善の方法は、すべてを関数 (またはクラス メソッド) にモジュール化することです。

擬似コード:

function updateThisThing() {

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

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

    // more update statements

    return true

}
于 2010-04-29T19:25:03.983 に答える
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 呼び出しなど) の作業エンティティを作成することもできます。

トランザクションセーフテーブルのないデータベースへの接続に関しては、私には洞察がありません。

于 2010-04-29T19:51:23.357 に答える