【发布时间】:2016-04-18 21:13:06
【问题描述】:
原始问题
我正在 Doctrine 中进行 fetch 连接,使用具有复合键且没有其他定义字段的连接表,并将 不正确的数据 加载到我的实体中。这是 Doctrine 中的错误,还是我做错了什么?
下面是一个说明问题的简单示例。
创建三个实体:
/**
* @ORM\Entity
* @ORM\Table(name="driver")
*/
class Driver
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=255);
*/
private $name;
/**
* @ORM\OneToMany(targetEntity="DriverRide", mappedBy="driver")
*/
private $driverRides;
function getId() { return $this->id; }
function getName() { return $this->name; }
function getDriverRides() { return $this->driverRides; }
}
/**
* @ORM\Entity
* @ORM\Table(name="driver_ride")
*/
class DriverRide
{
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Driver", inversedBy="driverRides")
* @ORM\JoinColumn(name="driver_id", referencedColumnName="id")
*/
private $driver;
/**
* @ORM\Id
* @ORM\ManyToOne(targetEntity="Car", inversedBy="carRides")
* @ORM\JoinColumn(name="car", referencedColumnName="brand")
*/
private $car;
function getDriver() { return $this->driver; }
function getCar() { return $this->car; }
}
/**
* @ORM\Entity
* @ORM\Table(name="car")
*/
class Car
{
/**
* @ORM\Id
* @ORM\Column(type="string", length=25)
* @ORM\GeneratedValue(strategy="NONE")
*/
private $brand;
/**
* @ORM\Column(type="string", length=255);
*/
private $model;
/**
* @ORM\OneToMany(targetEntity="DriverRide", mappedBy="car")
*/
private $carRides;
function getBrand() { return $this->brand; }
function getModel() { return $this->model; }
function getCarRides() { return $this->carRides; }
}
用这些数据填充相应的数据库表:
INSERT INTO driver (id, name) VALUES (1, 'John Doe');
INSERT INTO car (brand, model) VALUES ('BMW', '7 Series');
INSERT INTO car (brand, model) VALUES ('Crysler', '300');
INSERT INTO car (brand, model) VALUES ('Mercedes', 'C-Class');
INSERT INTO car (brand, model) VALUES ('Volvo', 'XC90');
INSERT INTO car (brand, model) VALUES ('Dodge', 'Dart');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Crysler');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Mercedes');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Volvo');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'BMW');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Dodge');
使用此代码水合Driver 实体并显示其内容:
$qb = $em->createQueryBuilder();
$driver = $qb->select('d, dr, c')
->from('Driver', 'd')
->leftJoin('d.driverRides', 'dr')
->leftJoin('dr.car', 'c')
->where('d.id = 1')
->getQuery()->getSingleResult();
print '<p>' . $driver->getName() . ':';
foreach ($driver->getDriverRides() as $ride) {
print '<br>' . $ride->getCar()->getBrand() . ' ' . $ride->getCar()->getModel();
}
预期输出:
约翰·多伊:
宝马 7 系
克莱斯勒 300
道奇飞镖
梅赛德斯C级
沃尔沃 XC90
实际输出:
约翰·多伊:
宝马 7 系
道奇飞镖
道奇飞镖
沃尔沃 XC90
沃尔沃 XC90
这里有一个奇怪的重复与关联实体。具体而言,子实体#3被复制为#2,#5被复制为#4等,子实体#2、#4等根本不加载。这种模式是一致且可重现的。
我的代码有问题吗?为什么 Doctrine 无法将数据库表中的数据正确映射到实体?
补充说明
如果查询所有游乐设施(即司机和汽车之间的关联)并执行 fetch-join,则会出现同样的问题。这是一个重要案例,因为@qaqar-haider 的回答不适用于这种情况。
假设下表数据
INSERT INTO driver (id, name) VALUES (1, 'John Doe');
INSERT INTO driver (id, name) VALUES (2, 'Erika Mustermann');
INSERT INTO car (brand, model) VALUES ('BMW', '7 Series');
INSERT INTO car (brand, model) VALUES ('Crysler', '300');
INSERT INTO car (brand, model) VALUES ('Mercedes', 'C-Class');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Crysler');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'Mercedes');
INSERT INTO driver_ride (driver_id, car) VALUES (1, 'BMW');
INSERT INTO driver_ride (driver_id, car) VALUES (2, 'BMW');
INSERT INTO driver_ride (driver_id, car) VALUES (2, 'Crysler');
想象一下使用 fetch-joins 的以下查询
$qb = $em->createQueryBuilder();
$rides = $qb->select('dr, d, c')
->from('DriverRide', 'dr')
->leftJoin('dr.driver', 'd')
->leftJoin('dr.car', 'c')
->getQuery()->getResult();
foreach ($rides as $ride) {
print $ride->driver->getName() . ': ' . $ride->getCar()->getModel();
}
预期输出:
约翰·多伊:克莱斯勒
John Doe:梅赛德斯
John Doe:宝马
Erika Mustermann:宝马
Erika Mustermann:克莱斯勒
实际输出:
约翰·多伊:克莱斯勒
John Doe:梅赛德斯
John Doe:梅赛德斯
Erika Mustermann:宝马
Erika Mustermann:宝马
再一次,结果的数量是正确的,但关联是混淆的。
这是原始问题的简化测试用例,不受任何 WHERE 或 GROUP BY 子句的约束。
【问题讨论】:
-
您找到解决方案了吗?我昨天遇到了同样的问题,它让我忙了几个小时却没有在我的代码中发现错误?我也得到奇怪的重复和缺失的关联。
-
需要考虑的一点:如果在初始查询中使用
getArrayResult()而不是getSingleResult(),那么$driver实际上将包含正确的汽车,以正确的顺序。 -
我几乎可以肯定这是 Doctrine 中的一个错误。我在 4 个多月前提交了 bug report,然后提交了 pull request 并进行了简单的错误修复。遗憾的是,他们都没有收到 Doctrine 项目维护者的任何回应。
标签: doctrine-orm eager-loading hydration