Spring JDBC

Spring JDBC

1. Vue d'ensemble

Dans cet article, nous allons passer en revue des cas d'utilisation pratiques du module Spring JDBC.

Toutes les classes de Spring JDBC sont divisées en quatre packages distincts:

  • core - la fonctionnalité principale de JDBC. Certaines des classes importantes de ce package incluentJdbcTemplate,SimpleJdbcInsert,SimpleJdbcCall etNamedParameterJdbcTemplate.

  • datasource - classes utilitaires pour accéder à une source de données. Il dispose également de différentes implémentations de sources de données pour tester le code JDBC en dehors du conteneur Java EE.

  • object - Accès à la base de données d'une manière orientée objet. Il permet d'exécuter des requêtes et de renvoyer les résultats sous forme d'objet métier. Il mappe également les résultats de la requête entre les colonnes et les propriétés des objets métier.

  • support - prend en charge les classes pour les classes sous les packagescore etobject. E.g. fournit la fonctionnalité de traduction deSQLException.

    == Lectures complémentaires:

Spring Security: Exploration de l'authentification JDBC

Explorez les fonctionnalités offertes par Spring pour effectuer l'authentification JDBC à l'aide d'une configuration DataSource existante.

Read more

Introduction à Spring Data JPA

Introduction à Spring Data JPA avec Spring 4 - la configuration de Spring, le DAO, les requêtes manuelles et générées et la gestion des transactions.

Read more

2. Configuration

Pour commencer, commençons par une configuration simple de la source de données (nous utiliserons une base de données MySQL pour cet exemple):

@Configuration
@ComponentScan("com.example.jdbc")
public class SpringJdbcConfig {
    @Bean
    public DataSource mysqlDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/springjdbc");
        dataSource.setUsername("guest_user");
        dataSource.setPassword("guest_password");

        return dataSource;
    }
}

Alternativement, nous pouvons également utiliser une base de données intégrée pour le développement ou les tests. Voici une configuration rapide qui crée une instance de la base de données intégrée H2 et la pré-remplit avec de simples scripts SQL:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
      .setType(EmbeddedDatabaseType.H2)
      .addScript("classpath:jdbc/schema.sql")
      .addScript("classpath:jdbc/test-data.sql").build();
}

Enfin - la même chose peut, bien sûr, être faite en utilisant la configuration XML pour lesdatasource:


    
    
    
    

3. LesJdbcTemplate et les requêtes en cours

3.1. Requêtes de base

Le modèle JDBC est la principale API grâce à laquelle nous accéderons à la plupart des fonctionnalités qui nous intéressent:

  • création et fermeture de connexions

  • exécution d'instructions et d'appels de procédures stockées

  • itération sur lesResultSet et retour des résultats

Tout d'abord, commençons par un exemple simple pour voir ce que lesJdbcTemplate peuvent faire:

int result = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM EMPLOYEE", Integer.class);

et voici également un simple INSERT:

public int addEmplyee(int id) {
    return jdbcTemplate.update(
      "INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)", id, "Bill", "Gates", "USA");
}

Notez la syntaxe standard de la fourniture de paramètres - en utilisant le caractère?. Ensuite, examinons une alternative à cette syntaxe.

3.2. Requêtes avec des paramètres nommés

Pour obtenirsupport for named parameters, nous utiliserons l'autre modèle JDBC fourni par le framework - lesNamedParameterJdbcTemplate.

De plus, cela encapsule lesJbdcTemplate et fournit une alternative à la syntaxe traditionnelle utilisant «?» pour spécifier les paramètres. Sous le capot, il remplace les paramètres nommés par JDBC «?» espace réservé et délègue auxJDCTemplate encapsulés pour exécuter les requêtes:

SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("id", 1);
return namedParameterJdbcTemplate.queryForObject(
  "SELECT FIRST_NAME FROM EMPLOYEE WHERE ID = :id", namedParameters, String.class);

Remarquez comment nous utilisons lesMapSqlParameterSource pour fournir les valeurs des paramètres nommés.

Par exemple, regardons l'exemple ci-dessous qui utilise les propriétés d'un bean pour déterminer les paramètres nommés:

Employee employee = new Employee();
employee.setFirstName("James");

String SELECT_BY_ID = "SELECT COUNT(*) FROM EMPLOYEE WHERE FIRST_NAME = :firstName";

SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(employee);
return namedParameterJdbcTemplate.queryForObject(
  SELECT_BY_ID, namedParameters, Integer.class);

Notez comment nous utilisons maintenant les implémentations deBeanPropertySqlParameterSource au lieu de spécifier les paramètres nommés manuellement comme auparavant.

3.3. Mappage des résultats de la requête vers un objet Java

Une autre fonctionnalité très utile est la possibilité de mapper les résultats de la requête aux objets Java - en implémentant l'interfacethe RowMapper.

Par exemple, pour chaque ligne renvoyée par la requête, Spring utilise le mappeur de lignes pour renseigner le bean java:

public class EmployeeRowMapper implements RowMapper {
    @Override
    public Employee mapRow(ResultSet rs, int rowNum) throws SQLException {
        Employee employee = new Employee();

        employee.setId(rs.getInt("ID"));
        employee.setFirstName(rs.getString("FIRST_NAME"));
        employee.setLastName(rs.getString("LAST_NAME"));
        employee.setAddress(rs.getString("ADDRESS"));

        return employee;
    }
}

Par la suite, nous pouvons maintenant transmettre le mappeur de lignes à l'API de requête et obtenir des objets Java entièrement remplis:

String query = "SELECT * FROM EMPLOYEE WHERE ID = ?";
List employees = jdbcTemplate.queryForObject(
  query, new Object[] { id }, new EmployeeRowMapper());

4. Traduction d'exception

Spring est livré avec sa propre hiérarchie d'exceptions de données prête à l'emploi - avecDataAccessException comme exception racine - et il traduit toutes les exceptions brutes sous-jacentes.

Et donc nous gardons notre bon sens en n'ayant pas à gérer les exceptions de persistance de bas niveau et bénéficions du fait que Spring enveloppe les exceptions de bas niveau dansDataAccessException ou l'une de ses sous-classes.

De plus, le mécanisme de gestion des exceptions reste indépendant de la base de données sous-jacente utilisée.

En plus, leSQLErrorCodeSQLExceptionTranslator par défaut, nous pouvons également fournir notre propre implémentation deSQLExceptionTranslator.

Voici un exemple rapide d'implémentation personnalisée, personnalisant le message d'erreur en cas de violation de clé en double, ce qui entraîneerror code 23505 lors de l'utilisation de H2:

public class CustomSQLErrorCodeTranslator extends SQLErrorCodeSQLExceptionTranslator {
    @Override
    protected DataAccessException
      customTranslate(String task, String sql, SQLException sqlException) {
        if (sqlException.getErrorCode() == 23505) {
          return new DuplicateKeyException(
            "Custom Exception translator - Integrity constraint violation.", sqlException);
        }
        return null;
    }
}

Pour utiliser ce traducteur d'exceptions personnalisé, nous devons le passer auJdbcTemplate en appelant la méthodesetExceptionTranslator():

CustomSQLErrorCodeTranslator customSQLErrorCodeTranslator =
  new CustomSQLErrorCodeTranslator();
jdbcTemplate.setExceptionTranslator(customSQLErrorCodeTranslator);

5. Opérations JDBC à l'aide des classes SimpleJdbc

Les classesSimpleJdbc fournissent un moyen simple de configurer et d'exécuter des instructions SQL. Ces classes utilisent des métadonnées de base de données pour créer des requêtes de base. Les classesSimpleJdbcInsert etSimpleJdbcCall fournissent un moyen plus simple d'exécuter des appels d'insertion et de procédure stockée.

5.1. SimpleJdbcInsert

Jetons un coup d'œil à l'exécution d'instructions d'insertion simples avec une configuration minimale.

The INSERT statement is generated based on the configuration of SimpleJdbcInsert et tout ce dont nous avons besoin est de fournir le nom de la table, les noms de colonne et les valeurs.

Commençons par créer unSimpleJdbcInsert:

SimpleJdbcInsert simpleJdbcInsert =
  new SimpleJdbcInsert(dataSource).withTableName("EMPLOYEE");

Ensuite, fournissons les noms et valeurs des colonnes, puis exécutons l'opération:

public int addEmplyee(Employee emp) {
    Map parameters = new HashMap();
    parameters.put("ID", emp.getId());
    parameters.put("FIRST_NAME", emp.getFirstName());
    parameters.put("LAST_NAME", emp.getLastName());
    parameters.put("ADDRESS", emp.getAddress());

    return simpleJdbcInsert.execute(parameters);
}

De plus, pour autoriser lesdatabase to generate the primary key, nous pouvons utiliser l'APIexecuteAndReturnKey(); nous devrons également configurer la colonne réelle qui est générée automatiquement:

SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource)
                                        .withTableName("EMPLOYEE")
                                        .usingGeneratedKeyColumns("ID");

Number id = simpleJdbcInsert.executeAndReturnKey(parameters);
System.out.println("Generated id - " + id.longValue());

Enfin - nous pouvons également transmettre ces données en utilisant lesBeanPropertySqlParameterSource etMapSqlParameterSource.

5.2. Procédures stockées avecSimpleJdbcCall

Jetons également un œil à l'exécution des procédures stockées - nous utiliserons l'abstraction deSimpleJdbcCall:

SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource)
                             .withProcedureName("READ_EMPLOYEE");
public Employee getEmployeeUsingSimpleJdbcCall(int id) {
    SqlParameterSource in = new MapSqlParameterSource().addValue("in_id", id);
    Map out = simpleJdbcCall.execute(in);

    Employee emp = new Employee();
    emp.setFirstName((String) out.get("FIRST_NAME"));
    emp.setLastName((String) out.get("LAST_NAME"));

    return emp;
}

6. Opérations par lots

Un autre cas d'utilisation simple - regrouper plusieurs opérations.

6.1. Opérations de base par lots utilisantJdbcTemplate

L'utilisation deJdbcTemplate, Batch Operations peut être exécutée via l'APIbatchUpdate().

La partie intéressante ici est l'implémentation concise mais très utile deBatchPreparedStatementSetter:

public int[] batchUpdateUsingJdbcTemplate(List employees) {
    return jdbcTemplate.batchUpdate("INSERT INTO EMPLOYEE VALUES (?, ?, ?, ?)",
        new BatchPreparedStatementSetter() {
            @Override
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                ps.setInt(1, employees.get(i).getId());
                ps.setString(2, employees.get(i).getFirstName());
                ps.setString(3, employees.get(i).getLastName());
                ps.setString(4, employees.get(i).getAddress();
            }
            @Override
            public int getBatchSize() {
                return 50;
            }
        });
}

6.2. Opérations par lots utilisantNamedParameterJdbcTemplate

Nous avons également la possibilité d'effectuer des opérations par lots avec l'APINamedParameterJdbcTemplate -batchUpdate().

Cette API est plus simple que la précédente: vous n'avez pas besoin d'implémenter d'interfaces supplémentaires pour définir les paramètres, car elle dispose d'un ensemble d'instructions préparé à l'interne pour définir les valeurs des paramètres.

Au lieu de cela, les valeurs des paramètres peuvent être transmises à la méthodebatchUpdate() sous la forme d'un tableau deSqlParameterSource.

SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(employees.toArray());
int[] updateCounts = namedParameterJdbcTemplate.batchUpdate(
    "INSERT INTO EMPLOYEE VALUES (:id, :firstName, :lastName, :address)", batch);
return updateCounts;

7. Spring JDBC avec Spring Boot

Spring Boot fournit unspring-boot-starter-jdbcde démarrage pour utiliser JDBC avec des bases de données relationnelles.

Comme tous les démarreurs Spring Boot, celui-ci nous aide également à mettre notre application en service rapidement.

7.1. Dépendance Maven

Nous aurons besoin de la dépendancespring-boot-starter-jdbc comme dépendance principale ainsi que d'une dépendance pour la base de données que nous utiliserons. Dans notre cas, c'estMySQL:


    org.springframework.boot
    spring-boot-starter-jdbc


    mysql
    mysql-connector-java
    runtime

7.2. Configuration

Spring Boot configure automatiquement la source de données pour nous. Nous avons juste besoin de fournir les propriétés dans un fichierproperties:

spring.datasource.url=jdbc:mysql://localhost:3306/springjdbc
spring.datasource.username=guest_user
spring.datasource.password=guest_password

Voilà, rien qu'en effectuant ces configurations uniquement, notre application est opérationnelle et nous pouvons l'utiliser pour d'autres opérations de base de données.

La configuration explicite que nous avons vue dans la section précédente pour une application Spring standard est maintenant incluse dans la configuration automatique de Spring Boot.

8. Conclusion

Dans cet article, nous avons examiné l'abstraction de JDBC dans Spring Framework, qui couvre les diverses fonctionnalités fournies par Spring JDBC à l'aide d'exemples pratiques.

Nous avons également examiné comment nous pouvons commencer rapidement avec Spring JDBC à l’aide d’un démarreur JDBC Spring Boot.

Le code source des exemples est disponibleover on GitHub.