簡體   English   中英

帶有codeigniter外鍵插入的doctrine2

[英]doctrine2 with codeigniter foreign key insert

我遵循數據庫架構 -

數據庫架構

現在部門,年份和部門表已經填滿了信息。

我現在需要插入學生數據。 學生數據將從xls文件導入(導入和解析部分已完成)。 正如您在架構中看到的那樣, student_data表中的列引用了year_iddepartment_didivision_id 因此,在插入時我需要他們的ID字段,因為xls具有各自的名稱值。

所以我要根據每個學生的列值來獲取相應的ID。 因此,這為在學生表中插入一條記錄引入了3個查詢。 像這樣 -

forloop(...):
     $studentData = new Entities\StudentData();

    $year =  $this->em->getRepository("Entities\Year")->findBy(array('year_name' => $this->year[$i]));
    $department =  $this->em->getRepository("Entities\Department")->findBy(array('department_name' => $this->branch[$i]));
    $division =  $this->em->getRepository("Entities\Division")->findBy(array('division_name'=>$this->division[$i]));

    $studentData->setYear($year[0]);
    $studentData->setDepartment($department[0]);
    $studentData->setDivision($division[0]);

    //other data
    .
    .
    .
    .
    .
    $this->em->persist($studentData);

endforloop();   

$this->em->flush();
$this->em->clear();

正如你所看到的,我將為每個部門,年份和部門獲得ID循環。 假設我正在導入100個學生列表,因此它最終會運行300個查詢來獲取這3個ID字段。

我可以在插入數據時直接從他們的名字中獲取年份,部門和部門的ID嗎? 我是學說的新手,我不知道如何去做。


更新如果問題不清楚,請告訴我。 我可以用更多細節更新它或重組它。

優化

您可以在不使用Doctrine的結果緩存的情況下優化您的流程:

首先像這樣創建一個年份的地圖:

$yearsMap = array();

$q = $em->createQuery('SELECT y.id, y.year_name FROM Entities\Year y');

foreach ($q->getScalarResult() as $row) {
    $yearsMap[$row['year_name']] = $row['id'];
}

還要創建一個部門地圖到他們的ID,並划分他們的ID。 這將導致3(輕)查詢。 放置此代碼的最佳位置是(自定義)存儲庫。

接下來你可以運行你的循環,但是“得到”這樣的實際實體:

$year       = $this->em->getReference('Entities\Year', $yearsMap[$this->year[$i]]);
$department = $this->em->getReference('Entities\Department', $departmentsMap[$this->branch[$i]]);
$division   = $this->em->getReference('Entities\Division', $divisionsMap[$this->division[$i]]);

我說“get”,因為getReference()實際上創建了一個代理(除非它已經由實體管理器加載,但在這種情況下它可能不是)。 該代理尚未加載,因此此處不執行任何查詢。

其余代碼不需要更改。

現在,當調用flush()時,Doctrine將僅加載每個不同的年份/部門/部門一次。 這可能仍然導致一些查詢,這取決於有多少不同的年/部門/司使用。 因此,如果所有100名學生使用不同的年份/部門/部門,您將最終得到403個查詢(3個用於地圖,300個用於加載代理,100個用於插入學生)。 但是,如果所有100名學生使用相同的年/部門/部門,則最終只會有106個查詢(3個用於地圖,3個用於加載代理,100個用於插入學生)。

優化另一種方式

另一種方法是使用您收集的名稱來獲取所需的所有實體:

$q = $em->createQuery('SELECT y FROM Entities\Year y INDEX BY y.year_name WHERE y.year_name IN(:years)');
$q->setParameter('years', $yearNames);

$yearsMap = $q->getResult();

您現在只需要1個查詢就可以獲得所需的所有Year實體。 您可以為部門和部門執行相同的操作。

另請注意DQL語句中的INDEX BY :這將確保您將獲得一個數組,其中year_name為鍵,實體為value。 您可以在循環中直接使用它,如下所示:

$year       = $yearsMap[$this->year[$i]];
$department = $departmentsMap[$this->branch[$i]];
$division   = $divisionsMap[$this->division[$i]];

100名學生的最終結果將是103個查詢(3個用於地圖,100個用於插入學生)。

高速緩存

當你需要相對經常運行這個循環並且它使數據庫緊張時,使用Doctrine的結果緩存是明智的。 有幾點需要注意:

getReference()不支持結果緩存(尚未),並且不會自動使用結果緩存。 所以我建議你把這樣的東西放在一個存儲庫中:

