SQLステートメントを外部ファイルに保存するJava library/framework/technique)を探しています。サポートチーム(DBAを含む)は、ステートメントを(わずかに)変更して保持する必要があります。データベーススキーマが変更された場合、またはチューニングのために同期します。
要件は次のとおりです。
ノート:
これまでのところ、SQLステートメントの保存に外部ファイルを使用するJavaライブラリを見つけることができました。ただし、すべてのJDBCの「複雑さ」を隠すライブラリではなく、主にストレージに興味があります。 。
サンプルファイルの内容:
<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>
サンプルファイルの内容:
<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>
-- 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}
}
誰もが試されてテストされたソリューションを推奨できますか?
次のようなキーと値のペアを持つ単純なJavaプロパティファイルを作成するだけです。
users.select.all = select * from user
DAOクラスでプロパティタイプのプライベートフィールドを宣言し、ファイルから値を読み取るSpring構成を使用して挿入します。
[〜#〜] update [〜#〜]:複数行のSQLステートメントをサポートする場合は、次の表記を使用します。
users.select.all.0 = select *
users.select.all.1 = from user
mustを行う必要がある場合は、 MyBatis プロジェクトをご覧ください。私はそれを使用していませんが、何度も推奨されると聞きました。
SQLとJavaの分離は、SQLは実際にはコードであり、それを呼び出すJavaコードと密接に結びついています。メンテナンスとデバッグ分離されたコードは難しい場合があります。
このためにストアドプロシージャを絶対に使用しないでください。 DBとアプリケーション間のトラフィックを減らすことでパフォーマンスを向上させるためにのみ使用してください。
これに直面したときに実装した簡単なソリューションは、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"});
ここに貼り付けてください answer to Spring jdbcを使用する場合に長い(SQLで20行)を外部化するクリーンな方法? :
しばらく前に同じ問題に直面し、YAMLを思いつきました。複数行の文字列プロパティ値をサポートしているため、クエリファイルに次のように記述できます。
selectSomething: >
SELECT column1, column2 FROM SOMETHING
insertSomething: >
INSERT INTO SOMETHING(column1, column2)
VALUES(1, '1')
ここで、selectSomething
とinsertSomething
はクエリ名です。とても便利で、特殊文字はほとんど含まれていません。クエリは空白行で区切られ、各クエリテキストはインデントする必要があります。クエリには独自のインデントを絶対に含めることができるため、次のことは完全に有効です。
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ファイルがクエリ名/文字列を含むマップにマッピングされます。
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をデータベースに依存しない方法で構築しようとするときによく見られる共通の基本を提供することです。
Apache Commons DbUtils で QueryLoader クラスを使用することもできます。これは、プロパティファイルからsqlを読み取ります。ただし、JDBCTemplateと同じ目的を果たすDbUtilsを使用する必要があります。
Springを使用して、BeanファイルからSQLステートメントを保存し、Beanファクトリからクラスを取得したときに挿入されます。そのクラスは、コードを簡素化するために、Beanファイルを介して構成できるSimpleJDBCTemplateのインスタンスを使用することもできます。
Springのクラスを使用して行うのは簡単で信頼性があります。 SQLファイルを取得して、クラスパス上のある場所に保存します。これは、必要に応じてSQLのみを含むJARファイルにすることができます。次に、SpringのClassPathResourceを使用してファイルをストリームにロードし、ApacheIOUtilsを使用して文字列に変換します。その後、SimpleJdbcTemplateまたは任意のDBコードを使用してSQLを実行できます。
単純なJavaクラスを選択するユーティリティクラスを作成することをお勧めします。このクラスは、選択した規則に従ってSQLファイル名に対応するパブリック文字列フィールドを持ちます。その後、ClassPathResourceクラスは、命名規則に適合するSQLファイルを見つけて、文字列フィールドに割り当てます。その後、SQLが必要なときにクラスフィールドを参照します。また、使い古したクラスとテクニックを使用します。何も空想していません。数年前にそれをやりました。素晴らしい作品です。コードを手に入れるのが面倒です。自分でそれを理解する時間はありません。
これを行うには、ローカライズ機能を使用できます。次に、データベースの名前をロケールとして使用して、英語またはフランス語のバージョンではなく、「oraclish」バージョンの「insert-foo-in-bar」を取得します。
通常、翻訳はプロパティファイルに保存され、これらのプロパティファイルの編集を許可することにより、アプリケーションをローカライズするための優れたツールがあります。
ストアドプロシージャを使用することを強くお勧めします。この種のことはまさに彼らの目的です。
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();
}
Velocityを使用すると、「スクリプト化可能な」SQLテンプレートを使用して、柔軟にファイルを操作できます。 sqlコマンドを作成するための条件やループなどのプリミティブステートメントがあります。
ただし、準備済みステートメントやストアドプロシージャを使用することを強くお勧めします。計画している方法でSQLを構築すると、SQLインジェクションに対して脆弱になり、DBサーバーはSQLクエリをキャッシュできなくなります(パフォーマンスが低下します)。
BTW:準備されたステートメントの定義もファイルに保存できます。これは最良の解決策ではありませんが、それに近いものであり、SQLインジェクションの保護とパフォーマンスの利点を得ることができます。
SQLスキーマが準備済みステートメントまたはストアドプロシージャで動作するように構築されていない場合、スキーマを再考する必要があります。たぶんそれはリファクタリングする必要があります。