Schreiben von SELECT-Anweisungen für Vererbungszuordnungen

Über dieses Dokument

Dieser Abschnitt verwendet ORM-Zuordnungen, die mit der Funktion ORM-Vererbung konfiguriert wurden und die unter Mapping von Klassenvererbungshierarchien beschrieben ist. Der Schwerpunkt liegt auf der Joined Table Inheritance, da dies der komplexeste Fall für die ORM-Abfrage ist.

ORM-Einrichtung für diese Seite anzeigen.

Auswahl aus der Basisklasse vs. spezifischen Unterklassen

Eine SELECT-Anweisung, die gegen eine Klasse in einer Joined-Inheritance-Hierarchie erstellt wird, fragt die Tabelle ab, der die Klasse zugeordnet ist, sowie alle vorhandenen übergeordneten Tabellen, wobei JOIN verwendet wird, um sie zu verknüpfen. Die Abfrage gibt dann Objekte vom angeforderten Typ sowie alle Untertypen des angeforderten Typs zurück, wobei der Wert des Diskriminators in jeder Zeile verwendet wird, um den korrekten Typ zu bestimmen. Die folgende Abfrage wird gegen die Unterklasse Manager von Employee erstellt, die dann ein Ergebnis zurückgibt, das nur Objekte vom Typ Manager enthält.

>>> from sqlalchemy import select
>>> stmt = select(Manager).order_by(Manager.id)
>>> managers = session.scalars(stmt).all()
BEGIN (implicit) SELECT manager.id, employee.id AS id_1, employee.name, employee.type, employee.company_id, manager.manager_name FROM employee JOIN manager ON employee.id = manager.id ORDER BY manager.id [...] ()
>>> print(managers) [Manager('Mr. Krabs')]

Wenn die SELECT-Anweisung gegen die Basisklasse in der Hierarchie gerichtet ist, ist das Standardverhalten, dass nur die Tabelle dieser Klasse in die gerenderte SQL-Anweisung aufgenommen wird und kein JOIN verwendet wird. Wie in allen Fällen wird die Spalte des Diskriminators verwendet, um zwischen verschiedenen angeforderten Untertypen zu unterscheiden, was dann dazu führt, dass Objekte eines beliebigen möglichen Untertyps zurückgegeben werden. Die zurückgegebenen Objekte haben Attribute, die der Basistabelle entsprechen, und Attribute, die Untertabellen entsprechen, beginnen in einem nicht geladenen Zustand und werden beim Zugriff automatisch geladen. Das Laden von Unterattributen ist konfigurierbar, um auf verschiedene Weise "eagerer" zu sein, was später in diesem Abschnitt behandelt wird.

Das folgende Beispiel erstellt eine Abfrage gegen die Oberklasse Employee. Dies bedeutet, dass Objekte eines beliebigen Typs, einschließlich Manager, Engineer und Employee, im Ergebnis-Set enthalten sein können.

>>> from sqlalchemy import select
>>> stmt = select(Employee).order_by(Employee.id)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit) SELECT employee.id, employee.name, employee.type, employee.company_id FROM employee ORDER BY employee.id [...] ()
>>> print(objects) [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]

Oben wurden die zusätzlichen Tabellen für Manager und Engineer nicht in die SELECT-Anweisung aufgenommen, was bedeutet, dass die zurückgegebenen Objekte noch keine Daten enthalten, die aus diesen Tabellen stammen, z. B. das Attribut .manager_name der Klasse Manager sowie das Attribut .engineer_info der Klasse Engineer. Diese Attribute beginnen im abgelaufenen Zustand und werden beim ersten Zugriff automatisch über Lazy Loading gefüllt.

>>> mr_krabs = objects[0]
>>> print(mr_krabs.manager_name)
SELECT manager.manager_name AS manager_manager_name FROM manager WHERE ? = manager.id [...] (1,)
Eugene H. Krabs

Dieses Lazy-Load-Verhalten ist unerwünscht, wenn eine große Anzahl von Objekten geladen wurde und die konsumierende Anwendung auf spezifische Attribute von Unterklassen zugreifen muss, da dies ein Beispiel für das N+1-Problem darstellt, das zusätzliche SQL pro Zeile erzeugt. Dieses zusätzliche SQL kann die Leistung beeinträchtigen und ist auch mit Ansätzen wie der Verwendung von asyncio inkompatibel. Darüber hinaus hatten wir bei unserer Abfrage nach Employee-Objekten, da die Abfrage nur gegen die Basistabelle gerichtet war, keine Möglichkeit, SQL-Kriterien einzubeziehen, die auf spezifischen Attributen von Unterklassen wie Manager oder Engineer basieren. Die nächsten beiden Abschnitte beschreiben zwei Konstrukte, die auf unterschiedliche Weise Lösungen für diese beiden Probleme bieten: die Ladeoption selectin_polymorphic() und das Entitätskonstrukt with_polymorphic().

Verwenden von selectin_polymorphic()

Um das Leistungsproblem beim Zugriff auf Attribute von Unterklassen zu lösen, kann die Ladefunktion selectin_polymorphic() verwendet werden, um diese zusätzlichen Attribute für viele Objekte gleichzeitig eager zu laden. Diese Ladeoption funktioniert ähnlich wie die Beziehungs-Ladefunktion selectinload(), indem sie eine zusätzliche SELECT-Anweisung gegen jede Untertabelle für Objekte aussendet, die in der Hierarchie geladen wurden, und dabei IN verwendet, um zusätzliche Zeilen basierend auf dem Primärschlüssel abzufragen.

selectin_polymorphic() akzeptiert als Argumente die Basisentität, die abgefragt wird, gefolgt von einer Sequenz von Unterklassen dieser Entität, für die ihre spezifischen Attribute für eingehende Zeilen geladen werden sollen.

>>> from sqlalchemy.orm import selectin_polymorphic
>>> loader_opt = selectin_polymorphic(Employee, [Manager, Engineer])

Das Konstrukt selectin_polymorphic() wird dann als Ladeoption verwendet und an die Methode Select.options() von Select übergeben. Das Beispiel veranschaulicht die Verwendung von selectin_polymorphic(), um Spalten, die zu den Unterklassen Manager und Engineer gehören, eager zu laden.

