回答:
まず、準備されたステートメントでクエリパラメータを使用することを検討してください。
PreparedStatement stm = c.prepareStatement("UPDATE user_table SET name=? WHERE id=?");
stm.setString(1, "the name");
stm.setInt(2, 345);
stm.executeUpdate();
実行できるもう1つのことは、すべてのクエリをプロパティファイルに保存することです。たとえば、querys.propertiesファイルでは、上記のクエリを配置できます。
update_query=UPDATE user_table SET name=? WHERE id=?
次に、簡単なユーティリティクラスを使用します。
public class Queries {
private static final String propFileName = "queries.properties";
private static Properties props;
public static Properties getQueries() throws SQLException {
InputStream is =
Queries.class.getResourceAsStream("/" + propFileName);
if (is == null){
throw new SQLException("Unable to load property file: " + propFileName);
}
//singleton
if(props == null){
props = new Properties();
try {
props.load(is);
} catch (IOException e) {
throw new SQLException("Unable to load property file: " + propFileName + "\n" + e.getMessage());
}
}
return props;
}
public static String getQuery(String query) throws SQLException{
return getQueries().getProperty(query);
}
}
次のようにクエリを使用できます。
PreparedStatement stm = c.prepareStatement(Queries.getQuery("update_query"));
これはかなり単純な解決策ですが、うまく機能します。
InputStream
に、if (props == null)
ステートメントの内側に置くことをお勧めします。
任意のSQLの場合は、jOOQを使用します。jOOQは現在サポートしているSELECT
、INSERT
、UPDATE
、DELETE
、TRUNCATE
、とMERGE
。次のようなSQLを作成できます。
String sql1 = DSL.using(SQLDialect.MYSQL)
.select(A, B, C)
.from(MY_TABLE)
.where(A.equal(5))
.and(B.greaterThan(8))
.getSQL();
String sql2 = DSL.using(SQLDialect.MYSQL)
.insertInto(MY_TABLE)
.values(A, 1)
.values(B, 2)
.getSQL();
String sql3 = DSL.using(SQLDialect.MYSQL)
.update(MY_TABLE)
.set(A, 1)
.set(B, 2)
.where(C.greaterThan(5))
.getSQL();
SQL文字列を取得する代わりに、jOOQを使用して実行することもできます。見る
(免責事項:私はjOOQの背後にある会社で働いています)
"?"
バインド値をレンダリングするかインラインにするかを選択できます。
考慮すべき1つのテクノロジーはSQLJです。これは、SQLステートメントをJavaに直接埋め込む方法です。簡単な例として、TestQueries.sqljと呼ばれるファイルに次のものがあるとします。
public class TestQueries
{
public String getUsername(int id)
{
String username;
#sql
{
select username into :username
from users
where pkey = :id
};
return username;
}
}
.sqljファイルを取得してピュアJavaに変換する追加のプリコンパイルステップがあります。つまり、次のように区切られた特別なブロックを探します。
#sql
{
...
}
それらをJDBC呼び出しに変換します。SQLJを使用することには、いくつかの重要な利点があります。
主要なデータベースベンダーのほとんどには、トランスレータの実装があるので、必要なものすべてを簡単に見つけることができます。
Spring JDBCを見てみましょう。SQLをプログラムで実行する必要があるときはいつでも使用します。例:
int countOfActorsNamedJoe
= jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});
これは、あらゆる種類のSQL実行、特にクエリに最適です。完全なORMの複雑さを追加することなく、結果セットをオブジェクトにマップするのに役立ちます。
私はSpringの名前付きJDBCパラメータを使用する傾向があるため、「select * from blah where colX = ':someValue'」のような標準文字列を記述できます。それはかなり読みやすいと思います。
別の方法は、別の.sqlファイルで文字列を提供し、ユーティリティメソッドを使用して内容を読み取ることです。
ああ、Squillも一見の価値があります:https : //squill.dev.java.net/docs/tutorial.html
次に、HibernateのようなORMを使用するための推奨事項について説明します。ただし、それでもうまくいかない状況は確かにあるので、この機会に、私が書いてきたいくつかのことを宣伝します。SqlBuilderは、「ビルダー」スタイルを使用してSQLステートメントを動的に構築するためのJavaライブラリです。それはかなり強力でかなり柔軟性があります。
アドホックレポートの目的で非常に動的なSQLステートメントを構築する必要があるJavaサーブレットアプリケーションに取り組んでいます。アプリの基本的な機能は、一連の名前付きHTTPリクエストパラメータを事前にコード化されたクエリにフィードし、適切にフォーマットされた出力のテーブルを生成することです。私は、Spring MVCと依存関係注入フレームワークを使用して、すべてのSQLクエリをXMLファイルに格納し、それらをテーブルの書式設定情報と共にレポートアプリケーションに読み込みました。最終的に、レポート要件は既存のパラメーターマッピングフレームワークの機能よりも複雑になり、自分で作成する必要がありました。これは、開発における興味深い演習であり、他の何よりもはるかに堅牢なパラメータマッピングのフレームワークを生み出しました。
新しいパラメータマッピングは次のようになりました。
select app.name as "App",
${optional(" app.owner as "Owner", "):showOwner}
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = ${integer(0,50):serverId}
and app.id in ${integerList(50):appId}
group by app.name, ${optional(" app.owner, "):showOwner} sv.name
order by app.name, sv.name
結果のフレームワークの優れた点は、適切な型チェックと制限チェックを使用して、HTTP要求パラメーターを直接クエリに処理できることでした。入力の検証に追加のマッピングは必要ありません。上記のクエリ例では、serverIdという名前のパラメーター がチェックされ、整数にキャストでき、0〜50の範囲にあることが確認されます。パラメータappIdは、長さ制限が50の整数の配列として処理されます。フィールドshowOwnerが存在し、「true」に設定されている場合、引用符内のSQLのビットが、オプションのフィールドマッピング用に生成されたクエリに追加されます。フィールドさらにパラメーターマッピングを備えたSQLのオプションセグメントを含む、いくつかのパラメータータイプマッピングが利用可能です。開発者が思いつく限りの複雑なクエリマッピングが可能です。特定のクエリがPreparedStatementを介して最終的なマッピングを持つのか、それとも単に事前に作成されたクエリとして実行されるのかを決定するためのコントロールもレポート構成にあります。
サンプルのHttpリクエスト値の場合:
showOwner: true
serverId: 20
appId: 1,2,3,5,7,11,13
次のSQLが生成されます。
select app.name as "App",
app.owner as "Owner",
sv.name as "Server", sum(act.trans_ct) as "Trans"
from activity_records act, servers sv, applications app
where act.server_id = sv.id
and act.app_id = app.id
and sv.id = 20
and app.id in (1,2,3,5,7,11,13)
group by app.name, app.owner, sv.name
order by app.name, sv.name
SpringやHibernate、またはそれらのフレームワークの1つは、型を検証し、配列などの複雑なデータ型を許可する、より堅牢なマッピングメカニズムを提供する必要があると私は本当に思います。私は自分の目的のためだけにエンジンを作成しましたが、一般的なリリースではまだ十分に読まれていません。現時点ではOracleクエリでのみ機能し、すべてのコードは大企業に属しています。いつか自分のアイデアを取り入れて新しいオープンソースフレームワークを構築するかもしれませんが、既存の大手企業の1人が挑戦してくれることを期待しています。
MyBatis(www.mybatis.org)もご覧ください。これは、Javaコードの外部でSQLステートメントを記述し、SQLの結果をJavaオブジェクトにマッピングするのに役立ちます。
GoogleはRoom Persitence Libraryと呼ばれるライブラリを提供しています。これは、Androidアプリ用の SQL を非常に簡潔に記述する方法を提供します。基本的には、基礎となるSQLiteデータベースの抽象化レイヤーです。以下は公式ウェブサイトの短いコードスニペットです。
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
ライブラリの公式ドキュメントには、より多くの例とより良いドキュメントがあります。
Java ORMであるMentaBeanと呼ばれるものもあります。それは素晴らしい機能を備えており、SQLを書くのにかなり単純な方法のようです。
Room provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite
。したがって、これはRDBMSの汎用ORMライブラリではありません。主にAndroidアプリ向けです。
XMLファイルから読み取ることができます。保守と操作が簡単です。標準のSTaX、DOM、SAXパーサーがあり、Javaで数行のコードにすることができます。
SQLをさらに活用するために、タグの属性を持ついくつかのセマンティック情報を持つことができます。これは、メソッド名やクエリの種類など、コーディングの手間を省くものであれば何でもかまいません。
jarの外側にxmlを配置して、簡単に維持できます。プロパティファイルと同じ利点。
XMLは拡張可能で、他の形式に簡単に変換できます。
I don't see a reason to make use of XML.
編集できなかったため、以前のコメントを削除しました。
複数行にまたがるPreparedStatementsの長いSQL文字列(テキストファイルで簡単に提供してリソースとして読み込むことができる)を除いて、文字列の連結をどのように取得しますか?
SQL文字列を直接作成していませんか?それはプログラミングにおける最大のノーノーです。PreparedStatementsを使用して、データをパラメーターとして指定してください。SQLインジェクションの可能性を大幅に減らします。