原始问题
我正在 Doctrine 中进行获取连接,使用具有复合键且没有其他定义字段的连接表,并将不正确的数据加载到我的实体中。这是 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();
}
预期输出:
John Doe:
宝马 7 系
Crysler 300
道奇 Dart
梅赛德斯 C 级
沃尔沃 XC90
实际输出:
John Doe:
宝马 7 系
道奇 Dart
道奇 Dart
沃尔沃 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:梅赛德斯
John Doe:宝马
Erika Mustermann:宝马
Erika Mustermann:克莱斯勒
实际输出:
John Doe:克莱斯勒
John Doe:梅赛德斯
John Doe:梅赛德斯
Erika Mustermann:宝马
Erika Mustermann:宝马
再一次,结果的数量是正确的,但关联是混乱的。
这是原始问题的简化测试用例,不受任何 WHERE 或 GROUP BY 子句的约束。