>>> from sqlalchemy.orm import selectin_polymorphic
>>> loader_opt = selectin_polymorphic(Employee, [Manager, Engineer])
>>> stmt = select(Employee).order_by(Employee.id).options(loader_opt)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit) SELECT employee.id, employee.name, employee.type, employee.company_id FROM employee ORDER BY employee.id [...] () SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name FROM employee JOIN manager ON employee.id = manager.id WHERE employee.id IN (?) ORDER BY employee.id [...] (1,) SELECT engineer.id AS engineer_id, employee.id AS employee_id, employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info FROM employee JOIN engineer ON employee.id = engineer.id WHERE employee.id IN (?, ?) ORDER BY employee.id [...] (2, 3)
>>> print(objects) [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]

Das obige Beispiel veranschaulicht zwei zusätzliche SELECT-Anweisungen, die ausgegeben werden, um zusätzliche Attribute wie Engineer.engineer_info sowie Manager.manager_name eager zu laden. Wir können diese Unterattribute nun auf den geladenen Objekten abrufen, ohne dass zusätzliche SQL-Anweisungen ausgegeben werden.

>>> print(objects[0].manager_name)
Eugene H. Krabs

Tipp

Die Ladeoption selectin_polymorphic() optimiert noch nicht die Tatsache, dass die Basistabelle employee in den beiden anderen "eager load"-Abfragen nicht enthalten sein muss. Daher sehen wir im obigen Beispiel einen JOIN von employee zu manager und engineer, obwohl Spalten aus employee bereits geladen sind. Dies steht im Gegensatz zur Beziehungs-Ladefunktion selectinload(), die in dieser Hinsicht ausgefeilter ist und den JOIN ausklammern kann, wenn er nicht benötigt wird.

Anwenden von selectin_polymorphic() auf eine vorhandene Eager-Zuweisung

Zusätzlich zur Angabe von selectin_polymorphic() als Option für eine Top-Level-Entität, die von einer Anweisung geladen wird, können wir selectin_polymorphic() auch auf dem Ziel einer vorhandenen Zuweisung angeben. Da unser Setup eine übergeordnete Company-Entität mit einer Company.employees relationship(), die sich auf Employee-Entitäten bezieht, enthält, können wir eine SELECT-Anweisung gegen die Company-Entität illustrieren, die alle Employee-Objekte sowie alle Attribute ihrer Untertypen eager lädt, indem wir Load.selectin_polymorphic() als verkettete Ladeoption anwenden; in dieser Form ist das erste Argument implizit aus der vorherigen Ladeoption (in diesem Fall selectinload()), so dass wir nur die zusätzlichen Zielunterklassen angeben, die wir laden möchten.

>>> from sqlalchemy.orm import selectinload
>>> stmt = select(Company).options(
...     selectinload(Company.employees).selectin_polymorphic([Manager, Engineer])
... )
>>> for company in session.scalars(stmt):
...     print(f"company: {company.name}")
...     print(f"employees: {company.employees}")
BEGIN (implicit) SELECT company.id, company.name FROM company [...] () SELECT employee.company_id AS employee_company_id, employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type FROM employee WHERE employee.company_id IN (?) [...] (1,) SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name FROM employee JOIN manager ON employee.id = manager.id WHERE employee.id IN (?) ORDER BY employee.id [...] (1,) SELECT engineer.id AS engineer_id, employee.id AS employee_id, employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info FROM employee JOIN engineer ON employee.id = engineer.id WHERE employee.id IN (?, ?) ORDER BY employee.id [...] (2, 3)
company: Krusty Krab employees: [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]

Siehe auch

Eager Loading von polymorphen Untertypen – illustriert das äquivalente Beispiel wie oben unter Verwendung von with_polymorphic().

Anwenden von Ladeoptionen auf die von selectin_polymorphic geladenen Unterklassen

Die von selectin_polymorphic() ausgegebenen SELECT-Anweisungen sind selbst ORM-Anweisungen, daher können wir auch andere Ladeoptionen hinzufügen (wie z. B. die unter Relationship Loading Techniques dokumentierten), die sich auf spezifische Unterklassen beziehen. Diese Optionen sollten als **Geschwister** einer selectin_polymorphic()-Option angewendet werden, d. h. durch Kommas getrennt innerhalb von select.options().

Wenn wir beispielsweise davon ausgehen, dass der Manager-Mapper eine One-to-Many-Beziehung zu einer Entität namens Paperwork hat, könnten wir die Verwendung von selectin_polymorphic() und selectinload() kombinieren, um diese Sammlung auf allen Manager-Objekten eager zu laden, wobei die Unterattribute von Manager-Objekten selbst ebenfalls eager geladen wurden.

>>> from sqlalchemy.orm import selectin_polymorphic
>>> stmt = (
...     select(Employee)
...     .order_by(Employee.id)
...     .options(
...         selectin_polymorphic(Employee, [Manager, Engineer]),
...         selectinload(Manager.paperwork),
...     )
... )
>>> objects = session.scalars(stmt).all()
SELECT employee.id, employee.name, employee.type, employee.company_id FROM employee ORDER BY employee.id [...] () SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name FROM employee JOIN manager ON employee.id = manager.id WHERE employee.id IN (?) ORDER BY employee.id [...] (1,) SELECT paperwork.manager_id AS paperwork_manager_id, paperwork.id AS paperwork_id, paperwork.document_name AS paperwork_document_name FROM paperwork WHERE paperwork.manager_id IN (?) [...] (1,) SELECT engineer.id AS engineer_id, employee.id AS employee_id, employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info FROM employee JOIN engineer ON employee.id = engineer.id WHERE employee.id IN (?, ?) ORDER BY employee.id [...] (2, 3)
>>> print(objects[0]) Manager('Mr. Krabs') >>> print(objects[0].paperwork) [Paperwork('Secret Recipes'), Paperwork('Krabby Patty Orders')]

Anwenden von Ladeoptionen, wenn selectin_polymorphic selbst eine Unteroption ist

Neu in Version 2.0.21.

