Doctrine 2 中的代理是啥?

Posted

技术标签:

【中文标题】Doctrine 2 中的代理是啥?【英文标题】:What is a Proxy in Doctrine 2?Doctrine 2 中的代理是什么? 【发布时间】:2011-06-22 20:51:15 【问题描述】:

我刚看完所有的Doctrine 2文档,开始了自己的沙箱,大部分原理我都懂了,但是还有一个问题,在文档中找不到完整的解释。

    什么是Proxy 类? 什么时候应该在实体上使用它们?

据我了解,代理类添加了一个层,让您可以向实体添加一些其他功能,但是为什么要使用代理而不是在实体类中实现方法本身呢?

【问题讨论】:

【参考方案1】:

更新

此答案包含有关代理对象和部分对象之间差异的错误信息。有关详细信息,请参阅@Kontrollfreak 的答案:https://***.com/a/17787070/252591


只要您的查询未返回创建实体所需的所有数据,就会使用代理对象。想象以下场景:

@Entity
class User 
     @Column protected $id;
     @Column protected $username;
     @Column protected $firstname;
     @Column protected $lastname;

     // bunch of setters/getters here


DQL query:

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

如您所见,此查询不返回 firstnamelastname 属性,因此您无法创建 User 对象。创建不完整的实体可能会导致意外错误。

这就是为什么 Doctrine 会创建支持延迟加载的 UserProxy 对象。当您尝试访问 firstname 属性(未加载)时,它将首先从数据库中加载该值。


我的意思是我为什么要使用代理?

您应该始终像根本不使用代理对象一样编写代码。它们可以被视为 Doctrine 使用的内部对象。

为什么实体本身不能实现延迟加载?

从技术上讲,它可能是,但请看一些随机代理对象的类。它充满了肮脏的代码,呃。很高兴在您的实体中有一个干净的代码。

你能给我一个用例吗?

您正在显示最新 25 篇文章的列表,并且您希望显示第一篇文章的详细信息。它们中的每一个都包含大量文本,因此获取所有这些数据将浪费内存。这就是您不获取不必要数据的原因。

SELECT a.title, a.createdAt
FROM Entity\Article a
ORDER BY a.createdAt DESC
LIMIT 25

$isFirst = true;
foreach ($articles as $article) 
    echo $article->getTitle();
    echo $article->getCreatedAt();

    if ($isFirst) 
        echo $article->getContent(); // Article::content is not loaded so it is transparently loaded 
                                     // for this single article.

        $isFirst = false;
    

【讨论】:

感谢您的回答,Partial Object 有什么不同?我的意思是我为什么要使用代理?为什么实体本身不能实现延迟加载?你能给我一个用例吗? 部分对象和代理对象是一回事——它们可以被视为同义词。至于其他问题,请查看我更新的答案。 我不明白为什么教义如果只有一半的属性就不能创建对象。在 php 中,即使我没有设置所有属性,我也能够创建一个对象。 这是一个非常棒的答案,应该在文档中。 这个答案包含一些对代理和部分对象的严重误解。请参阅my answer 了解原因。【参考方案2】:

代理

Doctrine 代理只是一个扩展实体类以提供延迟加载的包装器。

默认情况下,当您向实体管理器询问与另一个实体关联的实体时,关联的实体不会从数据库中加载,而是包装到代理对象中。当您的应用程序随后请求该代理实体的属性或调用方法时,Doctrine 将从数据库中加载该实体(除非您请求 ID,该 ID 始终为代理所知)。

由于代理扩展了您的实体类,这对您的应用程序完全透明。

如果您没有在查询中JOIN 或将获取模式设置为EAGER,Doctrine 默认会将关联水合物作为延迟加载代理。


现在我必须添加这个,因为我没有足够的声誉在任何地方发表评论:

很遗憾,克罗津的回答包含错误信息。

如果您执行 DQL 查询,例如

SELECT u.id, u.username FROM Entity\User u WHERE u.id = :id

你不会得到一个(代理的)实体对象,而是一个关联数组。所以不可能延迟加载任何额外的属性。

考虑到这一点,我们得出的结论是,用例示例也不起作用。 必须将 DQL 更改为类似这样才能将 $article 作为对象访问:

SELECT a FROM Entity\Article a ORDER BY a.createdAt DESC LIMIT 25

getContent() 返回的属性必须是一个关联,才能不加载所有 25 个实体的内容属性。


部分对象

如果你想部分加载不是关联的实体属性,你必须明确告诉这个 Doctrine:

SELECT partial u.id, username FROM Entity\User u WHERE u.id = :id

这为您提供了一个部分加载的实体对象。

但请注意,部分对象不是代理!延迟加载不适用于它们。因此,使用局部对象通常是危险的,应该避免。阅读更多:Partial Objects — Doctrine 2 ORM 2 documentation

【讨论】:

谢谢,这比公认的答案提供了更多关于 Doctrine 如何使用代理和部分对象的详细信息!对文档的引用也很有帮助。 也供参考,这里是关于代理对象的文档部分:doctrine-orm.readthedocs.org/en/latest/reference/… 那么在进行急切加载时,基本上只是添加结果集吗?

以上是关于Doctrine 2 中的代理是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Doctrine 2 中指定十进制字段类型时,比例和精度是啥意思?

Doctrine 2. 自动生成代理

从doctrine2中的代理对象获取“true”对象

Doctrine 2.1 @Column 注释中“选项”的语法是啥?

Doctrine 2 获取代理类的真实类名

这两个 Doctrine 映射的真正区别是啥?