理论分离,缓存和合并 [英] Doctrine detaching, caching, and merging
问题描述
$ em-> createQuery('
SELECT u,c,p
FROM Entities\User u
LEFT JOIN u.company c
LEFT JOIN u.privilege p
WHERE u.id =:id
') - > setParameter 'id',$ identity)
然后我得到结果(这是一个数组,我只是采取第一个元素),并运行分离 $ em-> detach($ result);
。
当我从缓存中获取(使用Doctrine的APC缓存驱动程序)时,我做:
$ cacheDriver = new \ Doctrine\Common\Cache\ApcCache();
if($ cacheDriver-> contains($ cacheId))
{
$ entity = $ cacheDriver-> fetch($ cacheId);
$ em-> merge($ entity);
return $ entity;
}
我希望这将重新启用在实体上加载的关系除了该查询中显示的用户对象之外,还有许多其他关系绑定到用户对象。
我正在尝试创建一个这样的新实体:
$ newEntity = new Entities\ClientType();
$ newEntity ['param'] = $ data;
$ newEntitiy ['company'] = $ this-> user ['company'];
$ em-> persist($ newEntity);
$ em-> flush();
当我这样做,我收到一个错误:
通过关系Entities\ClientType#company未被配置为级联实体的持久性操作,发现一个新实体:
Entities\Company @ 000000005d7b49b500000000dd7ad743 。
要解决这个问题:在这个未知实体上显式调用EntityManager#persist()或者配置级联在映射中保留这个关联,例如@ManyToOne(..,cascade = {persist})。
如果你找不到哪个实体导致问题实现'Entities\Company #__ toString()'得到一个线索。
当我不使用我从...获得的用户实体下的公司实体时,这工作正常缓存。有没有办法使这项工作,所以我每次想要使用它与一个新实体的关系时,不必从数据库中取回公司实体?
编辑:
这是我在我的User实体处理这两个关系:
/ **
* @ManyToOne(targetEntity =Company,inversedBy =user,cascade = {detach,merge})
* /
protected $公司;
/ **
* @ManyToOne(targetEntity =Privilege,inversedBy =user,cascade = {detach,merge})
* /
保护$特权;
我仍然收到相同的错误。
第二个编辑
尝试一个 $ em->包含($ this->用户);
和 $ em->包含($ this-> user ['company']);
都返回false。哪个听起来错了?
合并用户时,您希望将关联的公司和特权合并,正确吗?
此过程称为级联:
在您的用户实体在 @ManyToOne
注释(或您所使用的其他类型的关联定义)中放入 cascade = {merge}
使用)for $ company
和 $ privilege
。
如果你想要分离呼叫也是级联的(推荐),请放入 cascade = {detach,merge}
。
ps:不要在两个si上放这样的级联一个协会,你会创造一个无止境的循环;)
编辑:
这段代码:
$ entity = $ cacheDriver-> fetch($ cacheId);
$ em-> merge($ entity); //< -
return $ entity;
应该是:
$ entity = $ cacheDriver-> fetch($ cacheId);
$ entity = $ em-> merge($ entity); //< -
return $ entity;
与 merge()
的东西是它将您传递的实体作为参数保持不变,并返回表示实体的受管版本的新对象。所以你想使用return-value,而不是你传递的参数。
I'm on Doctrine 2.3. I have the following query:
$em->createQuery('
SELECT u, c, p
FROM Entities\User u
LEFT JOIN u.company c
LEFT JOIN u.privilege p
WHERE u.id = :id
')->setParameter('id', $identity)
I then take that, get the result (which is an array, I just take the first element), and run detach $em->detach($result);
.
When I go to fetch from the cache (using Doctrine's APC cache driver), I do:
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
if($cacheDriver->contains($cacheId))
{
$entity = $cacheDriver->fetch($cacheId);
$em->merge($entity);
return $entity;
}
My hope was that this would re-enable the relationship loading on the entity as there are many other relationships tied to the User object other than what's shown in that query.
I'm trying to create a new entity like such:
$newEntity = new Entities\ClientType();
$newEntity['param'] = $data;
$newEntitiy['company'] = $this->user['company'];
$em->persist($newEntity);
$em->flush();
When I do this, I get an error:
A new entity was found through the relationship 'Entities\ClientType#company' that was not configured to cascade persist operations for entity:
Entities\Company@000000005d7b49b500000000dd7ad743.
To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}).
If you cannot find out which entity causes the problem implement 'Entities\Company#__toString()' to get a clue.
This works just fine when I don't use the company entity under the user entity that I got from cache. Is there any way to make this work so I don't have to refetch the company entity from the database every time I want to use it in a relationship with a new entity?
Edit: This is what I have in my User entity dealing with these two relationships:
/**
* @ManyToOne(targetEntity="Company" , inversedBy="user", cascade={"detach", "merge"})
*/
protected $company;
/**
* @ManyToOne(targetEntity="Privilege" , inversedBy="user", cascade={"detach", "merge"})
*/
protected $privilege;
I am still getting the same error.
Second edit:
Trying a $em->contains($this->user);
and $em->contains($this->user['company']);
both return false. Which sounds... wrong.
When merging a User, you want the associated Company and Privilege to be merged as well, correct?
This process is called cascading:
In your User entity put cascade={"merge"}
in the @ManyToOne
annotation (or another type of association-definition you are using) for $company
and $privilege
.
And if you want the detach call to be cascaded too (which is recommended), put in cascade={"detach", "merge"}
.
p.s.: Don't put such cascades on both sides of one association, you'll create an endless loop ;)
Edit:
This piece of code:
$entity = $cacheDriver->fetch($cacheId);
$em->merge($entity); // <-
return $entity;
should be:
$entity = $cacheDriver->fetch($cacheId);
$entity = $em->merge($entity); // <-
return $entity;
The thing with merge()
is that it leaves the entity you pass as an argument untouched, and returns a new object that represents the managed version of the entity. So you want to use the return-value, not the argument you passed.
这篇关于理论分离,缓存和合并的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!