Der vorherige Abschnitt veranschaulichte die Verwendung von selectin_polymorphic() und selectinload() als geschwisterliche Optionen, beide verwendet innerhalb eines einzigen Aufrufs von select.options(). Wenn die Zielentität eine ist, die bereits aus einer übergeordneten Beziehung geladen wird, wie im Beispiel unter Anwenden von selectin_polymorphic() auf eine vorhandene Eager-Zuweisung, können wir dieses "Geschwister"-Muster mithilfe der Methode Load.options() anwenden, die Unteroptionen auf eine übergeordnete Entität anwendet, wie unter Angabe von Unteroptionen mit Load.options() veranschaulicht. Nachfolgend kombinieren wir die beiden Beispiele, um Company.employees zu laden, auch die Attribute für die Klassen Manager und Engineer zu laden und außerdem das Attribut `Manager.paperwork` eager zu laden.

>>> from sqlalchemy.orm import selectinload
>>> stmt = select(Company).options(
...     selectinload(Company.employees).options(
...         selectin_polymorphic(Employee, [Manager, Engineer]),
...         selectinload(Manager.paperwork),
...     )
... )
>>> for company in session.scalars(stmt):
...     print(f"company: {company.name}")
...     for employee in company.employees:
...         if isinstance(employee, Manager):
...             print(f"manager: {employee.name} paperwork: {employee.paperwork}")
BEGIN (implicit) SELECT company.id, company.name FROM company [...] () SELECT employee.company_id AS employee_company_id, employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type FROM employee WHERE employee.company_id IN (?) [...] (1,) SELECT manager.id AS manager_id, employee.id AS employee_id, employee.type AS employee_type, manager.manager_name AS manager_manager_name FROM employee JOIN manager ON employee.id = manager.id WHERE employee.id IN (?) ORDER BY employee.id [...] (1,) SELECT paperwork.manager_id AS paperwork_manager_id, paperwork.id AS paperwork_id, paperwork.document_name AS paperwork_document_name FROM paperwork WHERE paperwork.manager_id IN (?) [...] (1,) SELECT engineer.id AS engineer_id, employee.id AS employee_id, employee.type AS employee_type, engineer.engineer_info AS engineer_engineer_info FROM employee JOIN engineer ON employee.id = engineer.id WHERE employee.id IN (?, ?) ORDER BY employee.id [...] (2, 3)
company: Krusty Krab manager: Mr. Krabs paperwork: [Paperwork('Secret Recipes'), Paperwork('Krabby Patty Orders')]

Konfigurieren von selectin_polymorphic() auf Mappern

Das Verhalten von selectin_polymorphic() kann auf spezifischen Mappern so konfiguriert werden, dass es standardmäßig stattfindet, indem der Parameter Mapper.polymorphic_load verwendet wird, und zwar mit dem Wert "selectin" auf Pro-Unterklassen-Basis. Das folgende Beispiel veranschaulicht die Verwendung dieses Parameters innerhalb der Unterklassen Engineer und Manager.

class Employee(Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    type = mapped_column(String(50))

    __mapper_args__ = {"polymorphic_identity": "employee", "polymorphic_on": type}


class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
    engineer_info = mapped_column(String(30))

    __mapper_args__ = {
        "polymorphic_load": "selectin",
        "polymorphic_identity": "engineer",
    }


class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
    manager_name = mapped_column(String(30))

    __mapper_args__ = {
        "polymorphic_load": "selectin",
        "polymorphic_identity": "manager",
    }

Mit der obigen Zuordnung werden SELECT-Anweisungen gegen die Klasse Employee automatisch die Verwendung von selectin_polymorphic(Employee, [Engineer, Manager]) als Ladeoption annehmen, wenn die Anweisung ausgegeben wird.

Verwenden von with_polymorphic()

Im Gegensatz zu selectin_polymorphic(), das nur das Laden von Objekten beeinflusst, beeinflusst das Konstrukt with_polymorphic(), wie die SQL-Abfrage für eine polymorphe Struktur gerendert wird, am häufigsten als eine Reihe von LEFT OUTER JOINs zu jeder der enthaltenen Untertabellen. Diese Join-Struktur wird als **polymorphe Selektierbare** bezeichnet. Indem es eine gleichzeitige Ansicht mehrerer Untertabellen ermöglicht, bietet with_polymorphic() eine Möglichkeit, eine SELECT-Anweisung über mehrere vererbte Klassen gleichzeitig zu schreiben, mit der Möglichkeit, Filterkriterien basierend auf einzelnen Untertabellen hinzuzufügen.

with_polymorphic() ist im Wesentlichen eine spezielle Form des aliased()-Konstrukts. Es akzeptiert Argumente in einem ähnlichen Format wie selectin_polymorphic(), d. h. die Basisentität, die abgefragt wird, gefolgt von einer Sequenz von Unterklassen dieser Entität, für die ihre spezifischen Attribute für eingehende Zeilen geladen werden sollen.

>>> from sqlalchemy.orm import with_polymorphic
>>> employee_poly = with_polymorphic(Employee, [Engineer, Manager])

Um anzugeben, dass alle Unterklassen Teil der Entität sein sollen, akzeptiert with_polymorphic() auch den String "*", der anstelle der Sequenz von Klassen übergeben werden kann, um alle Klassen anzugeben (beachten Sie, dass dies von selectin_polymorphic() noch nicht unterstützt wird).

>>> employee_poly = with_polymorphic(Employee, "*")

Das folgende Beispiel veranschaulicht dieselbe Operation wie im vorherigen Abschnitt, um alle Spalten für Manager und Engineer gleichzeitig zu laden.

>>> stmt = select(employee_poly).order_by(employee_poly.id)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit) SELECT employee.id, employee.name, employee.type, employee.company_id, manager.id AS id_1, manager.manager_name, engineer.id AS id_2, engineer.engineer_info FROM employee LEFT OUTER JOIN manager ON employee.id = manager.id LEFT OUTER JOIN engineer ON employee.id = engineer.id ORDER BY employee.id [...] ()
>>> print(objects) [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]

Wie bei selectin_polymorphic() sind Attribute von Unterklassen bereits geladen.

