本文目录导读:

我来全面讲解PHP项目中标识映射(Identity Map) 与工作单元(Unit of Work) 这两种企业级设计模式。
标识映射(Identity Map)
概念
确保每个数据库记录在内存中只有一个对象实例,避免同一记录被加载多次。
实现示例
<?php
class IdentityMap
{
private array $objects = [];
// 按类名和ID存储对象
public function set(string $class, $id, object $object): void
{
$key = $this->createKey($class, $id);
$this->objects[$key] = $object;
}
// 按类名和ID获取对象
public function get(string $class, $id): ?object
{
$key = $this->createKey($class, $id);
return $this->objects[$key] ?? null;
}
// 检查对象是否已加载
public function has(string $class, $id): bool
{
$key = $this->createKey($class, $id);
return isset($this->objects[$key]);
}
// 移除对象
public function remove(string $class, $id): void
{
$key = $this->createKey($class, $id);
unset($this->objects[$key]);
}
// 清空所有对象
public function clear(): void
{
$this->objects = [];
}
private function createKey(string $class, $id): string
{
return sprintf('%s:%s', $class, $id);
}
}
// 使用示例
class UserRepository
{
private IdentityMap $identityMap;
private PDO $pdo;
public function __construct(PDO $pdo, IdentityMap $identityMap)
{
$this->pdo = $pdo;
$this->identityMap = $identityMap;
}
public function findById(int $id): ?User
{
// 先从标识映射中查找
if ($this->identityMap->has(User::class, $id)) {
echo "从缓存返回\n";
return $this->identityMap->get(User::class, $id);
}
// 从数据库加载
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$data) {
return null;
}
// 创建对象并存入标识映射
$user = new User($data);
$this->identityMap->set(User::class, $id, $user);
echo "从数据库加载\n";
return $user;
}
}
工作单元(Unit of Work)
概念
维护所有被修改的对象列表,并在事务边界统一提交所有变更。
完整实现
<?php
// 对象状态枚举
enum ObjectState: string
{
case CLEAN = 'clean';
case DIRTY = 'dirty'; // 需要更新
case NEW = 'new'; // 需要插入
case REMOVED = 'removed'; // 需要删除
}
// 工作单元实现
class UnitOfWork
{
private IdentityMap $identityMap;
private array $objects = []; // key => {object, state, originalData}
private array $callbacks = [];
public function __construct(IdentityMap $identityMap)
{
$this->identityMap = $identityMap;
}
// 注册新对象
public function registerNew(object $object): void
{
$key = $this->getObjectKey($object);
$this->objects[$key] = [
'object' => $object,
'state' => ObjectState::NEW,
'originalData' => $this->getObjectData($object)
];
}
// 注册脏对象(已修改)
public function registerDirty(object $object): void
{
$key = $this->getObjectKey($object);
if (!isset($this->objects[$key])) {
$this->objects[$key] = [
'object' => $object,
'state' => ObjectState::DIRTY,
'originalData' => $this->getObjectData($object)
];
} elseif ($this->objects[$key]['state'] !== ObjectState::NEW) {
$this->objects[$key]['state'] = ObjectState::DIRTY;
}
}
// 注册删除对象
public function registerRemoved(object $object): void
{
$key = $this->getObjectKey($object);
if (isset($this->objects[$key]) && $this->objects[$key]['state'] === ObjectState::NEW) {
unset($this->objects[$key]); // 如果是新对象,直接移除
return;
}
$this->objects[$key] = [
'object' => $object,
'state' => ObjectState::REMOVED,
'originalData' => $this->getObjectData($object)
];
}
// 注册清理对象
public function registerClean(object $object): void
{
$key = $this->getObjectKey($object);
$this->objects[$key] = [
'object' => $object,
'state' => ObjectState::CLEAN,
'originalData' => $this->getObjectData($object)
];
}
// 提交所有变更
public function commit(Persister $persister): void
{
try {
$persister->beginTransaction();
// 按顺序处理:新增、更新、删除
foreach ($this->objects as $key => $info) {
switch ($info['state']) {
case ObjectState::NEW:
$persister->insert($info['object']);
break;
case ObjectState::DIRTY:
$persister->update($info['object']);
break;
case ObjectState::REMOVED:
$persister->delete($info['object']);
break;
}
}
$persister->commit();
// 提交后,所有对象变为clean状态
foreach ($this->objects as $key => $info) {
if ($info['state'] !== ObjectState::REMOVED) {
$this->registerClean($info['object']);
} else {
unset($this->objects[$key]);
}
}
// 执行回调
foreach ($this->callbacks as $callback) {
$callback();
}
} catch (\Exception $e) {
$persister->rollback();
throw $e;
}
}
// 注册提交后的回调
public function addCommitCallback(callable $callback): void
{
$this->callbacks[] = $callback;
}
private function getObjectKey(object $object): string
{
return spl_object_id($object);
}
private function getObjectData(object $object): array
{
// 实际项目中应该使用反射或序列化
return (array) $object;
}
}
// 持久化器接口
interface Persister
{
public function beginTransaction(): void;
public function commit(): void;
public function rollback(): void;
public function insert(object $object): void;
public function update(object $object): void;
public function delete(object $object): void;
}
// 具体持久化器实现
class DatabasePersister implements Persister
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function beginTransaction(): void
{
$this->pdo->beginTransaction();
}
public function commit(): void
{
$this->pdo->commit();
}
public function rollback(): void
{
$this->pdo->rollBack();
}
public function insert(object $object): void
{
if ($object instanceof User) {
$stmt = $this->pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute([$object->getName(), $object->getEmail()]);
$object->setId((int) $this->pdo->lastInsertId());
}
}
public function update(object $object): void
{
if ($object instanceof User) {
$stmt = $this->pdo->prepare('UPDATE users SET name = ?, email = ? WHERE id = ?');
$stmt->execute([$object->getName(), $object->getEmail(), $object->getId()]);
}
}
public function delete(object $object): void
{
if ($object instanceof User) {
$stmt = $this->pdo->prepare('DELETE FROM users WHERE id = ?');
$stmt->execute([$object->getId()]);
}
}
}
组合使用示例
<?php
// 完整的应用服务示例
class UserService
{
private UnitOfWork $unitOfWork;
private UserRepository $repository;
public function __construct(UnitOfWork $unitOfWork, UserRepository $repository)
{
$this->unitOfWork = $unitOfWork;
$this->repository = $repository;
}
// 创建用户
public function createUser(string $name, string $email): User
{
$user = new User(null, $name, $email);
// 注册到工作单元
$this->unitOfWork->registerNew($user);
// 提交事务
$this->unitOfWork->commit(new DatabasePersister(new PDO('...')));
return $user;
}
// 更新用户信息
public function updateUser(int $userId, string $name, string $email): void
{
// 从标识映射获取用户(会先检查缓存)
$user = $this->repository->findById($userId);
if (!$user) {
throw new \RuntimeException('User not found');
}
// 修改用户属性
$user->setName($name);
$user->setEmail($email);
// 标记为脏对象
$this->unitOfWork->registerDirty($user);
// 批量提交
$this->unitOfWork->commit(new DatabasePersister(new PDO('...')));
}
// 批量操作示例
public function batchUpdate(): void
{
$persister = new DatabasePersister(new PDO('...'));
// 多个修改
$user1 = $this->repository->findById(1);
$user1->setName('New Name 1');
$this->unitOfWork->registerDirty($user1);
$user2 = $this->repository->findById(2);
$this->unitOfWork->registerRemoved($user2);
$user3 = new User(null, 'New User', 'new@email.com');
$this->unitOfWork->registerNew($user3);
// 一次提交所有变更
$this->unitOfWork->commit($persister);
}
}
// 使用示例
try {
$identityMap = new IdentityMap();
$unitOfWork = new UnitOfWork($identityMap);
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$repository = new UserRepository($pdo, $identityMap);
$service = new UserService($unitOfWork, $repository);
// 批量操作
$service->batchUpdate();
echo "所有操作成功完成\n";
} catch (\Exception $e) {
echo "操作失败: " . $e->getMessage() . "\n";
}
高级特性
懒加载与标识映射结合
<?php
class LazyLoader
{
private \Closure $loader;
private bool $loaded = false;
private $value;
public function __construct(\Closure $loader)
{
$this->loader = $loader;
}
public function get()
{
if (!$this->loaded) {
$this->value = ($this->loader)();
$this->loaded = true;
}
return $this->value;
}
}
// 在标识映射中使用懒加载
class ProxyUser extends User
{
private LazyLoader $profileLoader;
public function __construct(int $id)
{
parent::__construct();
$this->setId($id);
// 配置懒加载
$this->profileLoader = new LazyLoader(function () {
// 从数据库加载额外数据
return $this->loadProfileFromDB();
});
}
public function getProfile()
{
return $this->profileLoader->get();
}
}
最佳实践
- 事务边界清晰:工作单元应位于单一事务边界内
- 合理的粒度:不要在一个工作单元中处理过多对象
- 缓存策略:标识映射应与缓存策略结合,考虑过期时间
- 并发控制:考虑乐观锁或悲观锁防止并发问题
- 测试:使用依赖注入方便单元测试
框架中的实现
许多PHP框架已经实现了这些模式:
- Doctrine ORM:完整的标识映射和工作单元实现
- Laravel Eloquent:部分实现了标识映射
- Yii2 ActiveRecord:有自己的工作单元实现
了解这些底层模式有助于更好地理解和使用ORM框架。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播