機能からリレーショナルへのマッピングは、オブジェクトからリレーショナルへのマッピングよりも簡単ですか?

StackOverflow https://stackoverflow.com/questions/218190

質問

オブジェクトリレーショナルマッピングについては、ここを含めて詳しく説明されています。私はいくつかのアプローチと落とし穴と妥協の経験があります。真の解決には、オブジェクト指向モデルまたはリレーショナルモデル自体の変更が必要なようです。

関数型言語を使用している場合、同じ問題が発生しますか?これらの2つのパラダイムは、OOやRDBMSよりもうまく適合するはずです。 RDBMSのセットで考える考え方は、機能的アプローチが約束するように思われる自動並列処理と一致するようです。

興味深い意見や見識はありますか?業界の現状はどうですか?

役に立ちましたか?

解決

リレーショナルデータベースを拡張する際の難しい問題は、拡張トランザクション、データ型の不一致、自動クエリ変換、 N + 1 Select は、リレーショナルシステムを離れる際の基本的な問題であり、私の意見では、受信プログラミングパラダイムを変更しても変わらない。

他のヒント

ORMの目的は何ですか?

ORMを使用する主な目的は、ネットワークモデル(オブジェクトの向き、グラフなど)とリレーショナルモデルを橋渡しすることです。そして、2つのモデルの主な違いは驚くほど単純です。親が子を指すのか(ネットワークモデル)、子が親を指すのか(リレーショナルモデル)です。

この単純さを念頭に置いて、 2つのモデル間に<!> quot;インピーダンスミスマッチ<!> quot; のようなものはありません。人々が通常遭遇する問題は、実装固有のものであり、クライアントとサーバー間のデータ転送プロトコルが改善されていれば解決可能です。

SQLはORMで発生した問題にどのように対処できますか?

特に、 3番目のマニフェストは、SQL言語と関係代数の欠点に対処しようとしています。次のようなさまざまなデータベースに実装されているネストされたコレクションを許可します。

  • Oracle(おそらく最も洗練された実装)
  • PostgreSQL(ある程度)
  • Informix
  • SQL Server、MySQLなど(<!> quot; emulation <!> quot; XMLまたはJSON経由)

すべてのデータベースがSQL標準のMULTISET()演算子(Oracleなど)を実装している場合、ネストされたコレクションを内部から直接マテリアライズできるため、人々はマッピングに(おそらくオブジェクトグラフの永続性のために)ORMを使用しなくなりますデータベース、例えばこのクエリ:

SELECT actor_id, first_name, last_name,
  MULTISET (
    SELECT film_id, title
    FROM film AS f
    JOIN film_actor AS fa USING (film_id)
    WHERE fa.actor_id = a.actor_id
  ) AS films
FROM actor AS a

非正規化された結合結果ではなく、ネストされたコレクションとしてすべての俳優とその映画を生み出します(映画ごとに俳優が繰り返されます)。

クライアント側の機能パラダイム

クライアント側の関数型プログラミング言語がデータベースの相互作用により適しているかどうかという質問は、本当に直交しています。 ORMはオブジェクトグラフの永続化に役立つため、クライアントサイドモデルがグラフであり、それをグラフにしたい場合、関数型プログラミング言語を使用してそのグラフを操作しているかどうかに関係なく、ORMが必要になります。

ただし、オブジェクト指向は関数型プログラミング言語では慣用的ではないため、すべてのデータ項目をオブジェクトに押し込める可能性は低くなります。 SQLを書いている人にとって、任意の tuples を投影することは非常に自然です。 SQLは構造型付けを採用しています。各SQLクエリは、名前を事前に割り当てる必要なく、独自の行タイプを定義します。特に型推論が洗練されている場合、SQLの結果を以前に定義されたオブジェクト/クラスにマッピングすることを考えたことがない場合、それは機能的なプログラマーと非常によく共鳴します。

jOOQ を使用したJavaの例このブログ投稿からは次のようになります。

// Higher order, SQL query producing function:
public static ResultQuery<Record2<String, String>> actors(Function<Actor, Condition> p) {
    return ctx.select(ACTOR.FIRST_NAME, ACTOR.LAST_NAME)
              .from(ACTOR)
              .where(p.apply(ACTOR)));
}

このアプローチは、SQL言語が何らかのORMによって抽象化された場合、またはSQLの自然な<!> quot; string based <!> quot;自然が使われました。上記の機能を使用できるようになりました。このように:

// Get only actors whose first name starts with "A"
for (Record rec : actors(a -> a.FIRST_NAME.like("A%")))
    System.out.println(rec);

SQL上のFRM抽象化

一部のFRMは、通常次の理由でSQL言語を抽象化しようとします。

  • 彼らは、SQLが十分に構成可能でないと主張している(jOOQはこれを反証し、正しくするのは非常に難しい)。
  • 彼らは、APIユーザーは<!> quot; native <!> quot;コレクションAPI、たとえばJOINflatMap()に変換され、WHEREfilter()などに変換されます。

質問に答える

FRMは<!> quot; easier <!> quotではありません。 ORMよりも、別の問題を解決します。実際、FRMは問題をまったく解決しません。bSQLは宣言型プログラミング言語そのものであるため(関数型プログラミングとそれほど変わらない)、他の関数型クライアントプログラミング言語と非常によく一致します。そのため、FRMは、SQL、外部DSL、およびクライアント言語間のギャップを埋めるだけです。

jOOQ の背後にある会社で働いているため、この回答には偏りがあります)

それはあなたのニーズに依存します

  1. データ構造に集中したい場合は、JPA / HibernateなどのORMを使用します
  2. 治療法を明らかにしたい場合は、FRMライブラリーQueryDSLまたはJooqをご覧ください
  3. 特定のデータベースに合わせてSQLリクエストを調整する必要がある場合は、JDBCおよびネイティブSQLリクエストを使用してください

さまざまな<!> quot; Relational Mapping <!> quot;の強み。テクノロジーは移植性です。アプリケーションがほとんどのACIDデータベースで実行されるようにします。 そうしないと、SQL要求を手動で作成するときに、さまざまなSQLダイアレクトの違いに対処できます。

もちろん、SQL92標準に自分自身を制限して(そして、いくつかの関数型プログラミングを行う)、またはORMフレームワークで関数型プログラミングのいくつかの概念を再利用できます

ORM強度は、ボトルネックとして機能する可能性があるセッションオブジェクト上に構築されます。

  1. 基礎となるデータベーストランザクションが実行されている限り、オブジェクトのライフサイクルを管理します。
  2. Javaオブジェクトとデータベース行の間の1対1マッピングを維持します(オブジェクトの重複を避けるために内部キャッシュを使用します)。
  3. 関連付けの更新と削除する孤立オブジェクトを自動的に検出します
  4. オプティミスティックロックまたはペシミストロックの並行性の問題を処理します。

それにもかかわらず、その長所は短所でもあります:

  1. セッションはオブジェクトを比較できる必要があるため、equals / hashCodeメソッドを実装する必要があります。 しかし、オブジェクトの平等は<!> quot; Business Keys <!> quot;に根ざしている必要があります。データベースIDではありません(新しい一時オブジェクトにはデータベースIDがありません!)。 ただし、具体化された概念の中には、ビジネスの平等(操作など)を持たないものがあります。 一般的な回避策は、データベース管理者を混乱させる傾向があるGUIDに依存しています。

  2. セッションは関係の変更をスパイする必要がありますが、そのマッピングルールはビジネスアルゴリズムに適さないコレクションの使用をプッシュします。 HashMapを使用したい場合がありますが、ORMではキーを別の<!> quot; Rich Domain Object <!> quot;にする必要があります。別の軽いものの代わりに... 次に、キーとして機能するリッチドメインオブジェクトにオブジェクトの平等を実装する必要があります... しかし、このオブジェクトにはビジネスの世界に対応するものがないため、できません。 したがって、反復する必要がある単純なリストにフォールバックします(そしてパフォーマンスの問題が発生します)。

  3. ORM APIは、実際の使用には適さない場合があります。 たとえば、実際のWebアプリケーションは、いくつかの<!> quot; WHERE <!> quot;を追加して、セッションの分離を強制しようとします。データをフェッチするときの句... 次に、<!> quot; Session.get(id)<!> quot;十分ではないため、より複雑なDSL(HSQL、Criteria API)を使用するか、ネイティブSQLに戻る必要があります

  4. データベースオブジェクトは、他のフレームワーク専用のオブジェクト(OXMフレームワーク=オブジェクト/ XMLマッピングなど)と競合します。 たとえば、RESTサービスがjacksonライブラリを使用してビジネスオブジェクトをシリアル化する場合。 しかし、このジャクソンはHibernate Oneに正確にマッピングされます。 次に、両方をマージすると、APIとデータベースの間に強力なカップリングが表示されます または、翻訳を実装する必要があり、ORMから保存したコードはすべて失われます...

一方、FRMは<!> quot; Object Relational Mapping <!> quot;のトレードオフです。 (ORM)およびネイティブSQLクエリ(JDBCを使用)

FRMとORMの違いを説明する最良の方法は、DDDアプローチを採用することです。

  • オブジェクトリレーショナルマッピングにより、<!> quot; Rich Domain Object <!> quot;を使用できます。データベーストランザクション中に状態が変更可能なJavaクラスです
  • 機能リレーショナルマッピングは、<!> quot; Poor Domain Objects <!> quot;に依存しています。不変です(そのため、コンテンツを変更するたびに新しいクローンを作成する必要があります)

ORMセッションに課せられた制約を解除し、DSLにほとんどの時間を依存しますSQL(移植性は重要ではありません) しかし一方で、トランザクションの詳細、同時実行の問題を調べる必要があります

List<Person> persons = queryFactory.selectFrom(person)
  .where(
    person.firstName.eq("John"),
    person.lastName.eq("Doe"))
  .fetch();

機能からリレーショナルへのマッピングは、OOからRDBMSへの作成よりも簡単に作成して使用できるはずです。つまり、データベースに対してのみクエリを実行する限りです。 (まだ)副作用のないデータベース更新をうまく行う方法はまだわかりません。

主な問題はパフォーマンスです。今日のRDMSは、機能的なクエリで使用するように設計されていないため、多くの場合、動作が不十分になる可能性があります。

機能的なリレーショナルマッピングはまだ行っていませんが、それ自体ですが、RDBMSへのアクセスを高速化するために機能的なプログラミング手法を使用しました。

データセットから開始し、その上で複雑な計算を行い、結果を保存することは非常に一般的です。結果は、たとえば、追加の値を持つ元のサブセットです。命令型アプローチでは、追加のNULL列を使用して初期データセットを保存し、計算を実行してから、計算された値でレコードを更新する必要があります。

合理的なようです。しかし、それに関する問題は、非常に遅くなる可能性があることです。計算に更新クエリ自体のほかに別のSQLステートメントが必要な場合、またはアプリケーションコードで実行する必要がある場合、計算後に変更するレコードを文字通り(再)検索して、結果を適切な行に格納する必要があります。

結果用の新しいテーブルを作成するだけでこれを回避できます。これにより、更新の代わりに常に挿入することができます。キーを複製して別のテーブルを作成することになりますが、NULLを格納する列のスペースを無駄にする必要はなくなりました<!>#8211;持っているものだけを保存します。その後、最終選択で結果に参加します。

私はこのようにRDBMSを(ab)使用し、結局このようなSQLステートメントを書くことになりました...

create table temp_foo_1 as select ...;
create table temp_foo_2 as select ...;
...
create table foo_results as
  select * from temp_foo_n inner join temp_foo_1 ... inner join temp_foo_2 ...;

これが本質的に行っていることは、不変の束縛を作成することです。ただし、良い点は、セット全体を一度に処理できることです。 Matlabのような行列を操作できる言語を思い出させます。

これにより、並列処理がはるかに簡単になります。

追加の利点は、この方法で作成されたテーブルの列のタイプは、選択された列から推測されるため、指定する必要がないことです。

Samが述べたように、DBを更新する必要がある場合、OOの世界と同じ並行性の問題に直面する必要があると思います。プログラムの機能的な性質は、RDBMSのデータ、トランザクションなどの状態のために、オブジェクトの性質よりも少し問題があるかもしれません。

しかし、読むためには、いくつかの問題のあるドメインでは関数型言語のほうが自然かもしれません(DBに関係なく見えるように)

機能的な<!> lt;-<!> gt; RDBMSマッピングは、OO <!> lt;-<!> gt; RDMBSマッピングと大きな違いはありません。しかし、新しいDBスキーマを使用してプログラムを開発する場合や、レガシーDBスキーマに対して何かを実行する場合など、使用するデータの種類に大きく依存すると思います。

たとえば、関連付けの遅延フェッチなどは、遅延評価に関連するいくつかの概念を使用して、おそらくうまく実装できます。 (オブジェクト指向でも非常にうまくできますが)

編集:いくつかのグーグルで HaskellDB (SQLライブラリHaskell)-試してみる価値はありますか?

データベースと関数型プログラミングは融合できます。

例:

Clojureは、リレーショナルデータベース理論に基づいた関数型プログラミング言語です。

               Clojure -> DBMS, Super Foxpro
                   STM -> Transaction,MVCC
Persistent Collections -> db, table, col
              hash-map -> indexed data
                 Watch -> trigger, log
                  Spec -> constraint
              Core API -> SQL, Built-in function
              function -> Stored Procedure
             Meta Data -> System Table

注:最新のspec2では、specはRMDBに似ています。 参照: spec-alpha2 wiki:Schema-and-select

私は提言します:NoSQLとRMDBの利点の組み合わせを実現するために、ハッシュマップの上にリレーショナルデータモデルを構築します。これは実際にはposgtresqlの逆実装です。

アヒルの入力:アヒルのように見え、カモのように鳴る場合は、アヒルでなければなりません。

clojureのデータモデルがRMDBのように、clojureの機能がRMDBのように、clojureのデータ操作がRMDBの場合、clojureはRMDBでなければなりません。

Clojureはリレーショナルデータベース理論に基づいた関数型プログラミング言語です

すべてがRMDB

ハッシュマップに基づくプログラミング(NoSQL )

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top