这一直困扰着我,看不到尽头。 当我的php应用程序的用户添加新的更新并出现问题时,我需要能够撤消一批复杂的混合命令。它们可以是mysql更新和插入查询,文件删除和文件夹重命名和创建。
我可以跟踪所有插入命令的状态,如果抛出错误则撤消它们。 但是如何使用update语句执行此操作? 是否有一种智能方式(一些设计模式?)来跟踪文件结构和数据库中的这些变化?
我的数据库表是MyISAM。将所有内容转换为InnoDB很容易,这样我就可以使用事务了。这样我只需要处理文件和文件夹操作。 不幸的是,我不能假设所有客户都支持InnoDB。它还需要我将我的数据库中的许多表转换为InnoDB,我对此犹豫不决。
答案 0 :(得分:0)
PDO的rowcount()返回更新后的eftected行。 mysqli的afftected_rows也是一样的
我是客户,你的意思是客户的服务器,你将放置这个应用程序。如果您不需要在服务器上使用innoDB,则必须进行更多编码以回滚MyISAM表上的更改。
最好的方法是将所有内容模块化为函数(或类方法)
伪代码:
function updateThisThing() {
if ( !updateTable() ) {
rollbackUpdateTable();
return false;
}
if ( !updateFiles() ) {
rollbackUpdateFiles();
return false;
}
// more update statements
return true
}
答案 1 :(得分:0)
如果你完全坚持使用MyISAM,你应该看看是否可以安排代码,以便UPDATES是最后执行的操作。如果在此之前发生错误,则不会进行任何更新。
如果这不可行,则必须锁定相关表,获取当前记录,更新它们。如果错误,请使用抓取的记录进行恢复。解锁表格。
不太实用,这就是为什么有InnoDB(如你所知)。
我认为这是您可以查看的模块的基础:
答案 2 :(得分: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
}
现在,我没有做你想要的一些事情 - 比如跟踪抛出哪些异常,以便客户端脚本可以访问该信息 - 但我认为你明白了。当然,您可以继续为各种操作制作工作实体 - 数据库更新,API调用等等。
在连接到没有事务安全表的数据库方面 - 我没有任何见解。