>>> print(objects[0].manager_name)
Eugene H. Krabs

Da die von with_polymorphic() erzeugte Standard-Selektierbare LEFT OUTER JOIN verwendet, ist die Abfrage aus Datenbankperspektive nicht so gut optimiert wie der Ansatz, den selectin_polymorphic() verfolgt, mit einfachen SELECT-Anweisungen, die nur JOINs pro Tabelle ausgeben.

Filtern von Attributen von Unterklassen mit with_polymorphic()

Das Konstrukt with_polymorphic() macht Attribute der enthaltenen Unterklassen-Mapper verfügbar, indem es Namespaces einbezieht, die Referenzen auf Unterklassen ermöglichen. Das im vorherigen Abschnitt erstellte Konstrukt employee_poly enthält Attribute mit den Namen .Engineer und .Manager, die den Namespace für Engineer und Manager in Bezug auf die polymorphe SELECT-Anweisung bereitstellen. Im folgenden Beispiel können wir das Konstrukt or_() verwenden, um gleichzeitig Kriterien für beide Klassen zu erstellen.

>>> from sqlalchemy import or_
>>> employee_poly = with_polymorphic(Employee, [Engineer, Manager])
>>> stmt = (
...     select(employee_poly)
...     .where(
...         or_(
...             employee_poly.Manager.manager_name == "Eugene H. Krabs",
...             employee_poly.Engineer.engineer_info
...             == "Senior Customer Engagement Engineer",
...         )
...     )
...     .order_by(employee_poly.id)
... )
>>> objects = session.scalars(stmt).all()
SELECT employee.id, employee.name, employee.type, employee.company_id, manager.id AS id_1, manager.manager_name, engineer.id AS id_2, engineer.engineer_info FROM employee LEFT OUTER JOIN manager ON employee.id = manager.id LEFT OUTER JOIN engineer ON employee.id = engineer.id WHERE manager.manager_name = ? OR engineer.engineer_info = ? ORDER BY employee.id [...] ('Eugene H. Krabs', 'Senior Customer Engagement Engineer')
>>> print(objects) [Manager('Mr. Krabs'), Engineer('Squidward')]

Verwenden von Aliasing mit with_polymorphic

Das Konstrukt with_polymorphic() bietet als Sonderfall von aliased() auch die grundlegende Funktionalität, die aliased() bietet, nämlich das "Aliasing" der polymorphen Selektierbaren selbst. Konkret bedeutet dies, dass zwei oder mehr with_polymorphic()-Entitäten, die sich auf dieselbe Klassenhierarchie beziehen, gleichzeitig in einer einzigen Anweisung verwendet werden können.

Um diese Funktion mit einer Joined-Inheritance-Zuordnung zu verwenden, möchten wir typischerweise zwei Parameter übergeben: with_polymorphic.aliased sowie with_polymorphic.flat. Der Parameter with_polymorphic.aliased zeigt an, dass die polymorphe Selektierbare durch einen Aliasnamen referenziert werden soll, der für dieses Konstrukt eindeutig ist. Der Parameter with_polymorphic.flat ist spezifisch für die Standard-LEFT-OUTER-JOIN-polymorphe Selektierbare und gibt an, dass eine optimierte Form des Aliasings in der Anweisung verwendet werden soll.

Um diese Funktion zu veranschaulichen, gibt das folgende Beispiel eine SELECT-Anweisung für zwei separate polymorphe Entitäten aus: Employee mit Engineer, und Employee mit Manager. Da diese beiden polymorphen Entitäten die Basistabelle employee in ihre polymorphe Selektierbare einbeziehen, muss Aliasing angewendet werden, um diese Tabelle in ihren beiden verschiedenen Kontexten zu unterscheiden. Die beiden polymorphen Entitäten werden wie zwei einzelne Tabellen behandelt und müssen daher typischerweise auf irgendeine Weise miteinander verknüpft werden, wie unten veranschaulicht, wo die Entitäten über die Spalte company_id sowie einige zusätzliche Einschränkungen für die Entität Employee / Manager verknüpft werden.

>>> manager_employee = with_polymorphic(Employee, [Manager], aliased=True, flat=True)
>>> engineer_employee = with_polymorphic(Employee, [Engineer], aliased=True, flat=True)
>>> stmt = (
...     select(manager_employee, engineer_employee)
...     .join(
...         engineer_employee,
...         engineer_employee.company_id == manager_employee.company_id,
...     )
...     .where(
...         or_(
...             manager_employee.name == "Mr. Krabs",
...             manager_employee.Manager.manager_name == "Eugene H. Krabs",
...         )
...     )
...     .order_by(engineer_employee.name, manager_employee.name)
... )
>>> for manager, engineer in session.execute(stmt):
...     print(f"{manager} {engineer}")
SELECT employee_1.id, employee_1.name, employee_1.type, employee_1.company_id, manager_1.id AS id_1, manager_1.manager_name, employee_2.id AS id_2, employee_2.name AS name_1, employee_2.type AS type_1, employee_2.company_id AS company_id_1, engineer_1.id AS id_3, engineer_1.engineer_info FROM employee AS employee_1 LEFT OUTER JOIN manager AS manager_1 ON employee_1.id = manager_1.id JOIN (employee AS employee_2 LEFT OUTER JOIN engineer AS engineer_1 ON employee_2.id = engineer_1.id) ON employee_2.company_id = employee_1.company_id WHERE employee_1.name = ? OR manager_1.manager_name = ? ORDER BY employee_2.name, employee_1.name [...] ('Mr. Krabs', 'Eugene H. Krabs')
Manager('Mr. Krabs') Manager('Mr. Krabs') Manager('Mr. Krabs') Engineer('SpongeBob') Manager('Mr. Krabs') Engineer('Squidward')

Im obigen Beispiel besteht das Verhalten von with_polymorphic.flat darin, dass die polymorphen Selektierbaren als LEFT OUTER JOIN ihrer einzelnen Tabellen verbleiben, die selbst anonyme Aliasnamen erhalten. Es wird auch ein rechts verschachtelter JOIN erzeugt.

