[JAVA] Schrittweises Verständnis der O / R-Zuordnung

Einführung

Was ist O / R-Zuordnung?

Die O / R-Zuordnung ist eine Möglichkeit, relationale Datenbankeinträge als reguläre Objekte in einer objektorientierten Programmiersprache zu bearbeiten. Es ist einfacher, den tatsächlichen Code zu sehen, als eine detailliertere Definition zu geben. Im Folgenden finden Sie Beispiele für die Verwendung der JDBC-API auf niedriger Ebene und von JPA, die für das O / R-Mapping-Framework auf hoher Ebene repräsentativ sind.

public List<Issue> findByProjectId(long projectId) {
  String query = "select id, title, description from issue where project_id = ?";
  try (PreparedStatement ps = connection.prepareStatement(query)) {
    ps.setLong(1, projectId);
    List<Issue> issues = new ArrayList<>();
    try (ResultSet rs = ps.executeQuery()) {
      while (rs.next()) {
        Issue issue = new Issue();
        issue.setId(rs.getLong("id"));
        issue.setTitle(rs.getString("title"));
        issue.setDescription(rs.getString("description"));
        issues.add(issue);
      }
    }
    return issues;
  } catch (SQLException e) {
    throw new RuntimeException(e);
  }
}
public List<Issue> findByProjectId(long projectId) {
  String query = "select i from Issue i where i.project.id = ?1";
  List<Issue> issues = entityManager.createQuery(query, Issue.class)
      .setParameter(1, projectId).getResultList();
  return issues;
}

Der Unterschied wird beim Vergleich der beiden deutlich. Letzteres, das eine O / R-Zuordnung auf hoher Ebene verwendet, macht Routinebeschreibungen überflüssig und drückt die Absicht klarer aus.

Missverstandene O / R-Zuordnung

Das O / R-Mapping sieht im obigen Beispiel gut aus, aber es gibt weltweit viele Beschwerden. Einige sind radikal, z. B. das Ablehnen aller O / R-Zuordnungen, während andere O / R-Zuordnungs-Frameworks auf hoher Ebene ablehnen und die Verwendung einfacherer Alternativen bevorzugen. In einer solchen Situation scheint die Position, die Verwendung eines O / R-Mapping-Frameworks auf hoher Ebene aktiv zu fördern, eher eine Minderheit zu sein.

Es gibt zwei Hauptgründe, warum die O / R-Zuordnung nicht gemocht wird. Der erste Grund wäre, dass O / R-Mapping-Frameworks auf hoher Ebene nicht auf Programmierer zu hören scheinen. Viele Leute haben Erfahrung mit dem O / R-Mapping-Framework, die Schwierigkeiten hatten, mit Leistungsproblemen umzugehen, weil SQL nicht wie beabsichtigt unter der Haube ausgeführt wurde. Dies scheint ein Missverständnis über den grundlegenden Mechanismus der O / R-Zuordnung zu sein. Der zweite Grund kann sein, dass O / R-Mapping auf hoher Ebene häufig in Projekten verwendet wird, die dafür nicht geeignet sind. Wie wir später genauer sehen werden, ist die Verwendung eines O / R-Mapping-Frameworks auf hoher Ebene in Situationen, in denen Voraussetzungen wie die Diskretion des Schemas nicht erfüllt sind, dem Selbstmord nahe. Dies scheint ein Missverständnis hinsichtlich der Kriterien für die ordnungsgemäße Verwendung der O / R-Zuordnung zu sein.

Zusammenfassung dieses Artikels

In diesem Artikel werde ich zeigen, dass es mehrere Ebenen der O / R-Zuordnung gibt, um das obige Missverständnis zu beheben. Durch die schrittweise Betrachtung der Mittelwerte auf niedriger bis hoher Ebene, welche Art von Problem hat sich der grundlegende Mechanismus jeder Ebene als Lösung herausgestellt, und welche Kriterien sollten für jede Mittelstufe verwendet werden. Sie sollten verstehen, was zu tun ist.

5 Ebenen

In diesem Artikel werde ich die O / R-Zuordnung in fünf Ebenen erläutern:

