web-dev-qa-db-ja.com

JavaでSQL文字列を構築する最もクリーンな方法

データベース操作(更新、削除、挿入、選択、そのようなこと)を行うためのSQL文字列を構築したい-何百万もの「+」と最高でも判読できない引用符を使用したひどい文字列連結メソッドの代わりに-より良い方法でなければなりません。

私はMessageFormatの使用を考えました-しかし、それは合理的な仕事をすると思いますが、ユーザーメッセージに使用されることになっています-しかし、Java sqlのSQL型操作にもっと整合するものがあるはずですライブラリ。

Groovyは良いものでしょうか?

95
Vidar

まず、準備済みステートメントでクエリパラメータを使用することを検討してください。

PreparedStatement stm = c.prepareStatement("UPDATE user_table SET name=? WHERE id=?");
stm.setString(1, "the name");
stm.setInt(2, 345);
stm.executeUpdate();

他にできることは、すべてのクエリをプロパティファイルに保持することです。たとえば、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"));

これはかなり単純なソリューションですが、うまく機能します。

67

任意のSQLには、 jOOQ を使用します。 jOOQは現在、SELECTINSERTUPDATEDELETETRUNCATE、および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を使用して実行することもできます。見る

http://www.jooq.org

(免責事項:私はjOOQの背後にある会社で働いています)

59
Lukas Eder

考慮すべきテクノロジーの1つは [〜#〜] sqlj [〜#〜] -JavaにSQLステートメントを直接埋め込む方法です。簡単な例として、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ファイルを取得して、それらをpure Java-に変換します。つまり、次で区切られた特別なブロックを探します。

#sql
{
    ...
}

それらをJDBC呼び出しに変換します。 SQLJを使用することには、いくつかの重要な利点があります。

  • jDBCレイヤーを完全に抽象化します-プログラマーは、JavaおよびSQL
  • 翻訳者は、コンパイル時にデータベースに対してクエリの構文などをチェックすることができます
  • 「:」プレフィックスを使用してクエリ内の変数を直接バインドするJava

ほとんどの主要なデータベースベンダーには、トランスレーターの実装が存在するため、必要なものはすべて簡単に見つけることができます。

14
Ashley Mercer

Squiggle のようなものを求めているのだろうか。また、非常に便利なのは jDBI です。ただし、クエリには役立ちません。

12
tcurdt

Spring JDBC をご覧ください。プログラムでSQLを実行する必要があるときはいつでも使用します。例:

int countOfActorsNamedJoe
    = jdbcTemplate.queryForInt("select count(0) from t_actors where first_name = ?", new Object[]{"Joe"});

あらゆる種類のsql実行、特にクエリに最適です。完全なORMの複雑さを追加することなく、結果セットをオブジェクトにマップするのに役立ちます。

9

私は「select * from blah where colX = ':someValue'」のような標準の文字列を書くことができるように、Springの名前付きJDBCパラメーターを使用する傾向があります。かなり読みやすいと思います。

別の方法は、文字列を別の.sqlファイルで提供し、ユーティリティメソッドを使用して内容を読み取ることです。

ああ、Squillも見てみる価値があります。 https://squill.dev.Java.net/docs/tutorial.html

5
GaryF

次に、HibernateのようなORMを使用するための推奨事項を説明します。ただし、それがうまくいかない状況は確かにあるので、この機会に私が書いたいくつかのことを宣伝します: SqlBuilder is Java library 「ビルダー」スタイルを使用してSQLステートメントを動的に構築します。それはかなり強力でかなり柔軟性があります。

4
james

なぜすべてのSQLを手動で生成したいのですか? HibernateのようなORMを見たことがありますか?プロジェクトによっては、おそらく必要なものの少なくとも95%を実行し、その後生のSQLを実行し、最後のパフォーマンスを取得する必要がある場合は、手動で調整する必要があるSQLクエリ。

4
Jared

私はJavaアドホックレポートの目的で非常に動的なSQLステートメントを構築する必要があるサーブレットアプリケーションに取り組んでいます。アプリの基本機能は、名前付きのHTTPリクエストパラメータの束をpreコーディングされたクエリを作成し、適切にフォーマットされた出力テーブルを生成する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、またはこれらのフレームワークのいずれかは、型を検証し、配列などの複雑なデータ型を許可する、より堅牢なマッピングメカニズムを提供する必要があると思います。私は自分の目的のためだけにエンジンを書きましたが、一般的なリリースではあまり読まれていません。現時点ではOracleクエリでのみ機能し、すべてのコードは大企業に属します。いつか自分のアイデアを取り入れて新しいオープンソースフレームワークを構築するかもしれませんが、既存の大手企業が挑戦することを望んでいます。

3
Natalia

MyBatis( www.mybatis.org )もご覧ください。これは、Javaコードの外側にSQLステートメントを記述し、SQL結果をJavaオブジェクトにマッピングするのに役立ちます。

3
joshua

Googleは Room Persitence Library と呼ばれるライブラリを提供しています。これは、SQLを非常にクリーンに記述する方法を提供します。以下は、公式Webサイトからの短いコードスニペットです。

@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を書くための非常に簡単な方法のようです。

3
CasualCoder3

XMLファイルを読み取ります。

XMLファイルから読み取ることができます。メンテナンスと操作が簡単です。 Javaで数行のコードにするために、標準のSTaX、DOM、SAXパーサーが利用できます。

属性をさらに活用する

タグの属性を持ついくつかのセマンティック情報を使用して、SQLをさらに活用することができます。これは、メソッド名またはクエリタイプ、またはコーディングを減らすのに役立つ任意のものです。

維持

Xmlをjarの外に置いて、簡単に保守できます。プロパティファイルと同じ利点。

変換

XMLは拡張可能であり、他の形式に簡単に変換できます。

使用事例

Metamugはxmlを使用してREST sqlのリソースファイル)を構成します。

2
Sorter

SQL文字列をプロパティファイルに入れてから読むと、SQL文字列をプレーンテキストファイルに保存できます。

これはSQLタイプの問題を解決しませんが、少なくともTOADまたはsqlplusからのコピーと貼り付けをはるかに簡単にします。

1
Rowan

PreparedStatementsの長いSQL文字列(テキストファイルで簡単に指定でき、とにかくリソースとしてロードできる)を別として、複数行にまたがる文字列連結を取得するにはどうすればよいですか?

SQL文字列を直接作成していませんか?それはプログラミングの最大のノーノーです。 PreparedStatementsを使用し、データをパラメーターとして提供してください。 SQLインジェクションの可能性を大幅に減らします。

0
JeeBee