web-dev-qa-db-fra.com

Le moyen le plus propre de construire une chaîne SQL dans Java

Je veux construire une chaîne SQL pour manipuler la base de données (mises à jour, suppressions, insertions, sélections, ce genre de chose) - au lieu de la méthode de concaténation de chaînes horrible utilisant des millions de "+" et des guillemets illisibles au mieux - il doit être un meilleur moyen.

J'ai effectivement pensé à utiliser MessageFormat - mais il est supposé être utilisé pour les messages utilisateur, même si je pense que cela ferait un travail raisonnable - mais je suppose qu'il devrait y avoir quelque chose de plus aligné sur les opérations de type SQL dans le Java sql bibliothèques.

Groovy serait-il bon?

95
Vidar

Tout d'abord, envisagez d'utiliser des paramètres de requête dans les instructions préparées:

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

L'autre chose à faire est de conserver toutes les requêtes dans le fichier de propriétés. Par exemple, un fichier queries.properties peut placer la requête ci-dessus:

update_query=UPDATE user_table SET name=? WHERE id=?

Puis à l'aide d'une classe d'utilitaire simple:

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);
    }

}

vous pouvez utiliser vos requêtes comme suit:

PreparedStatement stm = c.prepareStatement(Queries.getQuery("update_query"));

C'est une solution assez simple, mais qui fonctionne bien.

67
Piotr Kochański

Pour du SQL arbitraire, utilisez jOOQ . jOOQ prend actuellement en charge SELECT, INSERT, UPDATE, DELETE, TRUNCATE et MERGE. Vous pouvez créer du SQL comme ceci:

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();

Au lieu d'obtenir la chaîne SQL, vous pouvez également simplement l'exécuter à l'aide de jOOQ. Voir

http://www.jooq.org

(Avertissement: je travaille pour l'entreprise derrière jOOQ)

59
Lukas Eder

Une technologie à considérer est SQLJ - un moyen d'intégrer des instructions SQL directement en Java. Par exemple, vous pourriez avoir les éléments suivants dans un fichier appelé TestQueries.sqlj:

public class TestQueries
{
    public String getUsername(int id)
    {
        String username;
        #sql
        {
            select username into :username
            from users
            where pkey = :id
        };
        return username;
    }
}

Il existe une étape supplémentaire de précompilation qui prend vos fichiers .sqlj et les traduit en pure Java - en bref, il recherche les blocs spéciaux délimités par

#sql
{
    ...
}

et les transforme en appels JDBC. L'utilisation de SQLJ présente plusieurs avantages clés:

  • élimine complètement la couche JDBC - les programmeurs doivent seulement penser à Java et SQL
  • le traducteur peut être amené à vérifier vos requêtes pour la syntaxe, etc. par rapport à la base de données au moment de la compilation
  • possibilité de lier directement Java dans des requêtes utilisant le préfixe ":"

Il existe des implémentations du traducteur pour la plupart des principaux fournisseurs de bases de données. Vous devriez donc pouvoir trouver facilement tout ce dont vous avez besoin.

14
Ashley Mercer

Je me demande si vous recherchez quelque chose comme Squiggle . Aussi, quelque chose de très utile est jDBI . Cela ne vous aidera pas avec les requêtes cependant.

12
tcurdt

Je voudrais jeter un oeil à Spring JDBC . Je l'utilise chaque fois que j'ai besoin d'exécuter des programmes SQL par programme. Exemple:

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

C'est vraiment génial pour tout type d'exécution SQL, en particulier les requêtes; cela vous aidera à mapper les résultats à des objets, sans ajouter la complexité d'un ORM complet.

9
Bent André Solheim

J'ai tendance à utiliser les paramètres JDBC nommés de Spring pour pouvoir écrire une chaîne standard telle que "select * from blah where colX = ': someValue'"; Je pense que c'est assez lisible.

Une alternative serait de fournir la chaîne dans un fichier .sql séparé et de lire le contenu à l'aide d'une méthode utilitaire.

Oh, cela vaut également la peine de regarder Squill: https://squill.dev.Java.net/docs/tutorial.html

5
GaryF

J'appuie les recommandations d'utilisation d'un ORM comme Hibernate. Cependant, il y a certainement des situations où cela ne fonctionne pas, je vais donc saisir cette occasion pour vanter certaines choses que j'ai aidé à écrire: SqlBuilder est une bibliothèque Java pour construire dynamiquement des instructions SQL en utilisant le style "constructeur". c'est assez puissant et assez flexible.

4
james

Pourquoi voulez-vous générer tout le sql à la main? Avez-vous regardé un ORM comme Hibernate? Selon votre projet, il fera probablement au moins 95% de vos besoins, utilisez-le de manière plus claire que le SQL brut, et si vous avez besoin d'obtenir les dernières performances, vous pouvez créer le Requêtes SQL devant être ajustées manuellement.

4
Jared

Je travaille sur une servlet Java) qui doit construire des instructions SQL très dynamiques à des fins de rapport ad hoc. La fonction de base de l'application consiste à alimenter une série de paramètres de requête HTTP nommés J'ai utilisé Spring MVC et le framework d'injection de dépendances pour stocker toutes mes requêtes SQL dans des fichiers XML et les charger dans l'application de génération de rapports, avec les informations de formatage de table. Les exigences en matière de rapport sont devenues plus compliquées que les capacités des cadres de mappage de paramètres existants et je devais écrire le mien.

Les nouveaux mappages de paramètres ressemblaient à ceci:

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

L'intérêt du framework résultant était qu'il pouvait traiter les paramètres de requête HTTP directement dans la requête avec le contrôle de type et le contrôle de limite appropriés. Aucun mappage supplémentaire requis pour la validation de l'entrée. Dans l'exemple de requête ci-dessus, le paramètre nommé serverId doit être vérifié pour s'assurer qu'il peut être converti en un entier et qu'il est compris entre 0 et 50. Le paramètre appId serait traité comme un tableau d'entiers, avec une limite de longueur de 50. Si le champ showOwner est présent et défini sur "true", les bits de SQL dans les guillemets seront ajoutés à la requête générée pour les mappages de champs facultatifs. field Plusieurs autres mappages de types de paramètres sont disponibles, y compris des segments facultatifs de SQL avec d'autres mappages de paramètres. Il permet un mappage de requête aussi complexe que le développeur peut en concevoir. Il a même des contrôles dans la configuration du rapport pour déterminer si une requête donnée aura les mappages finaux via un PreparedStatement ou s’exécutera simplement en tant que requête prédéfinie.

Pour les exemples de valeurs de demande HTTP:

showOwner: true
serverId: 20
appId: 1,2,3,5,7,11,13

Il produirait le code SQL suivant:

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

Je pense vraiment que Spring ou Hibernate ou l’un de ces frameworks devraient offrir un mécanisme de mappage plus robuste qui vérifie les types, permet des types de données complexes tels que les tableaux et autres. J'ai écrit mon moteur uniquement pour mes besoins, il n'est pas tout à fait lu pour une version générale. Cela fonctionne uniquement avec les requêtes Oracle pour le moment et tout le code appartient à une grande entreprise. Un jour, je pourrai prendre mes idées et créer un nouveau cadre open source, mais j'espère qu'un des gros joueurs existants relèvera le défi.

3
Natalia

Vous pouvez également consulter MyBatis ( www.mybatis.org ). Il vous aide à écrire des instructions SQL en dehors de votre code Java et mappe les résultats SQL dans vos objets Java, entre autres choses].

3
joshua

Google fournit une bibliothèque appelée = Bibliothèque de la salle de la pièce qui fournit un moyen très propre d’écrire SQL. Ci-dessous un extrait de code du site officiel:

@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);
}

Il y a plus d'exemples et une meilleure documentation dans la documentation officielle de la bibliothèque.

Il y a aussi un appelé MentaBean qui est un Java ORM . Il a de jolies fonctionnalités et semble être un moyen assez simple d’écrire en SQL.

3
CasualCoder3

Lire un fichier XML.

Vous pouvez le lire à partir d'un fichier XML. Il est facile à entretenir et à utiliser. Des analyseurs syntaxiques standard STaX, DOM et SAX sont disponibles pour en faire quelques lignes de code en Java.

Faire plus avec les attributs

Vous pouvez avoir des informations sémantiques avec des attributs sur la balise pour vous aider à utiliser davantage le code SQL. Cela peut être le nom de la méthode ou le type de requête ou tout ce qui peut vous aider à coder moins.

Maintenir

Vous pouvez placer le fichier XML à l'extérieur du pot et le maintenir facilement. Mêmes avantages qu'un fichier de propriétés.

Conversion

XML est extensible et facilement convertible en d'autres formats.

Cas d'utilisation

Metamug utilise xml pour configurer REST de ressources avec SQL.

2
Sorter

Si vous placez les chaînes SQL dans un fichier de propriétés, puis que vous les lisez, vous pouvez conserver les chaînes SQL dans un fichier texte.

Cela ne résout pas les problèmes de type SQL, mais au moins, cela facilite beaucoup le copier-coller depuis TOAD ou sqlplus.

1
Rowan

Comment obtenir une concaténation de chaînes, outre les longues chaînes SQL de PreparedStatements (que vous pouvez facilement fournir dans un fichier texte et charger de toute façon comme ressource), que vous séparez sur plusieurs lignes?

Vous ne créez pas directement de chaînes SQL, n'est-ce pas? C'est le plus grand non-non en programmation. Veuillez utiliser PreparedStatements et fournir les données en tant que paramètres. Cela réduit considérablement le risque d'injection SQL.

0
JeeBee