public function findOneYearByName($name)
{
    $q = $em->createQuery('SELECT y FROM Entities\Year y WHERE y.year_name = :year');
    $q->setParameter('year', $name);
    $q->useResultCache(true);

    return $q->getSingleResult();
}

您可能想要配置結果緩存,請參閱有關該文檔的文檔

另一個注意事項是結果緩存將在數據庫水合之前緩存從數據庫中獲取的結果。 因此,即使使用結果緩存,實際實體每次都會被水合。 我仍然建議使用地圖,但實施略有不同:

$yearsMap       = array();
$departmentsMap = array();
$divisionsMap   = array();

forloop (...):
    if (!isset($yearsMap[$this->year[$i]])) {
        $yearsMap[$this->year[$i]] = $this->em->getRepository('Entities\Year')->findOneYearByName($this->year[$i]);
    }

    if (!isset($departmentsMap[$this->branch[$i]])) {
        $departmentsMap[$this->branch[$i]] = $this->em->getRepository('Entities\Department')->findOneDepartmentByName($this->branch[$i]);
    }

    if (!isset($divisionsMap[$this->division[$i]])) {
        $divisionsMap[$this->division[$i]] = $this->em->getRepository('Entities\Division')->findOneDivisionByName($this->division[$i]);
    }

    $year       = $yearsMap[$this->year[$i]];
    $department = $departmentsMap[$this->branch[$i]];
    $division   = $divisionsMap[$this->division[$i]];

這將確保每個不同的年份/部門/部門僅含水一次。

PS:使用“優化另一種方式”的結果緩存不會有效,因為每次運行循環時,年/部門/部門的名稱可能會有所不同。 每次更改名稱時,查詢都會更改,並且無法使用緩存的結果。

DBAL

我可以在插入數據時直接從他們的名字中獲取年份,部門和部門的ID嗎?

你可以,但你不會使用ORM,只能使用DBAL。 你基本上這樣做:

$connection = $em->getConnection();
$statement  = $conn->executeQuery('insert query', array('parameter1', 'etc'));
$statement->execute();

我懷疑這會更有效率,因為MySQL(或你正在使用的任何供應商)仍然會為每個插入執行這些3(子)查詢,他們只是不“通過網絡”。 並且您沒有得到ORM的任何幫助,例如管理協會等。

不過,你可以在這里找到關於這個主題的一切。

你檢查一下它是否運行了300個查詢? 因為它絕對不應該,除非所有學生都有不同的年份,部門部門,這似乎不太可能。 如果是這樣的話,除了Doctrine之外,至少需要300個查詢,除非進行其他優化。

好消息是,Doctrine不僅僅是一種訪問對象的奇特方式 - 它是一個完整的數據庫抽象層,提供了更多的服務,例如一個完整的實體緩存 以下行:

$year =  $this->em->getRepository("Entities\Year")->findBy(array('year_name' => $this->year[$i]));

對於單個給定年份,這應該最多執行1個查詢 - 之后,結果將在Doctrine的實體管理器內部緩存中存儲,完全水合。 這假設你正在使用庫存MemoryCache,默認情況下啟用,如果你沒有指定任何其他東西,只在單個請求期間緩存。 如果您安裝APC,Memcache,Memcached甚至FilesystemCache( 選擇一個! ),結果可能會在多個請求中緩存。

因此,簡而言之,您正在想象一個不存在的問題,或通過一些簡單的配置調用輕松緩解。 除非我們談論假設的情況,即所有年份,部門和部門都是獨一無二的 - 那么您確實會觸發300個查詢。 然而,在這種情況下的問題不是Doctrine的 - 它只是按照你的命令執行,分別檢索300個唯一對象。 在這種情況下,沒有人阻止你自己圍繞Doctrine編寫一些智能代碼,例如:

// Build cache before loop
$years = [];
foreach($this->em->getRepository("Entities\Year")->findAll() as $year)
  $years[$year->getYearName()] = $year;

// Now loop much faster because everything's already indexed
forloop(...) :
  $studentData = new Entities\StudentData();
  $studentData->setYear($years[$this->year[$i]]);
endforloop;

突然之間你有1個“昂貴”的查詢而不是100個稍微便宜的查詢。 Doctrine是一種方便的工具,可以使很多與數據庫相關的編碼變得更容易和更有條理,它不會禁止像這樣的智能性能編碼。 最后,你仍然是編碼員,而Doctrine只是你腰帶中的一個工具,你可以按照自己的意願使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM