30 Stimmen

Ist ORM immer noch das "Vietnam der Informatik"?

Ich lese diese Stelle und ich habe festgestellt, dass sie aus dem Jahr 2006 stammt. Ich könnte so oder so auf die ORM, Datenbank Sache gehen, aber ich war nur fragen, ob alles schlechte Jeff sagte über ORM immer noch gilt auch jetzt unter Berücksichtigung der Post ist von 2006.

3voto

Rob Bygrave Punkte 3481

Jeffs Artikel verlinkt zu Ted Newards Artikel . Wenn Sie sich für die Details interessieren, müssen Sie dort nachsehen:

  1. Original - http://blogs.tedneward.com/post/the-vietnam-of-computer-science/

  2. Nachbereitung - http://blogs.tedneward.com/post/thoughts-on-vietnam-commentary/

Von Teds ursprünglichen Punkten habe ich folgende:

  • 1 war falsch (Identität)
  • 2 davon gelöst (Teilobjekte und N + 1)
  • 2 sind umstritten (Duales Schema / Gemeinsames Schema).

Disclaimer: Ich bin der Autor von Ebean ORM, so dass ich für die verschiedenen "Lösungen" für die angesprochenen Probleme verweisen wird.

Teds ursprüngliche Punkte (destilliert, weil er sehr wortreich ist):

1. Problem des Teilobjekts.

Immer lösbar. Ebean ORM machte partielle Objekte fundemental zu seiner Abfragesprache und alle Interna. JPQL nicht machen dies eine Priorität, es ist mehr ein Problem dort leider.

2. N + 1 (Teds Last-Zeit-Paradoxon)

Immer lösbar. Sollte geschrieben worden sein als 1 + N / batchSize aber es ist interessanter als das (pro Pfad, müssen SQL Paging zu berücksichtigen, vermeiden sql kartesisches Produkt). Einige ORMs machen daraus leider ein richtiges Chaos und das bringt ORM im Allgemeinen in Verruf. Einige ORMs sind in Ordnung, bis man ein gewisses Maß an Komplexität erreicht (wie OneToMany innerhalb von OneToMany innerhalb von OneToMany).

Um hier noch einen draufzusetzen, können ORMs ein Profil des verwendeten Objektgraphen erstellen und die Abfrage automatisch zu optimieren (nur abrufen, was benötigt wird, Abrufpfade definieren, um für N + 1 zu optimieren usw.).

Diese Idee der automatischen ORM-Abfrageoptimierung stammt von der University of Texas (unter Verwendung von Hibernate). Sie wurde 2008 als Teil von Ebean ORM aufgenommen, ist also schon eine Weile in Gebrauch.

3. Identität

Ted spricht von einem Missverhältnis zwischen Identität und Gleichzeitigkeit. Dieser Punkt ist unangebracht, da ORMs (nun ja, alle, die ich kenne) diesen Aspekt auf folgende Weise angehen genau wie die früheren Client/Server-Tools und insbesondere die ORMs bieten eine SNAPSHOT Ansicht eines Teils der Datenbank für die Anwendung. Hier gab es nie ein Problem, aber ORM-Implementierungen konnten sich in Schwierigkeiten bringen, wenn sie sich beispielsweise zu sehr auf hashCode()/equals() verließen.

4. Problem des doppelten Schemas

Das ist umstritten. Wenn die Organisation dies zulässt, kann der ORM ein DIFF/SQL-Skript für das Schema bereitstellen, das von FlywayDB/Liquibase usw. ausgeführt wird. Wenn Organisationen dies nicht zulassen, könnte dies bis zu einem gewissen Grad immer noch ein Problem darstellen.

5. DB-Refactoring / Gemeinsames Schema

Dies ist umstritten. Leute, die an DB-Design/Normalisierung glauben, würden argumentieren, dass das DB-Design 4NF erreichen sollte, und das bedeutet, dass jedes Refactoring nur additiv sein sollte (Denormalisierung, Hinzufügen von Spalten/Tabellen) und keine brechenden Änderungen. Leute, die nicht an die Normalisierung glauben, werden sich über ein gemeinsames Schema den Kopf zerbrechen.

1voto

duffymo Punkte 298898

Ich glaube schon.

Ich denke, der letzte Satz ist der interessanteste von allen: "Ich neige dazu, mich auf die Seite des Datenbank-als-Modell-Lagers zu schlagen, weil ich denke, dass Objekte überbewertet werden." Java, C++ und C# sind sicherlich die dominierenden Sprachen, aber die funktionale Programmierung erlebt mit F#, Scala usw. ein Comeback.

1voto

Bill K Punkte 61074

Dies ist nicht mein Fachgebiet, aber ich arbeitete in Rails für etwa ein Jahr und ich denke, ActiveRecord gelöst die meisten der DB-Mapping-Problem. Ich weiß, es hat ein paar Probleme, aber ich denke, es hat einen fantastischen Job.

Ich glaube nicht, dass sein Beitrag die Möglichkeit berücksichtigt hat, dass das Framework selbst (in diesem Fall AcitveRecord/Rails) die Datenbank UND das Objektmodell definiert, was - soweit ich das beurteilen kann - das Problem beseitigt.

