Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
7de94d7f
Commit
7de94d7f
authored
Mar 31, 2013
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented optimistic locking for AR.
parent
906e402b
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
80 additions
and
7 deletions
+80
-7
ActiveRecord.php
framework/db/ActiveRecord.php
+56
-7
StaleObjectException.php
framework/db/StaleObjectException.php
+24
-0
No files found.
framework/db/ActiveRecord.php
View file @
7de94d7f
...
@@ -191,15 +191,12 @@ class ActiveRecord extends Model
...
@@ -191,15 +191,12 @@ class ActiveRecord extends Model
*/
*/
public
static
function
updateAllCounters
(
$counters
,
$condition
=
''
,
$params
=
array
())
public
static
function
updateAllCounters
(
$counters
,
$condition
=
''
,
$params
=
array
())
{
{
$db
=
static
::
getDb
();
$n
=
0
;
$n
=
0
;
foreach
(
$counters
as
$name
=>
$value
)
{
foreach
(
$counters
as
$name
=>
$value
)
{
$quotedName
=
$db
->
quoteColumnName
(
$name
);
$counters
[
$name
]
=
new
Expression
(
"[[
$name
]]+:bp
{
$n
}
"
,
array
(
":bp
{
$n
}
"
=>
$value
));
$counters
[
$name
]
=
new
Expression
(
"
$quotedName
+:bp
{
$n
}
"
);
$params
[
":bp
{
$n
}
"
]
=
$value
;
$n
++
;
$n
++
;
}
}
$command
=
$db
->
createCommand
();
$command
=
static
::
getDb
()
->
createCommand
();
$command
->
update
(
static
::
tableName
(),
$counters
,
$condition
,
$params
);
$command
->
update
(
static
::
tableName
(),
$counters
,
$condition
,
$params
);
return
$command
->
execute
();
return
$command
->
execute
();
}
}
...
@@ -280,6 +277,34 @@ class ActiveRecord extends Model
...
@@ -280,6 +277,34 @@ class ActiveRecord extends Model
}
}
/**
/**
* Returns the column name that stores the lock version of a table row.
*
* This is used to implement optimistic locking. Optimistic locking allows multiple users
* to access the same record for edits. In case when a user attempts to save the record upon
* some staled data (because another user has modified the data), a [[StaleObjectException]]
* will be thrown, and the update is ignored.
*
* Optimized locking is only supported by [[update()]] and [[delete()]].
*
* To use optimized locking:
*
* 1. create a column to store the lock version. The column type should be integer (or bigint)
* and default to 0. Override this method to return the name of this column.
* 2. In the Web form that collects the user input, add a hidden field that stores
* the lock version of the recording being updated.
* 3. In the controller action that does the data updating, try to catch the [[StaleObjectException]]
* and implement necessary business logic (e.g. merging the changes, prompting stated data)
* to resolve the conflict.
*
* @return string the column name that stores the lock version of a table row.
* If null is returned (default implemented), optimistic locking will not be supported.
*/
public
static
function
lockVersion
()
{
return
null
;
}
/**
* PHP getter magic method.
* PHP getter magic method.
* This method is overridden so that attributes and related objects can be accessed like properties.
* This method is overridden so that attributes and related objects can be accessed like properties.
* @param string $name property name
* @param string $name property name
...
@@ -714,6 +739,8 @@ class ActiveRecord extends Model
...
@@ -714,6 +739,8 @@ class ActiveRecord extends Model
* meaning all attributes that are loaded from DB will be saved.
* meaning all attributes that are loaded from DB will be saved.
* @return integer|boolean the number of rows affected, or false if validation fails
* @return integer|boolean the number of rows affected, or false if validation fails
* or [[beforeSave()]] stops the updating process.
* or [[beforeSave()]] stops the updating process.
* @throws StaleObjectException if [[lockVersion|optimistic locking]] is enabled and the data
* being updated is outdated.
*/
*/
public
function
update
(
$runValidation
=
true
,
$attributes
=
null
)
public
function
update
(
$runValidation
=
true
,
$attributes
=
null
)
{
{
...
@@ -723,12 +750,24 @@ class ActiveRecord extends Model
...
@@ -723,12 +750,24 @@ class ActiveRecord extends Model
if
(
$this
->
beforeSave
(
false
))
{
if
(
$this
->
beforeSave
(
false
))
{
$values
=
$this
->
getDirtyAttributes
(
$attributes
);
$values
=
$this
->
getDirtyAttributes
(
$attributes
);
if
(
$values
!==
array
())
{
if
(
$values
!==
array
())
{
$condition
=
$this
->
getOldPrimaryKey
(
true
);
$lock
=
$this
->
lockVersion
();
if
(
$lock
!==
null
)
{
$values
[
$lock
]
=
$this
->
$lock
+
1
;
$condition
[
$lock
]
=
new
Expression
(
"[[
$lock
]]+1"
);
}
// We do not check the return value of updateAll() because it's possible
// We do not check the return value of updateAll() because it's possible
// that the UPDATE statement doesn't change anything and thus returns 0.
// that the UPDATE statement doesn't change anything and thus returns 0.
$rows
=
$this
->
updateAll
(
$values
,
$this
->
getOldPrimaryKey
(
true
));
$rows
=
$this
->
updateAll
(
$values
,
$condition
);
if
(
$lock
!==
null
&&
!
$rows
)
{
throw
new
StaleObjectException
(
'The object being updated is outdated.'
);
}
foreach
(
$values
as
$name
=>
$value
)
{
foreach
(
$values
as
$name
=>
$value
)
{
$this
->
_oldAttributes
[
$name
]
=
$this
->
_attributes
[
$name
];
$this
->
_oldAttributes
[
$name
]
=
$this
->
_attributes
[
$name
];
}
}
$this
->
afterSave
(
false
);
$this
->
afterSave
(
false
);
return
$rows
;
return
$rows
;
}
else
{
}
else
{
...
@@ -784,13 +823,23 @@ class ActiveRecord extends Model
...
@@ -784,13 +823,23 @@ class ActiveRecord extends Model
*
*
* @return integer|boolean the number of rows deleted, or false if the deletion is unsuccessful for some reason.
* @return integer|boolean the number of rows deleted, or false if the deletion is unsuccessful for some reason.
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
* Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
* @throws StaleObjectException if [[lockVersion|optimistic locking]] is enabled and the data
* being deleted is outdated.
*/
*/
public
function
delete
()
public
function
delete
()
{
{
if
(
$this
->
beforeDelete
())
{
if
(
$this
->
beforeDelete
())
{
// we do not check the return value of deleteAll() because it's possible
// we do not check the return value of deleteAll() because it's possible
// the record is already deleted in the database and thus the method will return 0
// the record is already deleted in the database and thus the method will return 0
$rows
=
$this
->
deleteAll
(
$this
->
getPrimaryKey
(
true
));
$condition
=
$this
->
getOldPrimaryKey
(
true
);
$lock
=
$this
->
lockVersion
();
if
(
$lock
!==
null
)
{
$condition
[
$lock
]
=
$this
->
$lock
;
}
$rows
=
$this
->
deleteAll
(
$condition
);
if
(
$lock
!==
null
&&
!
$rows
)
{
throw
new
StaleObjectException
(
'The object being deleted is outdated.'
);
}
$this
->
_oldAttributes
=
null
;
$this
->
_oldAttributes
=
null
;
$this
->
afterDelete
();
$this
->
afterDelete
();
return
$rows
;
return
$rows
;
...
...
framework/db/StaleObjectException.php
0 → 100644
View file @
7de94d7f
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\db
;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class
StaleObjectException
extends
Exception
{
/**
* @return string the user-friendly name of this exception
*/
public
function
getName
()
{
return
\Yii
::
t
(
'yii|Stale Object Exception'
);
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment