web-dev-qa-db-ja.com

Java-外部ファイルへのSQLステートメントの保存

SQLステートメントを外部ファイルに保存するJava library/framework/technique)を探しています。サポートチーム(DBAを含む)は、ステートメントを(わずかに)変更して保持する必要があります。データベーススキーマが変更された場合、またはチューニングのために同期します。

要件は次のとおりです。

  • ファイルは、Javaアプリケーションから読み取り可能である必要がありますが、派手なエディターを必要とせずにサポートチームが編集可能でなければなりません
  • ファイルはプレーンテキスト形式であることが理想ですが、XMLも問題ありません
  • DMLおよびDDLステートメントの保存/取得を許可する
  • 新しいステートメントは後の段階で追加できます(アプリケーションはそれらをピックアップして実行するのに十分な柔軟性があります)
  • ステートメントをグループ化することができます(アプリケーションによってグループとして実行されます)
  • ステートメントはパラメーターを許可する必要があります

ノート:

  • 取得されたステートメントは、SpringのJDBCTemplateを使用して実行されます
  • HibernateまたはSpringのIOCコンテナは使用されません

これまでのところ、SQLステートメントの保存に外部ファイルを使用するJavaライブラリを見つけることができました。ただし、すべてのJDBCの「複雑さ」を隠すライブラリではなく、主にストレージに興味があります。 。

  • Axamol SQLライブラリ

    サンプルファイルの内容:

    <s:query name="get_emp">
      <s:param name="name" type="string"/>
      <s:sql databases="Oracle">
        select    *
        from      scott.emp
                  join scott.dept on (emp.deptno = dept.deptno)
        where     emp.ename = <s:bind param="name"/>
      </s:sql>
    </s:query>
    
  • iBATIS

    サンプルファイルの内容:

    <sqlMap namespace="Contact"">
        <typeAlias alias="contact"
            type="com.sample.contact.Contact"/">
        <select id="getContact"
            parameterClass="int" resultClass="contact"">
                select CONTACTID as contactId,
                       FIRSTNAME as firstName,
                       LASTNAME as lastName from
                       ADMINISTRATOR.CONTACT where CONTACTID = #id#
        </select>
    </sqlMap>
    <insert id="insertContact" parameterClass="contact">
    INSERT INTO ADMINISTRATOR.CONTACT( CONTACTID,FIRSTNAME,LASTNAME)
            VALUES(#contactId#,#firstName#,#lastName#);
     </insert>
    <update id="updateContact" parameterClass="contact">
    update ADMINISTRATOR.CONTACT SET
    FIRSTNAME=#firstName# ,
    LASTNAME=#lastName#
    where contactid=#contactId#
    </update>
    <delete id="deleteContact" parameterClass="int">
    DELETE FROM ADMINISTRATOR.CONTACT WHERE CONTACTID=#contactId#
    </delete>
    
  • WEB4J

    -- This is a comment 
     ADD_MESSAGE   {
     INSERT INTO MyMessage -- another comment
      (LoginName, Body, CreationDate)
      -- another comment
      VALUES (?,?,?)
     }
    
    -- Example of referring to a constant defined above.
    FETCH_RECENT_MESSAGES {
     SELECT 
     LoginName, Body, CreationDate 
     FROM MyMessage 
     ORDER BY Id DESC LIMIT ${num_messages_to_view}
    }
    

誰もが試されてテストされたソリューションを推奨できますか?

45
Adrian

次のようなキーと値のペアを持つ単純なJavaプロパティファイルを作成するだけです。

users.select.all = select * from user

DAOクラスでプロパティタイプのプライベートフィールドを宣言し、ファイルから値を読み取るSpring構成を使用して挿入します。

[〜#〜] update [〜#〜]:複数行のSQLステートメントをサポートする場合は、次の表記を使用します。

users.select.all.0 = select *
users.select.all.1 = from   user
58
Boris Pavlović

mustを行う必要がある場合は、 MyBatis プロジェクトをご覧ください。私はそれを使用していませんが、何度も推奨されると聞きました。

SQLとJavaの分離は、SQLは実際にはコードであり、それを呼び出すJavaコードと密接に結びついています。メンテナンスとデバッグ分離されたコードは難しい場合があります。

このためにストアドプロシージャを絶対に使用しないでください。 DBとアプリケーション間のトラフィックを減らすことでパフォーマンスを向上させるためにのみ使用してください。

8
John Stauffer

これに直面したときに実装した簡単なソリューションは、SQL/DMLをファイル(mySql.properties)に外部化し、MessageFormat.format(String [] args)を使用して動的プロパティをSQLに注入することでした。

例:mySql.properties:

select    *
    from      scott.emp
              join scott.dept on (emp.deptno = dept.deptno)
    where     emp.ename = {0}

ユーティリティメソッド:

public static String format(String template, Object[] args) {
    String cleanedTemplate = replaceSingleQuotes(template);
    MessageFormat mf = new MessageFormat(cleanedTemplate);
    String output = mf.format(args);
    return output;
}
private static String replaceSingleQuotes(String template) {
    String cleaned = template.replace("'", "''");
    return cleaned;
}

その後、次のように使用します。

String sqlString = youStringReaderImpl("/path/to/file");
String parsedSql = format(sqlString, new String[] {"bob"});
7
Rich Kroll

ここに貼り付けてください answer to Spring jdbcを使用する場合に長い(SQLで20行)を外部化するクリーンな方法?

しばらく前に同じ問題に直面し、YAMLを思いつきました。複数行の文字列プロパティ値をサポートしているため、クエリファイルに次のように記述できます。

selectSomething: >
  SELECT column1, column2 FROM SOMETHING

insertSomething: >
  INSERT INTO SOMETHING(column1, column2)
  VALUES(1, '1')

ここで、selectSomethinginsertSomethingはクエリ名です。とても便利で、特殊文字はほとんど含まれていません。クエリは空白行で区切られ、各クエリテキストはインデントする必要があります。クエリには独自のインデントを絶対に含めることができるため、次のことは完全に有効です。

anotherSelect: <
  SELECT column1 FROM SOMETHING
  WHERE column2 IN (
    SELECT * FROM SOMETHING_ELSE
  )

次に、以下のコードを使用して、SnakeYAMLライブラリの助けを借りて、ファイルの内容をハッシュマップに読み込むことができます。

import org.Apache.commons.io.FilenameUtils;
import org.Apache.commons.io.FileUtils;
import Java.io.FileReader;

import org.yaml.snakeyaml.Yaml;
import Java.io.File;
import Java.io.FileNotFoundException;

public class SQLReader {
  private Map<String, Map> sqlQueries = new HashMap<String, Map>();

  private SQLReader() {
    try {
      final File sqlYmlDir = new File("dir_with_yml_files");
      Collection<File> ymlFiles = FileUtils.listFiles(sqlYmlDir, new String[]{"yml"}, false);
      for (File f : ymlFiles) {
        final String fileName = FilenameUtils.getBaseName(f.getName());
        Map ymlQueries = (Map)new Yaml().load(new FileReader(f));
        sqlQueries.put(fileName, ymlQueries);
      }
    }
    catch (FileNotFoundException ex) {
      System.out.println("File not found!!!");
    }
  }
}

上記の例では、マップのマップが作成され、各YAMLファイルがクエリ名/文字列を含むマップにマッピングされます。

6
siphiuel

ElSql ライブラリはこの機能を提供します。

ElSqlは、外部SQLファイル(elsql)をロードできる小さなjarファイル(6つのパブリッククラス)で構成されています。ファイルは単純な形式を使用して、オプションでファイルをロードするよりもわずかに多くの動作を提供します。

-- an example comment
@NAME(SelectBlogs)
  @PAGING(:paging_offset,:paging_fetch)
    SELECT @INCLUDE(CommonFields)
    FROM blogs
    WHERE id = :id
      @AND(:date)
        date > :date
      @AND(:active)
        active = :active
    ORDER BY title, author
@NAME(CommonFields)
  title, author, content

// Java code:
bundle.getSql("SelectBlogs", searchArgs);

ファイルは、コードから参照できる@NAMEブロックに分割されます。各ブロックは、重要な空白インデントによって定義されます。 @PAGINGは、FETCH/OFFSETなどのページングに必要なコードを挿入します。 @ANDは、指定された変数が存在する場合にのみ出力されます(動的検索の構築を支援します)。 DSLは、検索でのワイルドカードのLIKE vs =も処理します。オプションのDSLタグの目標は、動的なSQLをデータベースに依存しない方法で構築しようとするときによく見られる共通の基本を提供することです。

ブログ または ユーザーガイド の詳細。

5
JodaStephen

Apache Commons DbUtilsQueryLoader クラスを使用することもできます。これは、プロパティファイルからsqlを読み取ります。ただし、JDBCTemplateと同じ目的を果たすDbUtilsを使用する必要があります。

5
Ken Liu

Springを使用して、BeanファイルからSQLステートメントを保存し、Beanファクトリからクラスを取得したときに挿入されます。そのクラスは、コードを簡素化するために、Beanファイルを介して構成できるSimpleJDBCTemplateのインスタンスを使用することもできます。

3
user199218

Springのクラスを使用して行うのは簡単で信頼性があります。 SQLファイルを取得して、クラスパス上のある場所に保存します。これは、必要に応じてSQLのみを含むJARファイルにすることができます。次に、SpringのClassPathResourceを使用してファイルをストリームにロードし、ApacheIOUtilsを使用して文字列に変換します。その後、SimpleJdbcTemplateまたは任意のDBコードを使用してSQLを実行できます。

単純なJavaクラスを選択するユーティリティクラスを作成することをお勧めします。このクラスは、選択した規則に従ってSQLファイル名に対応するパブリック文字列フィールドを持ちます。その後、ClassPathResourceクラスは、命名規則に適合するSQLファイルを見つけて、文字列フィールドに割り当てます。その後、SQLが必要なときにクラスフィールドを参照します。また、使い古したクラスとテクニックを使用します。何も空想していません。数年前にそれをやりました。素晴らしい作品です。コードを手に入れるのが面倒です。自分でそれを理解する時間はありません。

2
marcus none

これを行うには、ローカライズ機能を使用できます。次に、データベースの名前をロケールとして使用して、英語またはフランス語のバージョンではなく、「oraclish」バージョンの「insert-foo-in-bar」を取得します。

通常、翻訳はプロパティファイルに保存され、これらのプロパティファイルの編集を許可することにより、アプリケーションをローカライズするための優れたツールがあります。

ストアドプロシージャを使用することを強くお勧めします。この種のことはまさに彼らの目的です。

1
Dave

dynamic-query は、JDBCとORMの間に何かを望む人のための優れたオープンソースフレームワークです。

1つのプレーンSQL。 -プレーンSQLを外部ファイルに保存します。冗長タグなし、コメントをサポートします。

/* It also supports comment.
This code is in an external file 'sample.sql', Not inisde Java code.*/
listUsers : select * from user_table
where user_id= $$;  /* $$ will automatically catch a parameter userId */


2つの拡張可能なSQL。 -他のファイルやサブクエリなどのパラメーターをサポートします。

listUsers:
select
    id, amount, created
    @checkEmail{ ,email } 
from user_table
where amount > $amt and balance < $amt
    @checkDate { and created = $$ }
    @checkEmail{ and email in (
        select email from vip_list ) } ;        
/* Above query can be four queries like below.
1. listUsers
2. listUsers.checkDate 
3. listUsers.checkEmail
4. listUsers.checkDate.checkEmail 
*/



-- It can include other files like below
& ../hr/additional hr.sql ; 
& ../fi/additional fi.sql ;


上記を使用したJavaのサンプルコード。 dbに値を設定します。

QueryUtil qu = qm.createQueryUtil("selectAll");
try {
    qu.setConnection(conn);

    // with native jdbc
    qu.setString("alpha");
    qu.setDouble(10.1);
    qu.executeQuery();

    // or with bean
    qu.executeQuery(new User("alpha", 10.1));

    // or with map
    Map<String, Object> map=new HashMap<String, Object>();
    map.put("userName", "alpha");
    map.put("amt", 10.1);
    qu.executeQuery(map);

    // or with array
    qu.executeQueryParameters("alpha", 10.1);

上記を使用したJavaのサンプルコード。 dbから値を取得します。

    while (qu.next()) // == qu.rs.next()
    {
        // native jdbc
        String usreName = qu.getString("user_name"); 
        double amt = qu.getDouble("amt");

        // or bean
        User user = new User();
        qu.updateBean(user);

        // or array
        Object[] values = qu.populateArray();
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    qu.closeJust();
}
1
user3408022

Velocityを使用すると、「スクリプト化可能な」SQLテンプレートを使用して、柔軟にファイルを操作できます。 sqlコマンドを作成するための条件やループなどのプリミティブステートメントがあります。

ただし、準備済みステートメントやストアドプロシージャを使用することを強くお勧めします。計画している方法でSQLを構築すると、SQLインジェクションに対して脆弱になり、DBサーバーはSQLクエリをキャッシュできなくなります(パフォーマンスが低下します)。

BTW:準備されたステートメントの定義もファイルに保存できます。これは最良の解決策ではありませんが、それに近いものであり、SQLインジェクションの保護とパフォーマンスの利点を得ることができます。

SQLスキーマが準備済みステートメントまたはストアドプロシージャで動作するように構築されていない場合、スキーマを再考する必要があります。たぶんそれはリファクタリングする必要があります。

0