Java(または同様の汎用オブジェクト指向プログラミング言語)でSQLクエリを外部化することの利点と欠点は何ですか。外部化とは、すべてのSQLをJava以外のファイルに保存することです。例は here です。
デバッグが難しくなったり、簡単になったりしますか?
データベースを変更する必要がある場合、コードへの変更を分離します。または、@ Moronsが言ったように、同じJavaコードで複数のSQL方言、最適化、またはデータベースをサポートするメカニズムを提供できます
SQLのみの変更は、配置後に行うことができます。
@JVXRによると、プログラマの職務とDBAの職務の分離。
本当に才能があり、勤勉なDBAは、SQLを記述し、データベースフィールドをORMツールよりも優れたパフォーマンス(より高速な実行)でJavaオブジェクトに手動でロードすることができるはずです。同じことがコンパイラと。何年もの間アセンブリ言語を使用していて、私たちが多くのアセンブリを作成しているのを目にすることはもうありません。
コードが別の非Javaファイルでデータベースと対話する方法を隠した場合、どのようにデータベースセッションを有意義に管理しますか?これがさらに難しくなるようです。
HibernateのようなORM(Object-Relational-Mapping)ツールのポイントは、Javaオブジェクトとクエリ抽象化を操作し、ORMツールにクエリを実行させることです。テーブルのクエリとロードJavaオブジェクトの適切なフィールドに各列を挿入することは、多くのプログラミング作業です。そのコードを維持することも、多くの作業です。ORMツールは、不要なクエリを排除するプロキシオブジェクトを提供するなど、すばらしいことを行います。タイプセーフなデータベースデータで作業できるJavaオブジェクト-そのためのコードを記述せずに。その後、多数の手動SQLクエリを記述し始めると、 ORM。ほとんどのSQLはほとんど常に書き込まれ、外部(非Java)ファイルに入れられる可能性があります。しかし、通常は少ないほど良いです。何に時間を費やしたいのかという問題です。 SQL、どこに置いてもかまいません。
ほとんどのアプリケーションでは、「実際の作業」はデータベースのクエリと更新、およびそれらのクエリと更新に最も近いコードで実行されます。これらのクエリを別のファイルで非表示にすると、それがわかりにくくなるようです。つまり、プログラマーにデータベースを知らないように勧めます。これは、適切な分業、または有用な抽象化(ORMツールが提供するものなど)である場合もあれば、理解するのに不必要なハードルである場合もあります。 SQLルーチンに適切な名前を付けると、この問題が多少緩和される可能性があります。
Javaとデータベースコードの分離の増加は、リファクタリングの障害となる可能性があります。フィールドを検索する代わりに、フィールドを使用するクエリを見つけ、次に各クエリが使用される場所を見つける必要があります。追加のステップ。
私は反対方向に動いていた-私はORM Javaオブジェクトをデータベースから変更するたびにデータベースから生成し、列ごとにSQLおよびHQL(Hibernate Query Language)トークンも生成するおよび各テーブル。すべてのSQLクエリまたはHQLシンボリッククエリで、フィールド名の代わりにトークンを追加します。これにより、アプリケーションで各フィールドが使用されている場所をJava = HQLクエリまたはSQLクエリのオブジェクトフィールド。
以下は、タイプセーフなHibernateスタイルのCriteriaクエリの例で、所有者がいる単一の会社の「thing」テーブルから削除されていないレコードをクエリします。
Thing.Javaの場合:
// SQL Table name
public static final String SQL_thing = "thing";
// SQL Column names
public static final String SQL_id = "id";
public static final String SQL_company_id = "company_id";
public static final String SQL_owner_id = "owner_id";
public static final String SQL_is_deleted = "is_deleted";
// HQL object name
public static final String HQL_Thing = "Thing";
// HQL field names
public static final String HQL_id = "id";
public static final String HQL_company = "company";
public static final String HQL_owner = "owner";
public static final String HQL_isDeleted = "isDeleted";
SQLにドロップダウンする必要がある場合は、モノのIDを照会するには、次のようになります。シンボルはfinal String
であるため、連結はコンパイラーによって行われ、実行時にではなく、Javaの場合でも、+演算子を使用しても行われます。
jdbcConn = HibernateUtil.getJdbcConnection();
String query = "select " + Thing.SQL_id +
" from " + Thing.SQL_thing +
" where " + Thing.SQL_company_id +
" = ? and " + Thing.SQL_is_deleted +
" = b'0' and " + Thing.SQL_owner_id +
" is not null";
PreparedStatement stmt = jdbcConn.prepareStatement(query);
stmt.setLong(1, someCompany.getId());
ResultSet results = stmt.executeQuery();
while (results.next()) {
Long thingId = results.getLong(1));
// do stuff with things IDs
}
明らかに、パフォーマンス上の理由で絶対に必要な場合や、SQLレポートの実行などを行う場合を除いて、SQLに依存しないようにしています。
アプリケーションの画面のデータを作成するコードでは:
Crit<Thing> tCrit = Crit.create(Thing.class);
tCrit.add(Restrictions.eq(Thing.HQL_company, someCompany));
tCrit.add(Restrictions.eq(Thing.HQL_isDeleted, false));
tCrit.add(Restrictions.isNotNull(Thing.HQL_owner));
for (Thing t : tCrit.list()) {
// do stuff with things
}
パフォーマンス以外は、基準を使用しないようにしています。繰り返しますが、できる限りジャワ島に滞在したいと思います。
for (Thing t : someCompany.getThings()) {
if ( t.getIsDeleted() ||
(t.getOwner() == null) ) {
continue;
}
// do stuff with things
}
明らかに追加のレコード(所有者のないものと削除されたもの)を取り戻していますが、多くのものがあり、ほとんどが削除されておらず所有者がいる場合、パフォーマンスへの影響は最小限になります。
参考までに、外部SQL実装は次のようになります。外部SQLファイル(WEB4Jに似た構文):
NON_DELETED_THINGS_WITH_OWNERS {
select t.id, t.company_id, t.owner_id, t.is_deleted
from thing where t.company_id = ? and
t.is_deleted = b'0' and
t.owner_id is not null; }
外部SQLファイルクエリをJavaで利用できるようにする方法については、あまり説明していません。おそらくWEB4Jのようなツールがそれを処理しますが、SQLファイルを何らかの方法で指定する必要があり、ツールがそれらのクエリを使用できるようにします。その他の抽象化。
会社のクエリ方法:
public Set<Thing> getNonDeletedThingsWithOwners(Connection jdbcConn) {
PreparedStatement stmt = jdbcConn.prepareStatement(NON_DELETED_THINGS_WITH_OWNERS);
stmt.setLong(1, this.id);
ResultSet results = stmt.executeQuery();
Set<Thing> things = new HashSet<>();
while (results.next()) {
things.add(Thing.valueOf(results));
}
return things;
}
事の工場方法:
public static Thing valueOf(ResultSet rs) throws SQLException {
Thing t = new Thing();
t.id = rs.getLong(SQL_id);
t.company_id = rs.getLong(SQL_company_id);
t.owner_id = rs.getLong(SQL_owner_id);
t.isDeleted = rs.getBoolean(SQL_is_deleted);
return t;
}
使用法:
Set<Thing> thingSet = someCompany.getNonDeletedThingsWithOwners();
for (Thing t : thingSet) {
// do stuff with things
}
これをPure-Java ORMの例または基準クエリの例と比較してください。これは4つのファイルにまたがる大量のコードです。パフォーマンスと制御が必要な場合は、エネルギーを投資してください。しかし、ORMがこれから私を救うことができるとき、私はそれを許すと思います。
ここでは、Javaデータベースオブジェクトを確実に生成するツールがあると仮定します。
Pure-Javaの例では、パフォーマンスが問題にならない限り、SQLまたはクエリのデバッグはありません。間違ったテストを実行することもできます(所有者のないものを除外するのを忘れるなど)が、それは非常に簡単です。自分がしていることに集中し続けることで、この可能性を最小限に抑えます。 ORMにログファイル内の実際のクエリを出力するように依頼するか、それらを調整する必要がある場合や、Thingテーブルを100回個別にクエリする場合などにこのコードを実行すると、クエリが1つになります。
Criteriaクエリに間違ったタイプのオブジェクトまたはnullを渡す可能性があります。また、冗長であるため、実際に何をしているかを確認するのに時間がかかり、バグが基準に侵入しやすくなります。
In-Java SQLを使用すると、上記のすべての問題に加えて、SQL構文を正しく設定できます。私のシンボルはこれを少し複雑にすることを認めますが、リファクタリングのプラスは読みやすさのマイナスよりも大きいと感じています。
外部SQLの例では、上記のすべてのエラーの可能性に加えて、結果をオブジェクトにロードすることによるすべての追加コードがあります。さらに、同期が取れなくなる可能性がある複数のファイル。フィールドの名前を変更、削除、または追加するとどうなりますか?コンパイルエラーが発生しますか?変更する必要があるすべての場所を見つけますか?または、テストでそれを見つける必要がありますか?.
デバッグの観点から言えば、純粋なJavaを使用したORMは明らかな勝利であり、そこから離れるたびに追加のデバッグを行う必要があります。
ORMとシンボルを使用することで、リファクタリングの困難な時間を数え切れないほど節約でき、データベースが簡単に変更できるようになりました。何かが壊れるとコンパイルされません。コードが大きくなると、複雑さを管理する唯一の方法はリファクタリングであり、複雑になると難しくなるため、特定のサイズのコードベースを超えると、リファクタリングの容易さが第一の目標になります。
ORMはまた、バグの機会を排除し、このためのパフォーマンスコストは通常最小限です。必要に応じてSQLにドロップダウンすることもできますが、SQLを個別のファイルに配置するよりも、アプリケーションのSQLを少なくする方が良い目標だと思います。
これは、より大きなプロジェクトがあり、偶然にもSQL忍者である専任の「データベース開発者」がいる場合に、より適切になると思います。
長所:
責任の明確な分離があります。開発者はコーディングを行い、db開発者はスクリプトを記述します。
両方がうまく機能し、db設計が正しい場合、アプリケーションは他のすべてのORMフレームワークよりもパフォーマンスが優れています。
短所:
アプリ開発者とDB開発者は通常同意しません(私は最高のドライバー症候群です)
何かがうまくいかない場合、指差しが多すぎます。
私はiBatisを使用して、このアプローチを使用するプロジェクトに取り組んでいました-現在 myBatis
長所:
-DB担当者がSQLに触れるのが簡単になります。
-SQLが変更されたときに再構築は必要ありません(インターフェースが同じであると想定)
短所:
-上記の長所の否定
-そのSQLを利用しているコードの横にあるSQLのフィールドを確認すると便利です。
次の理由により、クエリをデータベースのストアドプロシージャとして使用することをお勧めします。
1>これらは事前にコンパイルされているため、おそらくより高速に実行されます。
2>コードベースとは関係なく更新できます。通常、特に管理された企業スタイルの環境では、アプリケーションを再インストールするよりもSQLスクリプトを実行する方が簡単です。
3>デバッグ/テスト/パフォーマンス調整が簡単-アプリケーションを使わずにクエリを簡単に実行できます。
4>アプリケーションを更新せずにデータベース構造を変更できます。
5>アプリケーションごとに1つのコードベースを使用して、さまざまなデータベースエンジンに実装できます。各DBに複数のコードベースを使用することはできません。異なるdbに使用される構文は十分に異なる傾向があるため、最も単純なSQL(特にストアドプロシージャ)を使用しない限り、いくつかの調整が必要になります。
私が見ることができる主な欠点は、SQLがコンパイルされたコードに隠されていないため、外部の当事者が商用アプリケーションにとって重要であるかどうかに関係なく、アプリケーションを読み取ってリバースエンジニアリングするのが簡単なことです。
@JVXR Answerに追加するだけで、もう1つの大きな利点は複数のDBエンジンをサポートする機能です。たとえば、SQL Server TSQLをOracle Pl-Sqlに簡単に交換できます。
これは、パッケージソフトウェアを販売している場合に特に便利です。クライアントがすでに所有しているDBを使用できるようにして、追加コストなしでサポートできます。