Beim Weglassen des Parameters with_polymorphic.flat ist das übliche Verhalten, dass jede polymorphe Selektierbare innerhalb einer Unterabfrage eingeschlossen wird, was zu einer ausführlicheren Form führt.

>>> manager_employee = with_polymorphic(Employee, [Manager], aliased=True)
>>> engineer_employee = with_polymorphic(Employee, [Engineer], aliased=True)
>>> stmt = (
...     select(manager_employee, engineer_employee)
...     .join(
...         engineer_employee,
...         engineer_employee.company_id == manager_employee.company_id,
...     )
...     .where(
...         or_(
...             manager_employee.name == "Mr. Krabs",
...             manager_employee.Manager.manager_name == "Eugene H. Krabs",
...         )
...     )
...     .order_by(engineer_employee.name, manager_employee.name)
... )
>>> print(stmt)
SELECT anon_1.employee_id, anon_1.employee_name, anon_1.employee_type, anon_1.employee_company_id, anon_1.manager_id, anon_1.manager_manager_name, anon_2.employee_id AS employee_id_1, anon_2.employee_name AS employee_name_1, anon_2.employee_type AS employee_type_1, anon_2.employee_company_id AS employee_company_id_1, anon_2.engineer_id, anon_2.engineer_engineer_info FROM (SELECT employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type, employee.company_id AS employee_company_id, manager.id AS manager_id, manager.manager_name AS manager_manager_name FROM employee LEFT OUTER JOIN manager ON employee.id = manager.id) AS anon_1 JOIN (SELECT employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type, employee.company_id AS employee_company_id, engineer.id AS engineer_id, engineer.engineer_info AS engineer_engineer_info FROM employee LEFT OUTER JOIN engineer ON employee.id = engineer.id) AS anon_2 ON anon_2.employee_company_id = anon_1.employee_company_id WHERE anon_1.employee_name = :employee_name_2 OR anon_1.manager_manager_name = :manager_manager_name_1 ORDER BY anon_2.employee_name, anon_1.employee_name

Die obige Form war historisch gesehen portabler für Backends, die nicht unbedingt die Unterstützung für rechts verschachtelte JOINs hatten, und sie kann zusätzlich angemessen sein, wenn die von with_polymorphic() verwendete "polymorphe Selektierbare" kein einfacher LEFT OUTER JOIN von Tabellen ist, wie es bei der Verwendung von Zuordnungen wie Concrete Table Inheritance-Zuordnungen der Fall ist, sowie bei der allgemeinen Verwendung alternativer polymorpher Selektierbarer.

Konfigurieren von with_polymorphic() auf Mappern

Wie bei selectin_polymorphic() unterstützt das Konstrukt with_polymorphic() ebenfalls eine mapper-konfigurierte Version, die auf zwei verschiedene Arten konfiguriert werden kann: entweder auf der Basisklasse mit dem Parameter mapper.with_polymorphic oder in einer moderneren Form mit dem Parameter Mapper.polymorphic_load auf Pro-Unterklassen-Basis, wobei der Wert "inline" übergeben wird.

Warnung

Für Joined-Inheritance-Zuordnungen wird die explizite Verwendung von with_polymorphic() innerhalb von Abfragen bevorzugt, oder für implizites Eager-Laden von Unterklassen verwenden Sie Mapper.polymorphic_load mit "selectin", anstatt den in diesem Abschnitt beschriebenen Mapper-Level-Parameter mapper.with_polymorphic zu verwenden. Dieser Parameter ruft komplexe Heuristiken auf, die versuchen, die FROM-Klauseln in SELECT-Anweisungen neu zu schreiben, was die Erstellung komplexerer Anweisungen, insbesondere solcher mit verschachtelten Unterabfragen, die sich auf dieselbe gemappte Entität beziehen, beeinträchtigen kann.

Zum Beispiel können wir unsere Employee-Zuordnung mit Mapper.polymorphic_load als "inline" wie folgt angeben:

class Employee(Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    type = mapped_column(String(50))

    __mapper_args__ = {"polymorphic_identity": "employee", "polymorphic_on": type}


class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
    engineer_info = mapped_column(String(30))

    __mapper_args__ = {
        "polymorphic_load": "inline",
        "polymorphic_identity": "engineer",
    }


class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
    manager_name = mapped_column(String(30))

    __mapper_args__ = {
        "polymorphic_load": "inline",
        "polymorphic_identity": "manager",
    }

Mit der obigen Zuordnung werden SELECT-Anweisungen gegen die Klasse Employee automatisch die Verwendung von with_polymorphic(Employee, [Engineer, Manager]) als primäre Entität annehmen, wenn die Anweisung ausgegeben wird.

print(select(Employee))
SELECT employee.id, employee.name, employee.type, engineer.id AS id_1, engineer.engineer_info, manager.id AS id_2, manager.manager_name FROM employee LEFT OUTER JOIN engineer ON employee.id = engineer.id LEFT OUTER JOIN manager ON employee.id = manager.id

Bei der Verwendung von Mapper-Level "with polymorphic" können Abfragen sich auch direkt auf die Unterklassen-Entitäten beziehen, wo sie implizit die Joined-Tabellen in der polymorphen Abfrage repräsentieren. Oben können wir uns frei auf Manager und Engineer direkt gegen die Standard-Entität Employee beziehen.

print(
    select(Employee).where(
        or_(Manager.manager_name == "x", Engineer.engineer_info == "y")
    )
)
SELECT employee.id, employee.name, employee.type, engineer.id AS id_1, engineer.engineer_info, manager.id AS id_2, manager.manager_name FROM employee LEFT OUTER JOIN engineer ON employee.id = engineer.id LEFT OUTER JOIN manager ON employee.id = manager.id WHERE manager.manager_name = :manager_name_1 OR engineer.engineer_info = :engineer_info_1

Wenn wir uns jedoch in separaten, aliasten Kontexten auf die Employee Entität oder ihre Unterentitäten beziehen müssten, würden wir wieder direkt with_polymorphic() verwenden, um diese aliasten Entitäten zu definieren, wie in Verwendung von Aliasing mit with_polymorphic beschrieben.

Für eine zentralere Steuerung des polymorphen Selektierbaren kann die ältere Form der polymorphen Steuerung auf Mapper-Ebene verwendet werden, nämlich der Parameter Mapper.with_polymorphic, der auf der Basisklasse konfiguriert wird. Dieser Parameter akzeptiert Argumente, die mit dem with_polymorphic() Konstrukt vergleichbar sind. Die gängige Verwendung bei einer Joined-Inheritance-Abbildung ist jedoch der einfache Stern (*), der angibt, dass alle Subtabellen mit LEFT OUTER JOIN verbunden werden sollen, wie in

class Employee(Base):
    __tablename__ = "employee"
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String(50))
    type = mapped_column(String(50))

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "with_polymorphic": "*",
        "polymorphic_on": type,
    }


class Engineer(Employee):
    __tablename__ = "engineer"
    id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
    engineer_info = mapped_column(String(30))

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }


class Manager(Employee):
    __tablename__ = "manager"
    id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True)
    manager_name = mapped_column(String(30))

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

Insgesamt kann das LEFT OUTER JOIN-Format, das von with_polymorphic() und von Optionen wie Mapper.with_polymorphic verwendet wird, aus Sicht von SQL und dem Datenbankoptimierer umständlich sein; für das allgemeine Laden von Unterklassenattributen in Joined-Inheritance-Abbildungen ist der Ansatz selectin_polymorphic() oder sein Äquivalent auf Mapper-Ebene durch Setzen von Mapper.polymorphic_load auf "selectin" wahrscheinlich vorzuziehen, wobei with_polymorphic() nur bei Bedarf pro Abfrage verwendet wird.

Verknüpfung mit spezifischen Untertypen oder with_polymorphic()-Entitäten

Da eine with_polymorphic()-Entität ein Sonderfall von aliased() ist, verwenden wir, um eine polymorphe Entität als Ziel einer Verknüpfung zu behandeln, insbesondere wenn ein relationship()-Konstrukt als ON-Klausel verwendet wird, die gleiche Technik wie für reguläre Aliase, wie unter Verwendung von Relationships zum Verknüpfen zwischen aliasten Zielen beschrieben, am kompaktesten mit PropComparator.of_type(). Im folgenden Beispiel illustrieren wir eine Verknüpfung von der übergeordneten Company-Entität über die Eins-zu-viele-Beziehung Company.employees, die im Setup so konfiguriert ist, dass sie auf Employee-Objekte verweist, unter Verwendung einer with_polymorphic()-Entität als Ziel.

>>> employee_plus_engineer = with_polymorphic(Employee, [Engineer])
>>> stmt = (
...     select(Company.name, employee_plus_engineer.name)
...     .join(Company.employees.of_type(employee_plus_engineer))
...     .where(
...         or_(
...             employee_plus_engineer.name == "SpongeBob",
...             employee_plus_engineer.Engineer.engineer_info
...             == "Senior Customer Engagement Engineer",
...         )
...     )
... )
>>> for company_name, emp_name in session.execute(stmt):
...     print(f"{company_name} {emp_name}")
SELECT company.name, employee.name AS name_1 FROM company JOIN (employee LEFT OUTER JOIN engineer ON employee.id = engineer.id) ON company.id = employee.company_id WHERE employee.name = ? OR engineer.engineer_info = ? [...] ('SpongeBob', 'Senior Customer Engagement Engineer')
Krusty Krab SpongeBob Krusty Krab Squidward

Direkter ausgedrückt wird PropComparator.of_type() auch mit Vererbungsabbildungen jeglicher Art verwendet, um eine Verknüpfung entlang eines relationship() zu einem bestimmten Untertyp des Ziels der relationship() einzuschränken. Die obige Abfrage könnte rein in Bezug auf Engineer-Ziele wie folgt geschrieben werden:

>>> stmt = (
...     select(Company.name, Engineer.name)
...     .join(Company.employees.of_type(Engineer))
...     .where(
...         or_(
...             Engineer.name == "SpongeBob",
...             Engineer.engineer_info == "Senior Customer Engagement Engineer",
...         )
...     )
... )
>>> for company_name, emp_name in session.execute(stmt):
...     print(f"{company_name} {emp_name}")
SELECT company.name, employee.name AS name_1 FROM company JOIN (employee JOIN engineer ON employee.id = engineer.id) ON company.id = employee.company_id WHERE employee.name = ? OR engineer.engineer_info = ? [...] ('SpongeBob', 'Senior Customer Engagement Engineer')
Krusty Krab SpongeBob Krusty Krab Squidward

Wie oben zu sehen ist, hat die direkte Verknüpfung zum Engineer-Ziel, anstatt zur "polymorphen Selektion" von with_polymorphic(Employee, [Engineer]), den nützlichen Vorteil, dass ein Inner JOIN anstelle eines LEFT OUTER JOIN verwendet wird, was aus Sicht eines SQL-Optimierers generell performanter ist.

Eager Loading von polymorphen Untertypen

Die Verwendung von PropComparator.of_type(), die in der vorherigen Sektion mit der Select.join()-Methode illustriert wurde, kann auch äquivalent auf Relationship-Loader-Optionen wie selectinload() und joinedload() angewendet werden.

Als einfaches Beispiel: Wenn wir Company-Objekte laden und zusätzlich alle Elemente von Company.employees mittels des with_polymorphic()-Konstrukts gegen die gesamte Hierarchie eager laden möchten, können wir schreiben:

>>> all_employees = with_polymorphic(Employee, "*")
>>> stmt = select(Company).options(selectinload(Company.employees.of_type(all_employees)))
>>> for company in session.scalars(stmt):
...     print(f"company: {company.name}")
...     print(f"employees: {company.employees}")
SELECT company.id, company.name FROM company [...] () SELECT employee.company_id AS employee_company_id, employee.id AS employee_id, employee.name AS employee_name, employee.type AS employee_type, manager.id AS manager_id, manager.manager_name AS manager_manager_name, engineer.id AS engineer_id, engineer.engineer_info AS engineer_engineer_info FROM employee LEFT OUTER JOIN manager ON employee.id = manager.id LEFT OUTER JOIN engineer ON employee.id = engineer.id WHERE employee.company_id IN (?) [...] (1,) company: Krusty Krab employees: [Manager('Mr. Krabs'), Engineer('SpongeBob'), Engineer('Squidward')]

Die obige Abfrage kann direkt mit der selectin_polymorphic()-Version verglichen werden, die in der vorherigen Sektion Anwenden von selectin_polymorphic() auf einen bestehenden Eager Load illustriert wird.

Siehe auch

Anwenden von selectin_polymorphic() auf einen bestehenden Eager Load - illustriert das äquivalente Beispiel wie oben, aber unter Verwendung von selectin_polymorphic().

SELECT-Anweisungen für Single-Inheritance-Abbildungen

Single Table Inheritance Setup

Dieser Abschnitt behandelt Single Table Inheritance, beschrieben unter Single Table Inheritance, bei der eine einzelne Tabelle zur Darstellung mehrerer Klassen in einer Hierarchie verwendet wird.

ORM-Setup für diesen Abschnitt anzeigen.

Im Gegensatz zu Joined-Inheritance-Abbildungen ist die Konstruktion von SELECT-Anweisungen für Single-Inheritance-Abbildungen tendenziell einfacher, da bei einer rein Single-Inheritance-Hierarchie nur eine Tabelle existiert.

Unabhängig davon, ob die Vererbungshierarchie rein Single-Inheritance ist oder eine Mischung aus Joined und Single Inheritance aufweist, unterscheiden SELECT-Anweisungen für Single-Inheritance Abfragen der Basisklasse von denen einer Unterklasse, indem sie die SELECT-Anweisung mit zusätzlichen WHERE-Kriterien einschränken.

Als Beispiel wird eine Abfrage für die Single-Inheritance-Abbildung von Employee Objekte vom Typ Manager, Engineer und Employee mithilfe einer einfachen SELECT-Abfrage der Tabelle laden.

>>> stmt = select(Employee).order_by(Employee.id)
>>> for obj in session.scalars(stmt):
...     print(f"{obj}")
BEGIN (implicit) SELECT employee.id, employee.name, employee.type FROM employee ORDER BY employee.id [...] ()
Manager('Mr. Krabs') Engineer('SpongeBob') Engineer('Squidward')

Wenn eine Ladeanforderung für eine spezifische Unterklasse ausgegeben wird, werden zusätzliche Kriterien zur SELECT-Anweisung hinzugefügt, die die Zeilen einschränken, wie unten gezeigt, wo eine SELECT-Abfrage gegen die Engineer-Entität durchgeführt wird.

>>> stmt = select(Engineer).order_by(Engineer.id)
>>> objects = session.scalars(stmt).all()
SELECT employee.id, employee.name, employee.type, employee.engineer_info FROM employee WHERE employee.type IN (?) ORDER BY employee.id [...] ('engineer',)
>>> for obj in objects: ... print(f"{obj}") Engineer('SpongeBob') Engineer('Squidward')

Optimierung von Attribut-Loads für Single Inheritance

Das Standardverhalten von Single-Inheritance-Abbildungen bezüglich der Auswahl von Attributen von Unterklassen ähnelt dem von Joined-Inheritance, da unterklassenspezifische Attribute standardmäßig immer noch eine zweite SELECT-Anweisung ausgeben. Im folgenden Beispiel wird ein einzelner Employee vom Typ Manager geladen. Da die angeforderte Klasse jedoch Employee ist, ist das Attribut Manager.manager_name standardmäßig nicht vorhanden, und eine zusätzliche SELECT-Abfrage wird ausgegeben, wenn darauf zugegriffen wird.

>>> mr_krabs = session.scalars(select(Employee).where(Employee.name == "Mr. Krabs")).one()
BEGIN (implicit) SELECT employee.id, employee.name, employee.type FROM employee WHERE employee.name = ? [...] ('Mr. Krabs',)
>>> mr_krabs.manager_name
SELECT employee.manager_name AS employee_manager_name FROM employee WHERE employee.id = ? AND employee.type IN (?) [...] (1, 'manager')
'Eugene H. Krabs'

Um dieses Verhalten zu ändern, gelten die gleichen allgemeinen Konzepte zur eageren Ladung dieser zusätzlichen Attribute, die bei der Joined-Inheritance-Ladung verwendet werden, auch für Single-Inheritance, einschließlich der Verwendung der selectin_polymorphic()-Option sowie der with_polymorphic()-Option. Letztere schließt einfach die zusätzlichen Spalten ein und ist aus SQL-Sicht effizienter für Single-Inheritance-Mapper.

>>> employees = with_polymorphic(Employee, "*")
>>> stmt = select(employees).order_by(employees.id)
>>> objects = session.scalars(stmt).all()
BEGIN (implicit) SELECT employee.id, employee.name, employee.type, employee.manager_name, employee.engineer_info FROM employee ORDER BY employee.id [...] ()
>>> for obj in objects: ... print(f"{obj}") Manager('Mr. Krabs') Engineer('SpongeBob') Engineer('Squidward') >>> objects[0].manager_name 'Eugene H. Krabs'

Da der Overhead beim Laden von Single-Inheritance-Unterklassenabbildungen normalerweise gering ist, wird empfohlen, dass Single-Inheritance-Abbildungen den Parameter Mapper.polymorphic_load mit der Einstellung "inline" für diejenigen Unterklassen enthalten, bei denen das Laden ihrer spezifischen Unterklassenattribute häufig erwartet wird. Ein Beispiel, das das Setup illustriert und modifiziert wurde, um diese Option aufzunehmen, ist unten aufgeführt.

>>> class Base(DeclarativeBase):
...     pass
>>> class Employee(Base):
...     __tablename__ = "employee"
...     id: Mapped[int] = mapped_column(primary_key=True)
...     name: Mapped[str]
...     type: Mapped[str]
...
...     def __repr__(self):
...         return f"{self.__class__.__name__}({self.name!r})"
...
...     __mapper_args__ = {
...         "polymorphic_identity": "employee",
...         "polymorphic_on": "type",
...     }
>>> class Manager(Employee):
...     manager_name: Mapped[str] = mapped_column(nullable=True)
...     __mapper_args__ = {
...         "polymorphic_identity": "manager",
...         "polymorphic_load": "inline",
...     }
>>> class Engineer(Employee):
...     engineer_info: Mapped[str] = mapped_column(nullable=True)
...     __mapper_args__ = {
...         "polymorphic_identity": "engineer",
...         "polymorphic_load": "inline",
...     }