Da dies das Gegenteil der ersten beiden Antworten ist (im Wesentlichen, dass der Beitrag veraltet ist), habe ich das Gefühl, dass ich wahrscheinlich etwas nicht verstehe; wenn das der Fall ist, korrigieren Sie mich bitte, anstatt mich einfach abzuwählen, denn ich glaube, dass ich einen wichtigen Punkt übersehe.

1voto

IMHO sind monadische Ansätze wie der von Scala Glattes y Federkiel umgeht den oben erwähnten Sumpf weitgehend und bietet robustere Lösungen für viele von Teds Problemen (auch JOOQ verdient es, erwähnt zu werden). Sie sind zwar nicht perfekt, aber sie beseitigen definitiv das N+1- und das Partial Object-Problem, größtenteils das Identitätsproblem und teilweise das Dual Schema-Problem. Vergessen Sie die schwerfälligen und langatmigen CriteriaBuilder-Abfragen (hat jemand "Hinrichtung im Reich der Substantive" gelesen???) Die monadischen for-Verständnisse von Scala geben Ihnen eine einfache DSL, in der Sie Kriterienabfragen schreiben können:

case class Person(id: Int, name: String, age: Int)
case class Contact(personId: Int, phone: String)

val query = for {
    p <- query[Person] if(p.id == 999)
    c <- query[Contact] if(c.personId == p.id)
  } yield {
    (p.name, c.phone)
  }

Diese Art von Syntax bleibt auch bei komplexeren Abfragen, wie z. B. Teds Abfrage "is-acceptable-spouse", vernünftig:

case class Person(name:String, spouse:Option[PersonId]) {
    isThisAnAccpetableSpouse(person:Person) {...}
}

val query = for {
    p1 <- people
    p2 <- people if (
        p1.spouse.isEmpty && p2.spouse.isEmpty 
        && p1.isThisAnAccpetableSpouse(p2)
        && p1.isThisAnAccpetableSpouse(p1))
} yield (p1, p2)

All dies wird in einer einzigen SELECT-Abfrage zusammengefasst und in der Datenbank ausgeführt. Einfügungen, Aktualisierungen und Löschungen sind ähnlich.

0voto

Weifen Luo Punkte 506

Offenlegung: Ich bin der Autor von RDO.Net .

Ja. Ich glaube, dass die bestehenden ORMs in die falsche Richtung gehen - sie versuchen, relationale Daten auf beliebige Objekte abzubilden, was IMHO einfach nicht möglich ist. In der OO-Welt ist ein beliebiges Objekt nicht serialisierungs-/deserialisierungsfreundlich, da jedes Objekt eine Objektreferenz (die Adresse) hat, die lokal im aktuellen Prozess ist. Das ist in Ordnung, wenn man es mit einem einzelnen Objekt zu tun hat, aber man wird Probleme bekommen, wenn man es mit einem komplexen Objektgraphen zu tun hat.

Es bleibt zu hoffen, dass dieses Problem mit einem anderen Ansatz gelöst werden kann, wie in RDO.Net : Anstatt relationale Daten in beliebige Objekte abzubilden, bilden wir das Schema der Daten als umfangreiche Metadaten ab ( Model die Spalten, Primär-/Fremdschlüssel, Validierungen usw. enthält), und stellen Sie konkrete Db , DbTable , DbQuery y DataSet für den Datenzugriff. Bei der Durchführung von Datenbank-CRUD-Operationen konstruieren Sie den abstrakten SQL-Syntaxbaum (AST) explizit mit einer stark typisierten OO-Sprache, wie im folgenden C#-Beispielcode gezeigt wird, der eine Abfrage erstellt und ein DataSet für hierarchische Daten zurückgibt:

public async Task<DataSet<SalesOrderInfo>> GetSalesOrderInfoAsync(_Int32 salesOrderID, CancellationToken ct = default(CancellationToken))
{
    var result = CreateQuery((DbQueryBuilder builder, SalesOrderInfo _) =>
    {
        builder.From(SalesOrderHeader, out var o)
            .LeftJoin(Customer, o.FK_Customer, out var c)
            .LeftJoin(Address, o.FK_ShipToAddress, out var shipTo)
            .LeftJoin(Address, o.FK_BillToAddress, out var billTo)
            .AutoSelect()
            .AutoSelect(c, _.Customer)
            .AutoSelect(shipTo, _.ShipToAddress)
            .AutoSelect(billTo, _.BillToAddress)
            .Where(o.SalesOrderID == salesOrderID);
    });

    await result.CreateChildAsync(_ => _.SalesOrderDetails, (DbQueryBuilder builder, SalesOrderInfoDetail _) =>
    {
        builder.From(SalesOrderDetail, out var d)
            .LeftJoin(Product, d.FK_Product, out var p)
            .AutoSelect()
            .AutoSelect(p, _.Product)
            .OrderBy(d.SalesOrderDetailID);
    }, ct);

    return await result.ToDataSetAsync(ct);
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X