Skip to content
free2one edited this page Jul 13, 2023 · 1 revision

本章节翻译自Working with Objects - Doctrine Object Relational Mapper (ORM) (doctrine-project.org),并对部分内容进行了调整。

在本章中,我们将帮助你理解EntityManager 和UnitOfWork(工作单元)。一个工作单元类似于对象级别的事务。在初始化EntityManager或调用EntityManager#flush()之后,将隐式启动一个新的工作单元。调用 EntityManager#flush()将会提交一个工作单元(同时开启一个新的工作单元)。 一个工作单元可以通过EntityManager#close()来进行手动关闭。关闭操作会清除工作单元内所有尚未持久化的对象。

理解只有EntityManager#flush()会触发数据库持久化操作是非常重要的。任何其他方法,如EntityManager#persist($entity)EntityManager#remove($entity)只会通知UnitOfWork在刷新期间执行这些操作。

Doctrine 永远不会调用实体类中的公共方法(如 getter 和 setter)或构造函数。相反,它使用反射来获取或设置实体对象。

下图展示了EntityManager 及UnitOfWork在整个持久化过程中的变化。

image

UnitOfWork内部除了时除了实体变更集合外,还维护了其他一些中间数据,图中并未一一进行展示。

实体和标识映射

实体是具有唯一标识的对象。他们的标识在您的领域内具有概念意义。每个用户都拥有一个唯一的 ID。您可以通过该 ID 标识每位用户。

以下面的示例为例,检索一个名为小明且 ID 为 1 的用户:

$user = $em->find(User::class, 1);  
$user->setUserName('小明明');

$user2 = $builder  
    ->select('userAlias')  
    ->from($builder->alias(User::class, 'userAlias'))  
    ->where('userAlias.userName', '=', '小明')  
    ->first();  
echo $user2->getUserName();

以上代码中,用户会被实体管理器(EntityManager)访问两次,同时我们在两次检索期间对用户名称进行了修改(注意此处并没有执行任何persist操作)。无论你查询多少次或使用哪种查询方法(find、Repository Finder、DQL或Hyper QueryBuilder),Doctrine只会让您访问 ID 为 1 的相同用户实例。这称为标识映射模式(Identity Map pattern),这意味着 Doctrine 会维护查询到的每个实体与ID的映射关系,并保证永远返回相同的实例给你。

在前面的例子中,echo 的输出内容为小明明。您可以通过运行以下代码来验证$user$user2是否指向同一个实例:

if ($user === $user2) { 
	echo "Yes we are the same!"; 
}

有时您想清除 EntityManager 的标识映射以便重新开始。比如我们经常在单元测试中使用这种方法来强制再次从数据库加载对象,而不是从标识映射中获取。你可以调用EntityManager#clear()来实现这个结果。

持久化实体

通过将实体传递给EntityManager#persist($entity)方法,可以将实体持久化。该操作将使实体变成已托管(MANAGED)状态,这意味着从现在起它的持久化将交由EntityManager管理。因此,当调用EntityManager#flush()时,实体将会以正确的方式将对象与数据库进行同步。

调用persist方法不会立即触发数据库写入。Doctrine应用了一种称为事务异步回写(transactional write-behind)的策略,这意味着它将延迟执行SQL命令直到EntityManager#flush()被调用,然后将以最有效的方式在单一短事务中发送所有必要的SQL语句将您的对象与数据库进行同步,并负责维护引用的完整性。

例子:

$user = new User; 
$user->setUserName('Mr.Right'); 
$em->persist($user); 
$em->flush();

生成的实体标识符/主键保证在下一次涉及相关实体的成功刷新操作之后可用。您不能依赖生成的标识符在调用后直接可用 persist。反之亦然。您不能依赖生成的标识符在刷新操作失败后不可用。

删除实体

待补充

分离实体

待补充

合并实体

待补充

与数据库同步

待补充

Clone this wiki locally