1

这整天困扰着我,而且看不到尽头。当我的 php 应用程序的用户添加了新的更新并且出现问题时,我需要能够撤消一批复杂的混合命令。它们可以是 mysql 更新和插入查询、文件删除和文件夹重命名和创建。

我可以跟踪所有插入命令的状态,并在抛出错误时撤消它们。但是我如何使用更新语句来做到这一点?是否有一种聪明的方法(某种设计模式?)来跟踪文件结构和数据库中的这种变化?

我的数据库表是 MyISAM。将所有内容都转换为 InnoDB 很容易,这样我就可以使用事务了。这样我只需要处理文件和文件夹操作。不幸的是,我不能假设所有客户端都支持 InnoDB。它还需要我将数据库中的许多表转换为 InnoDB,我对此犹豫不决。

4

3 回答 3

0

如果你完全被 MyISAM 卡住了,你应该看看代码是否可以安排,以便 UPDATES 是最后执行的事情。如果在此之前发生错误,则不会进行任何更新。

如果这不可行,您将不得不锁定相关表,获取当前记录,更新它们。如果出错,用抓取的记录恢复。解锁表。

不是很实用,这就是为什么有 InnoDB(如你所知)。

我认为这是该模块的基础,您可以查看:

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

于 2010-04-29T19:35:53.600 回答
0

PDO 的 rowcount() 在更新时返回受影响的行。mysqli 的 afftected_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

您是否研究过工作单元模式?

这是一个关于如何开始的非常粗略的示例。

基本的 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
}

现在,我没有做一些你想做的事情——比如跟踪抛出了哪些异常,以便客户端脚本可以访问该信息——但我想你明白了。当然,您可以继续为各种操作创建工作实体——数据库更新、API 调用等等。

在连接到没有事务安全表的数据库方面 - 我没有任何见解。

于 2010-04-29T19:51:23.357 回答