Diese Pegeleinstellungen dienen nur zur Vereinfachung der Erklärung. Die Funktionalität verschiedener Java O / R-Mapping-Frameworks überschneidet sich tatsächlich auf mehreren Ebenen. Außerdem wird der Kürze halber der Zielverarbeitungstyp auf das Referenzsystem eingegrenzt und das Aktualisierungssystem weggelassen.

Da sich das Thema mit verschiedenen tatsächlichen Java-Frameworks befasst, sollte es als kurze Einführung in diese gelesen werden. Es ist jedoch nicht beabsichtigt, eine erschöpfende Erklärung der Funktion zu geben. Wenn Sie mehr wissen möchten, lesen Sie bitte das offizielle Dokument, auf das verwiesen wird.

Stufe 1: Low-Level-API

Schauen wir uns zunächst den Datenzugriff mit der in JDK integrierten JDBC-API an. Als Beispiel werde ich den am Anfang dieses Artikels angegebenen Code erneut drucken.

public List<Issue> findByProjectId(long projectId) {
  String query = "select id, title, description from issue where project_id = ?";
  try (PreparedStatement ps = connection.prepareStatement(query)) {
    ps.setLong(1, projectId);
    List<Issue> issues = new ArrayList<>();
    try (ResultSet rs = ps.executeQuery()) {
      while (rs.next()) {
        Issue issue = new Issue();
        issue.setId(rs.getLong("id"));
        issue.setTitle(rs.getString("title"));
        issue.setDescription(rs.getString("description"));
        issues.add(issue);
      }
    }
    return issues;
  } catch (SQLException e) {
    throw new RuntimeException(e);
  }
}

Das Thema ist eine einfache Task-Management-Anwendung. Der Verarbeitungsinhalt des obigen Codes ist die Extraktion anhand der Projekt-ID des Problems.

Die Definition der "Issue" -Tabelle sieht folgendermaßen aus:

create table issue
(
   id bigint primary key,
   project_id bigint,
   title varchar (100),
   description text
);

Wie benutzt man

Was Sie tun müssen, um diesen Ansatz zu verwenden, ist:

Aufgabe

Die Komplexität der Routinebeschreibung ist ein Problem. Für Code-Schreiber ist es zu viel, für Code zu schreiben, der nur Abfragen ausführt und Ergebnisse abruft. Selbst auf der Seite des Lesens des Codes ist die Absicht im zusätzlichen Code vergraben und es ist schwer zu verstehen.

Darüber hinaus können Ressourcenmanagementrisiken nicht übersehen werden. Wenn Sie den Abschlussprozess vergessen, der im obigen Beispiel durch die Verwendung von Try-with-Resources unterstützt wird, tritt ein Ressourcenleck auf.

Auswahlkriterium

Ab 2019 gibt es im Produktionscode nur wenige Fälle, in denen dieser Ansatz angewendet werden sollte. Möglicherweise haben Sie die Möglichkeit, diesen Ansatz nur dann zu verwenden, wenn Sie äußerst leistungsbewusst sind oder wenn aus irgendeinem Grund Ihre Verwendung des Frameworks eingeschränkt ist. Selbst in diesen Fällen ist es jedoch einfach, die Methode, die der später beschriebenen Stufe 2 entspricht, selbst zu realisieren.

Stufe 2: Abstraktion vor und nach der Verarbeitung

Von den Standardbeschreibungen der Stufe 1 können diejenigen, die sich auf die Vorverarbeitung und Nachbearbeitung beziehen, relativ leicht abstrahiert werden. Das folgende Beispiel zeigt Jdbi.

public List<Issue> findByProjectId(long projectId) {
  String query = "select id, title, description from issue where project_id = ?";
  List<Issue> issues = handle.createQuery(query).bind(0, projectId)
      .map((rs, ctx) -> {
        Issue issue = new Issue();
        issue.setId(rs.getLong("id"));
        issue.setTitle(rs.getString("title"));
        issue.setDescription(rs.getString("description"));
        return issue;
      }).list();
  return issues;
}

Wie benutzt man

Was Sie tun müssen, um diesen Ansatz zu verwenden, ist: Es ist im Vergleich zu Level 1 deutlich reduziert.

Aufgabe

Obwohl die Vor- und Nachbearbeitung abstrahiert wurde, ist das Nachfüllen von Datensätzen in Objekte immer noch umständlich. Da der obige Code nur ein Beispiel ist, ist die Anzahl der Spalten begrenzt. In einem tatsächlichen Projekt ist jedoch für viele Spalten eine Standardbeschreibung erforderlich.

Typisches Java-Framework

Es gibt kein Framework für diese Ebene, aber Jdbi und Spring JdbcTemplate Frameworks mit Level 3-Funktionalität, wie z. B. /spring-framework-reference/data-access.html#jdbc-JdbcTemplate), verfügen ebenfalls über Level 2-Funktionalität.

Wie in Stufe 1 „Auswahlkriterien“ erwähnt, ist es auch einfach, ein Framework dieser Stufe selbst zu erstellen. Es sollte eine gute Übungsplattform sein, um sich mit Lambda vertraut zu machen.

Auswahlkriterium

Es gibt nicht viele Situationen, in denen diese Methode angewendet werden sollte. Wie bei Level 1 ist dies eine Option, wenn die Leistung äußerst wichtig ist oder wenn aus irgendeinem Grund die Verwendung des Frameworks eingeschränkt ist.

Wenn sich die Struktur von Datensätzen und Objekten erheblich unterscheidet und Sie flexible Zuordnungsprozesse manuell schreiben müssen, können Sie es auch wagen, auf dieser Ebene statt auf Ebene 3 zu bleiben.

Stufe 3: Abfrage und einfache Objektzuordnung

Stufe 3 automatisiert das Nachfüllen von Datensätzen in Objekte, die in Stufe 2 manuell unterstützt wurden. Das folgende Beispiel verwendet eine andere API von Jdbi, die mit Level 2 identisch ist.

public List<Issue> findByProjectId(long projectId) {
  handle.registerRowMapper(BeanMapper.factory(Issue.class));
  String query = "select id, title, description from issue where project_id = ?";
  List<Issue> issues = handle.createQuery(query).bind(0, projectId)
      .mapTo(Issue.class).list();
  return issues;
}

Wie benutzt man

Was Sie tun müssen, um diesen Ansatz zu verwenden, ist:

Aufgabe

Auf den ersten Blick mag diese Herangehensweise vielseitig erscheinen, aber das Fehlen einer zugehörigen Navigation von Objekten ist ein schwerwiegender Fehler. Die eigentliche Anwendung besteht aus mehreren Tabellen. Zum Beispiel sollte im Fall der einfachen Issue-Management-Anwendung, die Gegenstand dieses Artikels ist, zusätzlich zur "Issue" -Tabelle eine Viele-zu-Eins-verwandte "Projekt" -Tabelle und eine Eins-zu-Viele-Verwandte "Kommentar" -Tabelle vorhanden sein. Für objektorientiertes Denken ist es natürlich, dass auf solche Daten als verwandte Objekte mit Methoden wie "Issue # getProject ()" und "Issue # getComments ()" zugegriffen werden kann. Eine solche verwandte Navigation kann mit diesem Ansatz jedoch nicht erreicht werden.

Auf dieser technischen Ebene können Sie nur ein einzelnes Objekt oder eine Liste von Objekten abrufen (zweidimensionale Tabellenstruktur). Wenn Sie einen Datenzugriff erzielen möchten, der der zugehörigen Navigation entspricht, können Sie entweder die Logik schreiben, um sie in separaten Abfragen abzurufen und selbst zusammenzuführen, oder Sie können sie als ein JOIN-Objekt erzwingen.

Unter diesen Bedingungen ist die Realisierung einer Architektur, die ein umfangreiches Domänenmodell wie domänengesteuertes Design voraussetzt, hoffnungslos. Infolgedessen vermehren sich einzelne Modelle, die durch die bequeme Anzeige jedes Bildschirms in Mitleidenschaft gezogen werden, was zu einer bildschirmzentrierten Anwendung und nicht zu einer domänenzentrierten führt. Es gibt keine Möglichkeit, die mit dieser Level-Methode erworbenen Objekte selbst in ein umfangreiches Domänenmodell zu füllen, aber es ist oft billiger, die Level 4-5-Methode gehorsam zu lernen, als solch eine mühsame Sache zu tun. Du solltest fertig sein.

Darüber hinaus ist die verwandte Navigation in Benutzeroberflächen wie OOUI, die Benutzern eine kostenlose Interaktion bieten, fast eine unverzichtbare Funktion. Modelle ohne zugehörige Navigation können möglicherweise die Realisierung einer benutzerfreundlichen Benutzeroberfläche behindern.

Typisches Java-Framework

Typische Frameworks auf dieser Ebene sind Jdbi und [Spring JdbcTemplate](https://docs.spring.io/spring-framework/docs/current/spring- Framework-Referenz / Datenzugriff.html # jdbc-JdbcTemplate). Genau genommen können beide Datenzugriffe verarbeiten, die Level 4 entsprechen, wenn Sie Ihr Bestes geben (obwohl Beispiel Wie Sie an zu vielen Beziehungen mit jdbi-sql-object-api sehen können, ist es kompliziert. Es gibt auch viele andere Optionen, wie sql2o und die nostalgischen Commons DbUtils.

Außerdem unterstützt Doma, das als einfaches O / R-Mapping-Framework Unterstützung erhalten hat, nur bis zu Stufe 3 für Referenzsysteme. Nicht. Dies ist hartnäckig [Unterstützt keine verwandte Navigation als Designkonzept](https://doma.readthedocs.io/en/stable/faq/#does-doma-map-database-relationships-such-as-one -zu-eins-und-eins-zu-viele-zu-Java-Objekten) wird angegeben.

Auswahlkriterium

Es sollte Situationen geben, in denen diese Vorgehensweise ausreichend ist, z. B. eine Anwendung mit einer Standardbenutzeroberfläche oder ein Stapel mit einfachem Datenzugriff. Hoffen wir, dass es keine übliche Entwicklung sein wird, dass die tatsächlichen Anforderungen entgegen der ursprünglichen Annahme weder Standard noch einfach waren.

Stufe 4: Zuordnen von Abfragen und zugehörigen navigierbaren Objekten

Schauen wir uns ein Beispiel für die Implementierung mit MyBatis für eine verwandte Navigation an, die auf Stufe 3 nicht erreicht werden konnte.

Erstens ist die Definition der "Projekt" -Kommentartabelle, die der "Issue" -Tabelle zugeordnet ist, wie folgt.

create table project
(
   id bigint primary key,
   name varchar (100)
);
create table comment
(
   id bigint primary key,
   issue_id bigint,
   description text
);

Als nächstes wird die zuzuordnende Java-Klasse unten gezeigt.

@Data
public class Issue {
	private long id;
	private Project project;
	private List<Comment> comments;
	private String title;
	private String description;
}
@Data
public class Project {
	private long id;
	private String name;
}
@Data
public class Comment {
	private long id;
	private String description;
}

Schreiben Sie außerdem Einstellungen, um diese zuzuordnen. Hier wird die XML-basierte Methode verwendet (Beachten Sie, dass MyBatis auch die Annotation-basierte Methode hat. //mybatis.org/mybatis-3/ja/java-api.html#Mapper_.E3.82.A2.E3.83.8E.E3.83.86.E3.83.BC.E3.82.B7.E3. Es gibt auch 83.A7.E3.83.B3)).

<resultMap id="issueResult" type="Issue" autoMapping="true">
  <id property="id" column="id" />
  <association property="project" column="project_id"
    select="Project.find" />
  <collection property="comments" column="id"
    select="Comment.findByIssueId" />
</resultMap>
<select id="findByProjectId" parameterType="long"
  resultMap="issueResult">
  <![CDATA[
    select
      id,
      project_id,
      title,
      description
    from
      issue
    where
      project_id = #{projectId}
  ]]>
</select>
<resultMap id="projectResult" type="Project"
  autoMapping="true">
  <id property="id" column="id" />
</resultMap>
<select id="find" parameterType="long"
  resultMap="projectResult">
  <![CDATA[
    select
      id,
      name
    from
      project
    where
      id = #{id}
  ]]>
</select>
<resultMap id="commentResult" type="Comment"
  autoMapping="true">
  <id property="id" column="id" />
</resultMap>
<select id="findByIssueId" parameterType="long"
  resultMap="commentResult">
  <![CDATA[
    select
      id,
      description
    from
      comment
    where
      issue_id = #{issueId}
  ]]>
</select>

Schließlich wird der Datenzugriff basierend auf den obigen Einstellungen durchgeführt.

public List<Issue> findByProjectId(long projectId) {
  return sqlSession.selectList("Issue.findByProjectId", projectId);
}

Wie benutzt man

Was Sie tun müssen, um diesen Ansatz zu verwenden, ist:

Aufgabe

Jetzt, da die entsprechende Navigation möglich ist, ist die Einrichtung nicht einfach, wie Sie dem Beispiel entnehmen können. Wenn die Arten von Referenzabfragen zunehmen oder wenn die Verarbeitung zum Einfügen / Aktualisieren / Löschen von Updates erforderlich ist, muss SQL jedes Mal manuell geschrieben werden. Wie später beschrieben wird, gibt es Gegenmaßnahmen durch automatische Erzeugung, aber der Effekt ist begrenzt.

Es gibt auch eine Schwäche in der Abstraktion der Routineverarbeitung. Diese Ebene ist für die Routineverarbeitung vorgesehen, die mit Methoden der Ebene 5 leicht abstrahiert werden kann, z. B. automatische Eingabe von Überwachungsspalten (Registrierungsdatum und -zeit, Aktualisierungsdatum und -zeit, registrierter Benutzer, Aktualisierungsbenutzer, ...) und optimistisches Sperren nach Versionsnummer. Bei dieser Methode müssen Sie SQL jedes Mal manuell schreiben.

Darüber hinaus besteht als Problem, das bisher allen Ebenen gemeinsam ist, eine Abhängigkeit von einem bestimmten DBMS, solange SQL manuell geschrieben wird. Es ist schwierig, alle Anforderungen mit standardkonformem SQL zu erfüllen, das die Portabilität berücksichtigt. Es gibt nur wenige Möglichkeiten, sich dieses Problems in der täglichen Entwicklung bewusst zu werden, aber wenn die Geschichte einer umfassenden Renovierung wie dem Austausch von Systemen auftaucht, wird der Ernst der Lage sofort offensichtlich.

Beachten Sie, dass das N + 1-Problem, das auf Stufe 5 wütet, auch auf dieser Stufe auftreten kann. Diese Herangehensweise funktioniert jedoch nur, wenn Sie SQL geschrieben haben. Sie sind also dafür verantwortlich, nicht für das Framework, und es ist klar, wie Sie damit umgehen sollen. Im obigen Beispiel tritt beispielsweise das N + 1-Problem tatsächlich auf, das Problem kann jedoch gelöst werden, indem SQL durch das mit JOIN verwendete Problem ersetzt wird (siehe unten).

<resultMap id="issueResultWithProjectAndComments"
  type="Issue" autoMapping="true">
  <id property="id" column="id" />
  <association property="project" columnPrefix="p_"
    resultMap="Project.projectResult" />
  <collection property="comments" columnPrefix="c_"
    resultMap="Comment.commentResult" />
</resultMap>
<select id="findByProjectIdWithProjectAndComments"
  parameterType="long" resultMap="issueResultWithProjectAndComments">
  <![CDATA[
    select
      i.id as id,
      i.project_id as project_id,
      i.title as title,
      i.description as description,
      p.id as p_id,
      p.name as p_name,
      c.id as c_id,
      c.description as c_description
    from
      issue i
      inner join project p on i.project_id = p.id
      left outer join comment c on i.id = c.issue_id
    where
      project_id = #{projectId}
  ]]>
</select>

Hilfsmechanismus

Diese Herangehensweise wird von folgenden Mechanismen begleitet:

Typisches Java-Framework

Ein typisches Framework auf dieser Ebene ist MyBatis, das im Beispiel erwähnt wird. Aufgrund seiner langen Geschichte verfügt es über alle Funktionen, die unter "Angehängte Mechanismen" aufgeführt sind.

Darüber hinaus ist JPA, das auf Ebene 5 angezeigt wird, Teil von Native Query. /5.4/userguide/html_single/Hibernate_User_Guide.html#sql) kann als Level 4-Technik betrachtet werden. Im Vergleich zu MyBatis gibt es jedoch einen Unterschied in der Benutzerfreundlichkeit der Grundfunktionen und der Unterstützung der unter "Angehängter Mechanismus" aufgeführten Funktionen.

Auswahlkriterium

Datenbankfesseln können ein Grund sein, sich für einen Level 4-Ansatz zu entscheiden. "Großvater" ist beispielsweise ein Legacy-Schema, das nicht geändert werden kann, eine Datenstruktur, die nicht mit den Anforderungen der Benutzeroberfläche übereinstimmt, und eine Anforderung für die Umleitung von vorhandenem SQL.

Angesichts eines umfangreichen Domänenmodells mit domänengesteuertem Design ist ein Level 4-Ansatz, der Level 5 in Bezug auf die Flexibilität der Zuordnungskonfiguration überlegen ist, eine praktikable Option.

Unter Berücksichtigung der Leistungsanforderungen und der Fähigkeiten der Teammitglieder können Level 4-Ansätze als risikoarme Alternative mit geringer Rendite zu Level 5-Leistungsrisiken gewählt werden.

Stufe 5: Zuordnung von Tabelle zu Objekt

Auf Stufe 4 habe ich SQL jedes Mal manuell geschrieben, aber in erster Linie ist die Struktur von Tabellen und Objekten in allgemeinen Anwendungen in vielerlei Hinsicht ähnlich. Wenn Sie sie also gut zuordnen können, sollten Sie in der Lage sein, Arbeit zu sparen. Schauen wir uns unten ein Beispiel mit Hibernate ORM an, das dem Standard JPA entspricht. ..

Das Datenbankschema entspricht dem bis zu Stufe 4. Die zuzuordnende Java-Klasse ist dieselbe wie Level 4, jedoch wird die Anmerkung für die Zuordnungseinstellung hinzugefügt.

@Data
@Entity
public class Issue {
	@Id
	private long id;
	@ManyToOne
	@JoinColumn(name = "project_id")
	private Project project;
	@OneToMany
	@JoinColumn(name = "issue_id")
	private List<Comment> comments;
	private String title;
	private String description;
}
@Data
@Entity
public class Project {
	@Id
	private long id;
	private String name;
}
@Data
@Entity
public class Comment {
	@Id
	private long id;
	private String description;
}

Führen Sie den Datenzugriff auf der Grundlage der oben genannten Schritte durch.

public List<Issue> findByProjectId(long projectId) {
  String query = "select i from Issue i where i.project.id = ?1";
  List<Issue> issues = entityManager.createQuery(query, Issue.class)
      .setParameter(1, projectId).getResultList();
  return issues;
}

Wie benutzt man

Die Verwendung dieser Level-Methode ist wie folgt.

--Stellen Sie die Zuordnung von Tabellenobjekten ein

Aufgabe

Eine große Herausforderung für diesen Ansatz ist die Leistungsverschlechterung aufgrund einer unbeabsichtigten SQL-Ausgabe, von der das N + 1-Problem am typischsten ist. Das N + 1-Problem besteht darin, dass N Datensätze, die in einer einzelnen Abfrage an die Haupttabelle zurückgegeben werden, N-mal an die zugehörige Tabelle abgefragt werden. Basierend auf der Issue-Management-Anwendung, die Gegenstand dieses Artikels ist, wird im Datenzugriff des Issue-Listenbildschirms nach einer einzelnen Abfrage der Issue-Tabelle `Issue # getComments () in einer Schleife ausgeführt. Wenn "jedes Mal" aufgerufen wird, wird die "Kommentar" -Tabelle N-mal abgefragt, was der Anzahl der "Issue" -Datensätze entspricht, die in der vorherigen Abfrage erhalten wurden.

Eine der Hauptlösungen für das N + 1-Problem in JPA ist FETCH JOIN. Ist. Im obigen Beispiel tritt beispielsweise das N + 1-Problem tatsächlich auf, das Problem kann jedoch unterdrückt werden, indem die Abfrage durch die Abfrage ersetzt wird, die FETCH JOIN verwendet (siehe unten).

public List<Issue> findByProjectIdWithProjectAndComments(long projectId) {
  String query = "select distinct i from Issue i join fetch i.project"
      + " left join fetch i.comments where i.project.id = ?1";
  List<Issue> issues = entityManager.createQuery(query, Issue.class)
      .setParameter(1, projectId).getResultList();
  return issues;
}

Die tatsächlich generierte SQL-Abfrage sieht folgendermaßen aus:

select
    distinct issue0_.id as id1_1_0_,
    project1_.id as id1_2_1_,
    comments2_.id as id1_0_2_,
    issue0_.description as descript2_1_0_,
    issue0_.project_id as project_4_1_0_,
    issue0_.title as title3_1_0_,
    project1_.name as name2_2_1_,
    comments2_.description as descript2_0_2_,
    comments2_.issue_id as issue_id3_0_0__,
    comments2_.id as id1_0_0__
from
    issue issue0_
inner join
    project project1_
        on issue0_.project_id=project1_.id
left outer join
    comment comments2_
        on issue0_.id=comments2_.issue_id
where
    issue0_.project_id=?

Zusätzlich zu FETCH JOIN wird @Fetch (FetchMode.SUBSELECT) verwendet Oder Sie können [Entity Graph] verwenden (https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#fetching-strategies-dynamic-fetching-entity-graph) .. Außerdem @Where und @Filter In einigen Fällen ist eine fein abgestimmte Steuerung erforderlich (z. B. /hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#pc-filter).

Es gibt verschiedene Leistungsmaßstäbe, wie oben beschrieben, aber die Tatsache, dass die anfänglichen Lernkosten nicht gering sind, ist eines der Hauptprobleme bei der Einführung von Stufe 5. Möglicherweise werden solche Kosten in vielen Situationen nicht bewusst im Voraus bezahlt, und die daraus resultierenden Leistungsprobleme werden vage dem Framework zugeschrieben. Wenn Analysetools wie Hypersistence Optimizer verwendet werden können, ist zu erwarten, dass die anfänglichen Lernkosten in gewissem Maße gesenkt werden.

Es gibt auch das Problem von Mängeln im Standard JPA. Zum Beispiel implementiert im Verhalten von Entity Graph oben. Es gibt einige Abhängigkeiten sowie @Where und [@Filter](https: // docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#pc-filter) ist eine nicht standardisierte, von der Implementierung des Ruhezustands abhängige Funktion.

Darüber hinaus gibt es als Einschränkung in erster Linie einen Punkt, an dem der Effekt in einem Projekt nicht zu erwarten ist, in dem die strukturelle Ähnlichkeit zwischen der Tabelle und dem Objekt, die die Voraussetzung von Stufe 5 darstellt, hinsichtlich der Anforderungen gering ist. In solchen Fällen ist die Methode der Stufe 3-4, mit der Sie Abfragen frei schreiben können, besser geeignet.

Hilfsmechanismus

Diese Herangehensweise wird von folgenden Mechanismen begleitet:

--Lebenszyklusverwaltung

Typisches Java-Framework

Ein typisches Framework auf dieser Ebene ist der Standard JPA. Zusätzlich zu dem in diesem Artikel behandelten Hibernate ORM ist das JPA-Implementierungssystem EclipseLink. Es gibt auch Eclipselink /). Welche Implementierung Sie auswählen, hängt von der Standardeinstellung für Ihren Anwendungsserver und das übergeordnete Framework ab.

Darüber hinaus wurden EBean und jOOQ in "Attached Mechanism" sowie [Reladomo](https: // github) erwähnt. Es gibt Alternativen wie .com / goldmansachs / reladomo). Angesichts der Tatsache, dass sie alle nicht dem Standard entsprechen und besonders bei jOOQ und Reladomo süchtig machen, sollten Sie bei Ihren Entscheidungen vorsichtig sein.

Auswahlkriterium

Wie in der Aufgabe erwähnt, gibt es bei Verwendung dieses Ansatzes strenge Voraussetzungen für anfängliche Lernkosten und Schemata. Hohe Produktivitätsrenditen sind zu erwarten, wenn die Voraussetzungen erfüllt sind. Andernfalls kann es zu einer Verschwendung von Arbeitskräften kommen.

Wenn einige Einschränkungen die Einhaltung von Standards erzwingen, können Sie Level 3-4 überspringen und nur Level 5 JPA haben (wie oben erwähnt, können Sie die Native Query-Funktion verwenden, die Level 4 entspricht).

So wählen Sie die beste Methode für Ihr Projekt aus

Nun, welche Methodenebene in welcher Situation ausgewählt werden soll, ist mit der Beschreibung der "Auswahlkriterien" jeder Ebene etwas dupliziert, aber lassen Sie uns einen kurzen Blick zurück werfen.

Erstens ist es selten, mit Level 1-2 zu beginnen. Ob Level 3 ausreicht oder Level 4-5 ist oft die erste Entscheidung.

Level 3-Techniken können ausreichen, wenn die Projektkomplexität gering ist. Es gibt jedoch keinen einzigen leicht verständlichen Index zur Komplexität, und es ist erforderlich, eine umfassende Beurteilung vorzunehmen, die auf den Merkmalen der Benutzeroberfläche, den Merkmalen des Datenzugriffs, dem Umfang des Projekts und der Personalzusammensetzung des Teams basiert.

Wenn Ihr Projekt sehr komplex ist, wählen Sie aus den Ebenen 4-5. Die Level 4-Methode ist wahrscheinlich geeignet, wenn das Schema ein Legacy ist oder die Zuordnung entwickelt werden muss und eine geringe Unsicherheit wichtig ist, andernfalls ist die Level 5-Methode geeignet.

Es ist zu beachten, dass die Tatsache, dass die derzeitige Methode der Stufe 5 noch nicht abgeschlossen wurde, das Problem kompliziert. Wenn es Verbesserungen gibt, z. B. die Option, das Lebenszyklusmanagement mit hohen anfänglichen Lernkosten optional zu gestalten, die zugehörigen Navigationssteuerungsspezifikationen zu vereinfachen und in den Standard aufzunehmen und sie bei Bedarf mit umfangreichen Level 4-Techniken zu kombinieren. Es sollte mehr Möglichkeiten geben, eine Methode der Stufe 5 zu wählen.

(Nebenbei) Auf welcher Ebene befindet sich "O / R-Zuordnung"?

Es ist nicht offensichtlich, von welcher Ebene es als "O / R-Zuordnung" bezeichnet werden sollte. Es wäre nicht zu beanstanden, das O / R-Mapping des Level 5-Ansatzes aufzurufen. Vielleicht scheint auch die Level 4-Methode mit dem Präfix "breit definiert" wenig Einwände gegen die Bezeichnung O / R-Mapping zu haben. Für Level 3-Methoden gibt es jedoch Fälle wie JDBC (etwas, das sich von der O / R-Zuordnung unterscheidet) (http://jdbi.org/#_introduction_to_jdbi) und Doma in der Vergangenheit. In einigen Fällen als O / R-Mapper proklamiert. Selbst für die Methoden, die ursprünglich zu Level 1-2 gehören, ist die Schnittstelle der Datenzugriffskomponente technisch dieselbe wie Level 3-5 (Repositor, der von oben betrachtet wie eine O / R-Zuordnung aussieht). Kann vorgesehen sein.

Es kann mehrere Positionen bezüglich der Ebene der "O / R-Zuordnung" geben, wie folgt. Ich denke nicht, dass es eine sehr produktive Debatte darüber sein wird, welche Position richtig ist, also werde ich nicht darauf eingehen.

abschließend

Wie oben erwähnt, sind die Ebenen in Bezug auf die O / R-Zuordnung in 5 Ebenen unterteilt, und es wird gezeigt, dass jede ihre eigenen Unvermeidlichkeits- und Verwendungskriterien hat. Wir hoffen, dass dieser Artikel Missverständnisse über O / R-Mapping und unglückliche Fehlanpassungen bei der Auswahl elementarer Technologien am Entwicklungsstandort verringert.

Recommended Posts

Schrittweises Verständnis der O / R-Zuordnung
Schrittweises Verständnis der Behandlung von Java-Ausnahmen
Berücksichtigung von GIS und O / R Mapper
Flexibler Datentypkonvertierungsmechanismus der O / R-Zuordnungsbibliothek Lightsleep für Java 8
Ich habe es verwendet, ohne die O / R-Zuordnung der Schienen zu kennen, also habe ich es überprüft.
[Java] Anfängerverständnis von Servlet-②
[Java] Anfängerverständnis von Servlet-①
Memorandum über LOD.