web-dev-qa-db-fra.com

Utilisation d'instructions préparées avec JDBCTemplate

J'utilise le modèle JDBC et je souhaite lire à partir d'une base de données à l'aide d'instructions préparées. J'itère sur plusieurs lignes dans un fichier .csv, et sur chaque ligne j'exécute certaines requêtes de sélection SQL avec les valeurs correspondantes.

Je veux accélérer ma lecture de la base de données mais je ne sais pas comment faire fonctionner le modèle JDBC avec des instructions préparées.

Il y a le PreparedStatementCreator et le PreparedStatementSetter . Comme dans cet exemple les deux sont créés avec des classes internes anonymes. Mais à l'intérieur de la classe PreparedStatementSetter, je n'ai pas accès aux valeurs que je veux définir dans l'instruction préparée.

Étant donné que j'itère dans un fichier .csv, je ne peux pas les coder en dur en tant que chaîne car je ne les connais pas. Je ne peux pas non plus les transmettre au PreparedStatementSetter car il n'y a aucun argument pour le constructeur. Et définir mes valeurs sur final serait aussi stupide.

J'étais habitué à ce que la création de déclarations préparées soit assez simple. Quelque chose comme

PreparedStatement updateSales = con.prepareStatement(
    "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75); 
updateSales.setString(2, "Colombian"); 
updateSales.executeUpdate():

comme dans ce tutoriel Java .

26
user321068

J'ai essayé une instruction select maintenant avec un PreparedStatement , mais il s'est avéré que ce n'était pas plus rapide que le modèle Jdbc. Peut-être, comme l'a suggéré mezmo, qu'il crée automatiquement des instructions préparées.

Quoi qu'il en soit, la raison pour laquelle mes sql SELECTs étaient si lents était une autre. Dans la clause WHERE, j'ai toujours utilisé l'opérateur LIKE, quand tout ce que je voulais faire était de trouver une correspondance exacte. Comme je l'ai découvert, LIKE recherche un modèle et est donc assez lent.

J'utilise l'opérateur = maintenant et c'est beaucoup plus rapide.

2
user321068

Par défaut, le JDBCTemplate fait son propre PreparedStatement en interne, si vous utilisez simplement le formulaire .update(String sql, Object ... args). Spring et votre base de données géreront la requête compilée pour vous, vous n'avez donc pas à vous soucier de l'ouverture, de la fermeture, de la protection des ressources, etc. L'une des grâces de sauvegarde de Spring. n lien vers la documentation de Spring 2.5 à ce sujet. J'espère que cela clarifiera les choses. En outre, la mise en cache des instructions peut être effectuée au niveau JDBC, comme dans le cas de au moins certains des pilotes JDBC d'Oracle. Cela entrera dans beaucoup plus de détails que je ne peux avec compétence.

28
mezmo
class Main {
    public static void main(String args[]) throws Exception {
        ApplicationContext ac = new
          ClassPathXmlApplicationContext("context.xml", Main.class);
        DataSource dataSource = (DataSource) ac.getBean("dataSource");
// DataSource mysqlDataSource = (DataSource) ac.getBean("mysqlDataSource");

        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);

        String prasobhName = 
        jdbcTemplate.query(
           "select first_name from customer where last_name like ?",
            new PreparedStatementSetter() {
              public void setValues(PreparedStatement preparedStatement) throws
                SQLException {
                  preparedStatement.setString(1, "nair%");
              }
            }, 
            new ResultSetExtractor<Long>() {
              public Long extractData(ResultSet resultSet) throws SQLException,
                DataAccessException {
                  if (resultSet.next()) {
                      return resultSet.getLong(1);
                  }
                  return null;
              }
            }
        );
        System.out.println(machaceksName);
    }
}
19
Prasobh.K

Essayez ce qui suit:

PreparedStatementCreator creator = new PreparedStatementCreator() {
    @Override
    public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
        PreparedStatement updateSales = con.prepareStatement(
        "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
        updateSales.setInt(1, 75); 
        updateSales.setString(2, "Colombian"); 
        return updateSales;
    }
};
10
Kevin

Je prendrais en compte la gestion des instructions préparées pour au moins une méthode. Dans ce cas, parce qu'il n'y a aucun résultat, c'est assez simple (et en supposant que la connexion est une variable d'instance qui ne change pas):

private PreparedStatement updateSales;
public void updateSales(int sales, String cof_name) throws SQLException {
    if (updateSales == null) {
        updateSales = con.prepareStatement(
            "UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
    }
    updateSales.setInt(1, sales);
    updateSales.setString(2, cof_name);
    updateSales.executeUpdate();
}

À ce stade, il suffit alors d'appeler:

updateSales(75, "Colombian");

Lequel est assez simple à intégrer avec d'autres choses, oui? Et si vous appelez la méthode plusieurs fois, la mise à jour ne sera construite qu'une seule fois et cela accélérera les choses. Eh bien, en supposant que vous ne faites pas de choses folles comme faire chaque mise à jour dans sa propre transaction ...

Notez que les types sont fixes. En effet, pour toute requête/mise à jour particulière, elles devraient être fixées de manière à permettre à la base de données de faire son travail efficacement. Si vous extrayez simplement des chaînes arbitraires d'un fichier CSV, passez-les sous forme de chaînes. Il n'y a pas non plus de verrouillage; il est préférable de conserver les connexions individuelles à partir d'un seul thread.

3
Donal Fellows