Mit der obigen Abbildung werden die Klassen Manager und Engineer automatisch in SELECT-Anweisungen für die Entität Employee einbezogen.

>>> print(select(Employee))
SELECT employee.id, employee.name, employee.type, employee.manager_name, employee.engineer_info FROM employee

Inheritance Loading API

Objektname Beschreibung

selectin_polymorphic(base_cls, classes)

Gibt an, dass eine Eager-Ladung für alle Attribute erfolgen soll, die spezifisch für eine Unterklasse sind.

with_polymorphic(base, classes[, selectable, flat, ...])

Erzeugt ein AliasedClass-Konstrukt, das Spalten für Nachfahren-Mapper der gegebenen Basis angibt.

Funktion sqlalchemy.orm.with_polymorphic(base: Type[_O] | Mapper[_O], classes: Literal['*'] | Iterable[Type[Any]], selectable: Literal[False, None] | FromClause = False, flat: bool = False, polymorphic_on: ColumnElement[Any] | None = None, aliased: bool = False, innerjoin: bool = False, adapt_on_names: bool = False, name: str | None = None, _use_mapper_path: bool = False) AliasedClass[_O]

Erzeugt ein AliasedClass-Konstrukt, das Spalten für Nachfahren-Mapper der gegebenen Basis angibt.

Die Verwendung dieser Methode stellt sicher, dass die Tabellen jedes Nachfahren-Mappers in die FROM-Klausel aufgenommen werden und ermöglicht die Verwendung von filter()-Kriterien auf diese Tabellen. Die resultierenden Instanzen haben auch die Spalten bereits geladen, sodass kein "Post-Fetch" dieser Spalten erforderlich ist.

Siehe auch

Verwendung von with_polymorphic() - vollständige Diskussion von with_polymorphic().

Parameter:
  • base – Basisklasse, die aliast werden soll.

  • classes – eine einzelne Klasse oder ein Mapper, oder eine Liste von Klassen/Mappern, die von der Basisklasse erben. Alternativ kann es auch der String '*' sein, in welchem Fall alle absteigenden zugeordneten Klassen zur FROM-Klausel hinzugefügt werden.

  • aliased – wenn True, wird das Selektierbare aliast. Für einen JOIN bedeutet dies, dass der JOIN aus einer Subquery heraus SELECTiert wird, es sei denn, das Flag with_polymorphic.flat ist auf True gesetzt, was für einfachere Anwendungsfälle empfohlen wird.

  • flat – Boolean, wird an den FromClause.alias()-Aufruf übergeben, sodass Aliase von Join-Objekten die einzelnen Tabellen innerhalb des Joins aliastieren und nicht eine Subquery erstellen. Dies wird von allen modernen Datenbanken in Bezug auf rechtsverschachtelte Joins generell unterstützt und erzeugt generell effizientere Abfragen. Das Setzen dieses Flags wird empfohlen, solange der resultierende SQL funktionsfähig ist.

  • selectable

    eine Tabelle oder Subquery, die anstelle der generierten FROM-Klausel verwendet wird. Dieses Argument ist erforderlich, wenn eine der gewünschten Klassen Concrete Table Inheritance verwendet, da SQLAlchemy derzeit keine UNIONs zwischen Tabellen automatisch generieren kann. Wenn verwendet, muss das Argument selectable den vollständigen Satz von Tabellen und Spalten darstellen, die von jeder zugeordneten Klasse zugeordnet sind. Andernfalls führen die nicht berücksichtigten zugeordneten Spalten dazu, dass ihre Tabelle direkt an die FROM-Klausel angehängt wird, was normalerweise zu falschen Ergebnissen führt.

    Wenn der Standardwert von False beibehalten wird, wird das für den Basis-Mapper zugewiesene polymorphe Selektierbare zum Auswählen von Zeilen verwendet. Es kann jedoch auch als None übergeben werden, wodurch das konfigurierte polymorphe Selektierbare umgangen und stattdessen ein Ad-hoc-Selektierbares für die gegebenen Zielklassen erstellt wird; für Joined-Table-Inheritance ist dies ein Join, der alle Ziel-Mapper und ihre Unterklassen einschließt.

  • polymorphic_on – eine Spalte, die als "Diskriminator"-Spalte für das gegebene Selektierbare verwendet wird. Wenn nicht angegeben, wird das Attribut polymorphic_on des Mappers der Basisklassen verwendet, falls vorhanden. Dies ist nützlich für Abbildungen, die standardmäßig kein polymorphes Ladeverhalten aufweisen.

  • innerjoin – wenn True, wird ein INNER JOIN verwendet. Dies sollte nur angegeben werden, wenn nur nach einem spezifischen Untertyp abgefragt wird.

  • adapt_on_names

    Übergibt den Parameter aliased.adapt_on_names an das aliastierte Objekt. Dies kann in Situationen nützlich sein, in denen das gegebene Selektierbare nicht direkt mit dem vorhandenen zugeordneten Selektierbaren zusammenhängt.

    Neu in Version 1.4.33.

  • name

    Name, der dem generierten AliasedClass zugewiesen wird.

    Neu in Version 2.0.31.

Funktion sqlalchemy.orm.selectin_polymorphic(base_cls: _EntityType[Any], classes: Iterable[Type[Any]]) _AbstractLoad

Gibt an, dass eine Eager-Ladung für alle Attribute erfolgen soll, die spezifisch für eine Unterklasse sind.

Dies verwendet eine zusätzliche SELECT-Anweisung mit IN für alle übereinstimmenden Primärschlüsselwerte und ist das pro-Abfrage-Analogon zur Einstellung "selectin" für den Parameter mapper.polymorphic_load.

Neu seit Version 1.2.