Je recherche une Java/framework/technique de stockage des instructions SQL dans un fichier externe. L'équipe de support (y compris les DBA) devrait être en mesure de modifier (légèrement) l'instruction pour les conserver dans synchroniser en cas de modification du schéma de la base de données ou à des fins de réglage.
Voici les prérequis:
Remarques:
Jusqu'à présent, j'ai réussi à trouver les bibliothèques Java Java, qui utilisent des fichiers externes pour stocker les instructions SQL. Cependant, je suis principalement intéressé par le stockage plutôt que par une bibliothèque qui cache toutes les "complexités" JDBC .
Exemple de contenu de fichier:
<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>
Exemple de contenu de fichier:
<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}
}
Quelqu'un peut-il recommander une solution qui a fait ses preuves?
Créez simplement un simple Java avec des paires clé-valeur comme celle-ci:
users.select.all = select * from user
Déclarez un champ privé de type Propriétés dans votre classe DAO et injectez-le à l'aide de la configuration Spring qui lira les valeurs du fichier.
UPDATE : si vous souhaitez prendre en charge les instructions SQL sur plusieurs lignes, utilisez cette notation:
users.select.all.0 = select *
users.select.all.1 = from user
Si vous devez faire cela, vous devriez regarder le projet MyBatis . Je ne l'ai pas utilisé, mais je l'ai entendu à plusieurs reprises.
Séparer SQL et Java n'est pas mon approche préférée, car SQL est en fait du code, et est étroitement couplé au code Java qui l'appelle. Maintenance et débogage) le code séparé peut être difficile.
N'utilisez absolument pas de proc stockés pour cela. Ils ne doivent être utilisés que pour améliorer les performances en réduisant le trafic entre la base de données et l'application.
Une solution simple que nous avons implémentée face à cela était d'externaliser le SQL/DML dans un fichier (mySql.properties), puis d'utiliser MessageFormat.format (String [] args) pour injecter des propriétés dynamiques dans le SQL.
Par exemple: mySql.properties:
select *
from scott.emp
join scott.dept on (emp.deptno = dept.deptno)
where emp.ename = {0}
Méthodes utilitaires:
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;
}
Ensuite, utilisez-le comme ceci:
String sqlString = youStringReaderImpl("/path/to/file");
String parsedSql = format(sqlString, new String[] {"bob"});
Coller ici mon answer to Clean way to externalize long (+20 lines sql) when using spring jdbc? :
J'ai fait face au même problème il y a quelque temps et j'ai trouvé YAML. Il prend en charge les valeurs de propriété de chaîne multi-lignes, vous pouvez donc écrire quelque chose comme ceci dans vos fichiers de requête:
selectSomething: >
SELECT column1, column2 FROM SOMETHING
insertSomething: >
INSERT INTO SOMETHING(column1, column2)
VALUES(1, '1')
Ici, selectSomething
et insertSomething
sont des noms de requête. C'est donc très pratique et contient très peu de caractères spéciaux. Les requêtes sont séparées par des lignes vides et chaque texte de requête doit être mis en retrait. Notez que les requêtes peuvent absolument contenir leur indentation, de sorte que ce qui suit est parfaitement valide:
anotherSelect: <
SELECT column1 FROM SOMETHING
WHERE column2 IN (
SELECT * FROM SOMETHING_ELSE
)
Vous pouvez ensuite lire le contenu du fichier dans une table de hachage à l'aide de la bibliothèque SnakeYAML, en utilisant le code ci-dessous:
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!!!");
}
}
}
Dans l'exemple ci-dessus, une carte de cartes est créée, mappant chaque fichier YAML sur une carte contenant des noms/chaînes de requête.
La bibliothèque ElSql fournit cette fonctionnalité.
ElSql consiste en un petit fichier jar (six classes publiques) qui permet de charger un fichier SQL externe (elsql). Le fichier utilise un format simple pour fournir éventuellement un comportement légèrement supérieur au simple chargement d'un fichier:
-- 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);
Le fichier est divisé en @NAME
blocs auxquels on peut se référer à partir du code. Chaque bloc est défini par une indentation importante des espaces blancs. @PAGING
insérera le code nécessaire pour la pagination tel que FETCH/OFFSET. @AND
ne sera affiché que si la variable spécifiée existe (ce qui aide à construire des recherches dynamiques). La DSL gère également LIKE
vs =
pour les caractères génériques dans les recherches. Le but des balises DSL facultatives est de fournir les principes de base communs qui se produisent souvent lorsque vous essayez de créer du SQL dynamique d'une manière neutre pour la base de données.
Plus d'informations sur le blog ou guide de l'utilisateur .
Vous pouvez également utiliser la classe QueryLoader dans Apache Commons DbUtils , qui lira le sql à partir d'un fichier de propriétés. Cependant, vous devrez utiliser DbUtils qui sert en quelque sorte le même but que le JDBCTemplate.
Vous pouvez utiliser Spring et avoir vos instructions sql stockées dans votre fichier beans qui sont injectées lorsque vous obtenez la classe de votre fabrique de beans. Cette classe peut également utiliser une instance de SimpleJDBCTemplate qui peut être configurée via le fichier bean pour simplifier votre code.
C'est simple et fiable de faire en utilisant des classes de Spring. Prenez vos fichiers SQL et enregistrez-les dans un emplacement sur votre chemin de classe. Cela peut être dans un fichier JAR qui ne contient que du SQL si vous le souhaitez. Ensuite, utilisez Spring's ClassPathResource pour charger le fichier dans un flux et utilisez Apache IOUtils pour le convertir en chaîne. Vous pouvez ensuite exécuter le SQL en utilisant SimpleJdbcTemplate, ou le code DB de votre choix.
Je vous suggère de créer une classe utilitaire qui prend une simple classe Java avec des champs String publics qui correspondent aux noms de fichiers SQL suivant une convention de votre choix. Ensuite, utilisez la réflexion conjointement avec le ClassPathResource classe pour rechercher les fichiers SQL conformes à votre convention de dénomination et les affecter aux champs String. Ensuite, faites simplement référence aux champs de classe lorsque vous avez besoin de SQL. C'est simple, fonctionne très bien et atteint l'objectif que vous veulent. Il utilise également des classes et des techniques bien portées. Rien d'extraordinaire. Je l'ai fait il y a quelques années. Fonctionne très bien. Trop paresseux pour aller chercher le code. Vous n'aurez pas le temps de le découvrir vous-même.
Vous pouvez utiliser les fonctionnalités de localisation pour ce faire. Vous utilisez ensuite le nom de la base de données comme paramètres régionaux pour obtenir la version "oraclish" de "insert-foo-in-bar" au lieu de la version anglaise ou française.
Les traductions sont généralement stockées dans des fichiers de propriétés, et il existe de bons outils pour localiser les applications en permettant de modifier ces fichiers de propriétés.
Je vous encourage fortement à utiliser les procédures stockées. Ce genre de chose est exactement ce à quoi ils servent.
dynamic-query est un bon framework open source pour ceux qui veulent quelque chose entre JDBC et ORM.
1 SQL simple. - Il enregistre SQL brut dans des fichiers externes. pas de balises redondantes, prend en charge les commentaires.
/* 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 extensible. -Il prend en charge les paramètres, y compris d'autres fichiers et sous-requête.
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 ;
Exemple de code Java utilisant ci-dessus. définition des valeurs sur 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);
Exemple de code Java utilisant ci-dessus. obtenir des valeurs de 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();
}
Vous pouvez utiliser velocity pour avoir des modèles sql "scriptables" que vous pouvez utiliser pour travailler avec les fichiers de manière flexible. Vous avez des instructions primitives comme des conditionnelles et des boucles pour construire vos commandes sql.
Mais je suggère fortement d'utiliser des instructions préparées et/ou des procédures stockées. Construire votre SQL comme vous le planifiez vous rendra vulnérable à l'injection SQL, le serveur de base de données ne pourra pas mettre en cache les requêtes SQL (ce qui entraînera de mauvaises performances).
BTW: Vous pouvez également stocker la définition des instructions préparées dans des fichiers. Ce n'est pas la meilleure solution mais assez proche d'elle et vous bénéficiez de la protection et des performances d'injection SQL.
Lorsque votre schéma SQL n'est pas construit pour fonctionner avec des instructions préparées ou des procédures stockées, vous souhaiterez peut-être repenser votre schéma. Peut-être qu'il doit être refactorisé.