Database
 sql >> Base de données >  >> RDS >> Database

Travailler avec JDBC et Spring

Dans un scénario d'application réel, une énorme quantité de traitement est effectuée sur le serveur principal où les données sont réellement traitées et conservées dans un référentiel. Outre de nombreuses fonctionnalités importantes de Spring, telles que DI (Dependency Injection), Aspects et le développement orienté POJO, Spring offre un excellent support pour la gestion des données. Il existe différentes façons d'écrire de bonnes applications de base de données. Aujourd'hui encore, un grand nombre d'applications sont écrites sur la base de la capacité d'accès aux données JDBC. Cet article traite spécifiquement du JDBC en relation avec Spring, de son support, et des avantages et inconvénients avec des exemples et des extraits de code appropriés.

Présentation de JDBC

L'un des plus grands avantages de continuer à utiliser JDBC dans le monde de l'ORM est qu'il ne nécessite pas de maîtriser le langage de requête d'un autre framework, à part travailler avec des données à un niveau beaucoup plus bas. Il permet à un programmeur de tirer parti des fonctionnalités propriétaires de la base de données. Il a aussi ses inconvénients. Malheureusement, les inconvénients sont souvent si visibles qu'ils n'ont pas besoin d'être mentionnés. Par exemple, l'un d'eux est le code standard . Le terme code standard signifie essentiellement écrire le même code encore et encore sans incorporer aucune valeur dans le code. Cela se voit généralement lorsque nous interrogeons les données d'une base de données ; par exemple, dans le code suivant, nous récupérons simplement un Utilisateur enregistrement de la base de données.

public User getUserById(long id) {
   User user = null;
   Connection con = null;
   PreparedStatement pstmt = null;
   ResultSet rs = null;
   try {
      con = dataSource.getConnection();
      pstmt = con.prepareStatement("select * from "
         + "user_table where userid=?");
      pstmt.setInt(1, id);
      rs.pstmt.executeQuery();
      if (rs.next()) {
         user = new User();
         user.setId(rs.getInt("userid"));
         user.setFullName(rs.getString("fullname"));
         user.setUserType(rs.getString("usertype"));
         user.setPassword(rs.getString("password"));
      }
   } catch (SQLException ex1) {}
   finally {
      try {
         if (rs != null)
         rs.close();
         if (pstmt != null)
            rs.close();
         if (con != null)
            rs.close();
      } catch (SQLException ex2) {}
   }
   return user;
}

Notez que, chaque fois que nous devons interagir avec la base de données, nous devons créer trois objets :une connexion (Connection ), instruction (PreparedStatement ), et le jeu de résultats (ResultSet ). Tous ces éléments doivent également être enfermés dans le try…catch imposé bloc. Même la fermeture de la connexion doit également être incluse dans try…catch . C'est ridicule car le code réel requis pour la fonction est bien moindre. Le code est simplement gonflé de code inutile mais obligatoire et doit être répété partout où nous interagissons avec la base de données. Un schéma de codage intelligent peut réduire ce gâchis, mais il est impossible d'éradiquer le problème du code standard JDBC. Ce n'est pas seulement le problème avec JDBC mais aussi JMS, JNDI et REST.

La solution du printemps

Le framework Spring a fourni une solution à ce gâchis et a fourni un moyen d'éliminer le code passe-partout en utilisant des classes de modèles. Ces classes encapsulent le code passe-partout, soulageant ainsi le programmeur. Cela signifie que le code passe-partout est toujours là, seul le programmeur qui utilise l'une des classes de modèle est soulagé de la peine de l'écrire. Le JdbcTemplate fourni par Spring est la classe centrale du package de base JDBC.

Il simplifie l'utilisation de JDBC et aide à éviter les erreurs courantes. Il exécute le flux de travail JDBC principal, laissant le code de l'application fournir le code SQL et extraire les résultats. Cette classe exécute des requêtes ou des mises à jour SQL, lançant une itération sur les ResultSets et interceptant les exceptions JDBC et les traduisant dans la hiérarchie d'exceptions générique et plus informative définie dans org.springframework.dao paquet.

Nous devons implémenter uniquement les interfaces de rappel et leur donner un contrat clair et défini. Par exemple, le PreparedStatementCreator L'interface de rappel est utilisée pour créer une instruction préparée. Le ResultsetExtractor l'interface agit comme un ResultSet .

Par conséquent, l'extrait de code précédent peut être réécrit avec JdbcTemplate comme suit :

@Autowired
private JdbcTemplate jdbcTemplate;
public User getUserById(long id) {
   return jdbcTemplate.queryForObject(
      "select * from user_table where userid=?",
      new UserRowMapper(),id);
   }
class UserRowMapper implements RowMapper<User>{
   @Override
   public User mapRow(ResultSet rs, int runNumber)
         throws SQLException {
      User user=new User();
      user.setId(rs.getInt("userid"));
      user.setFullName(rs.getString("fullname"));
      user.setUserType(rs.getString("usertype"));
      user.setPassword(rs.getString("password"));
      return user;
   }
}

Le RowMapper est une interface généralement utilisée par le JdbcTemplate pour mapper une ligne par base de lignes du ResultSet . Le RowMapper les objets sont sans état et donc réutilisables. Ils sont parfaits pour implémenter n'importe quelle logique de mappage de lignes. Notez que, dans le code précédent, nous n'avons pas géré les exceptions explicitement comme nous l'avons fait dans le code qui n'utilise pas JdbcTemplate . L'implémentation de RowMapper effectue l'implémentation réelle du mappage de chaque ligne à l'objet de résultat sans que le programmeur ait à se soucier de la gestion des exceptions. Il sera appelé et géré en appelant JdbcTemplate .

Les exceptions

Les exceptions fournies par JDBC sont souvent trop imposantes que nécessaire, avec peu de valeur. La hiérarchie des exceptions d'accès aux données de Spring est plus rationalisée et raisonnable à cet égard. Cela signifie qu'il dispose d'un ensemble cohérent de classes d'exceptions dans son arsenal contrairement à la taille unique de JDBC adaptée à toutes les exceptions appelée SQLException pour tous les problèmes liés à l'accès aux données. Les exceptions d'accès aux données de Spring sont enracinées avec DataAccessException classe. Par conséquent, nous pouvons avoir à la fois le choix de l'exception vérifiée ou non vérifiée ancrée dans le cadre. Cela semble plus pratique car il n'y a vraiment aucune solution à de nombreux problèmes qui se sont produits lors de l'accès aux données d'exécution et il est inutile de les détecter lorsque nous ne pouvons pas résoudre la situation avec une alternative appropriée.

La manière de Spring de simplifier l'accès aux données

Ce que Spring fait en réalité, c'est qu'il distingue la partie fixe et variable du mécanisme d'accès aux données en deux ensembles de classes appelées classes de modèles et classes de rappel , respectivement. La partie fixe du code représente la partie superficielle de l'accès aux données et la partie variable est cette méthode d'accès aux données qui varie en fonction de l'évolution des besoins.

En bref, les classes modèles gérer :

  • Contrôle des transactions
  • Gestion des ressources
  • Gestion des exceptions

Et, les classes de rappel gérer :

  • Créer une instruction de requête
  • Liaison de paramètres
  • Assemblage des ensembles de résultats

Nous pouvons choisir une parmi de nombreuses classes de modèles, selon le choix de la technologie persistante utilisée. Par exemple, pour JDBC, nous pouvons choisir JdbcTemplate , ou pour ORM, nous pouvons choisir JpaTemplate , HibernateTemplate , et ainsi de suite.

Maintenant, lors de la connexion à la base de données, nous avons trois options pour configurer la source de données, telles que :

  • Défini par le pilote JDBC
  • Recherché par JNDI
  • Récupéré du pool de connexions

Une application prête pour la production utilise généralement un pool de connexions ou JNDI. Les sources de données définies par le pilote JDBC sont de loin les plus simples, bien qu'elles soient principalement utilisées à des fins de test. Spring propose trois classes dans le package org.springframework.jdbc.datasource de cette catégorie ; ce sont :

  • Source de données DriverManager : Implémentation simple du standard JDBC DataSource interface, configuration de l'ancien DriverManager JDBC via les propriétés du bean, et renvoyant une nouvelle Connexion de chaque demande.
  • Source de données de connexion unique : Renvoie la même connexion à chaque requête. Ce type de connexion est principalement destiné aux tests.
  • SimpleDriverDataSource : Identique à DriverManagerDataSource sauf qu'il a des problèmes de chargement de classe spéciaux tels que OSGi ; cette classe fonctionne directement avec le pilote JDBC.

La configuration de ces sources de données est similaire. Nous pouvons les configurer dans une classe de bean ou via XML.

// Configuring MySQL data source
@Bean
public DataSource dataSource() {
   DriverManagerDataSource ds=new DriverManagerDataSource();
   ds.setDriverClassName("com.mysql.jdbc.Driver");
   ds.setUrl("jdbc:mysql://localhost:3306/testdb");
   ds.setUsername("root");
   ds.setPassword("secret");
   return ds;
}

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p_driverClassName="com.mysql.jdbc.Driver"
p_url="jdbc:mysql://localhost:3306/testdb"
p_username="root"
p_password="secret"/>

Classes de modèles JDBC

Spring propose quelques classes de modèles pour simplifier l'accès aux données avec JDBC :

  • Modèle Jdbc : Il s'agit de la classe de base du package JDBC de base org.springframework.jdbc.core qui fournit l'accès le plus simple à la base de données via des requêtes indexées.
  • NamedParameterJdbcTemplate : Cette classe de modèle fournit également un ensemble de base d'opérations JDBC dans lesquelles les valeurs sont liées à des paramètres nommés plutôt qu'aux espaces réservés traditionnels " ?" dans les requêtes SQL.

Classes de rappel JDBC

Les principales interfaces fonctionnelles de rappel JDBC définies dans org.springframework.jdbc.core sont :

  • CallableStatementCallback : Fonctionne sur le JDBC CallableStatement. Ce rappel est utilisé en interne par JdbcTemplate et permet l'exécution sur un seul CallableStatement tels que des appels d'exécution SQL uniques ou multiples avec différents paramètres.
  • PréparéStatementCallback : Fonctionne sur JDBC PreparedStatement. Ce rappel est utilisé en interne par JdbcTemplate et permet l'exécution de plusieurs opérations sur un seul PreparedStatement comme un ou plusieurs appels SQL executeUpdate avec différents paramètres.
  • Rappel de déclaration : Fonctionne sur la instruction JDBC . Ce rappel est également utilisé en interne par JdbcTemplate pour exécuter plus d'une opération sur une seule instruction tels que des appels SQL executeUpdate uniques ou multiples.

Un exemple JDBC de démarrage simple à ressort

Essayons un simple exemple de démarrage Spring. Un projet de démarrage Spring gère automatiquement de nombreuses complexités de configuration où un développeur est soulagé de tous les problèmes une fois qu'une dépendance correcte est incluse dans le fichier Maven pom.xml . Pour garder la longueur de l'article courte, nous n'inclurons pas d'explications de code. Veuillez utiliser les références données à la fin de l'article pour une description plus détaillée.

Pour travailler sur l'exemple suivant, créez une base de données et une table dans MySQl comme suit :

Connectez-vous à la base de données MySQL et créez une base de données et une table avec la commande suivante :

CREATE DATABASE testdb;
USE testdb;
CREATE TABLE candidate(
   id INT UNSIGNED NOT NULL AUTO_INCREMENT,
   fullname VARCHAR(100) NOT NULL,
   email VARCHAR(100) NOT NULL,
   phone VARCHAR(10) NOT NULL,
   PRIMARY KEY(id)
);

Démarrez en tant que projet de démarrage Spring à partir de Spring Tool Suite (STS) avec la dépendance JDBC et MySQL. Le fichier de configuration Maven, pom.xml , du projet est la suivante :

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi_schemaLocation="http://maven.apache.org/POM/4.0.0
      http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.mano.springbootjdbc.demo</groupId>
   <artifactId>spring-boot-jdbc-demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>spring-boot-jdbc-demo</name>
   <description>Demo project for Spring Boot
      jdbc</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.10.RELEASE</version>
      <relativePath/> <!-- Look up parent from repository -->
   </parent>

   <properties>
     <project.build.sourceEncoding>UTF-8
         </project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8
         </project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>

      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <scope>runtime</scope>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-
               plugin</artifactId>
         </plugin>
      </plugins>
   </build>
</project>

Classe modèle :Candidate.java

package org.mano.springbootjdbc.demo.model;

public class Candidate {
   private int id;
   private String fullname;
   private String email;
   private String phone;

   public Candidate() {
      super();
   }

   public Candidate(int id, String fullname,
         String email, String phone) {
      super();
      setId(id);
      setFullname(fullname);
      setEmail(email);
      setPhone(phone);
   }

   public int getId() {
      return id;
   }

   public void setId(int id) {
      this.id = id;
   }

   public String getFullname() {
      return fullname;
   }

   public void setFullname(String fullname) {
      this.fullname = fullname;
   }

   public String getEmail() {
      return email;
   }

   public void setEmail(String email) {
      this.email = email;
   }

   public String getPhone() {
      return phone;
   }

   public void setPhone(String phone) {
      this.phone = phone;
   }

   @Override
   public String toString() {
      return "Candidate [id=" + id + ", fullname=" + fullname
         + ", email=" + email + ", phone=" + phone + "]";
   }

}

Interface d'objet d'accès aux données :CandidateDao.java

package  org.mano.springbootjdbc.demo.dao;

import java.util.List;

import org.mano.springbootjdbc.demo.model.Candidate;

public interface CandidateDao {
   public void addCandidate(Candidate candidate);

   public void modifyCandidate(Candidate candidate,
      int candidateId);

   public void deleteCandidate(int candidateId);

   public Candidate find(int candidateId);

   public List<Candidate> findAll();
}

Classe d'implémentation d'objet d'accès aux données :CandidateDaoImpl.java

package org.mano.springbootjdbc.demo.dao;

import java.util.ArrayList;
import java.util.List;

import org.mano.springbootjdbc.demo.model.Candidate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
@Qualifier("candidateDao")
public class CandidateDaoImpl implements CandidateDao {

   @Autowired
   JdbcTemplate jdbcTemplate;

   @Override
   public void addCandidate(Candidate candidate) {
      jdbcTemplate.update("insert into candidate
            (id,fullname,email,phone) "
            + "values (?,?,?,?)", candidate.getId(),
         candidate.getFullname(), candidate.getEmail(),
         candidate.getPhone());
      System.out.println(candidate+" is added successfully!");

   }

   @Override
   public void modifyCandidate(Candidate candidate, int
         candidateId) {
      jdbcTemplate.update("update candidate fullname=?,
         email=?,phone=? "
         + "where id=? values (?,?,?,?)",candidate.getFullname(),
      candidate.getEmail(), candidateId);
      System.out.println("Candidate with id="+candidateId+
         " modified successfully!");

   }

   @Override
   public void deleteCandidate(int candidateId) {
      jdbcTemplate.update("delete from candidate where id=?",
         candidateId);
      System.out.println("Candidate with id="+candidateId+
         " deleted successfully!");

   }

   @Override
   public Candidate find(int candidateId) {
      Candidate c = null;
      c = (Candidate) jdbcTemplate.queryForObject("select *
         from candidate "
         + "where id=?", new Object[] { candidateId },
      new BeanPropertyRowMapper<Candidate>(Candidate.
         class));
      return c;
   }

   @Override
   public List<Candidate> findAll() {
      List<Candidate> candidates = new ArrayList<>();
      candidates = jdbcTemplate.query("select * from candidate",
      new BeanPropertyRowMapper<Candidate>
         (Candidate.class));
      return candidates;
   }

}

Classe de chargeur de démarrage Spring :SpringBootJdbcDemoApplication.java

package org.mano.springbootjdbc.demo;

import java.util.List;

import org.mano.springbootjdbc.demo.dao.CandidateDao;
import org.mano.springbootjdbc.demo.model.Candidate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.
   SpringBootApplication;

@SpringBootApplication
public class SpringBootJdbcDemoApplication implements
      CommandLineRunner {
   @Autowired
   private CandidateDao cdao;

   public static void main(String[] args) {
     SpringApplication.run(SpringBootJdbcDemoApplication.
         class, args);
   }

   @Override
   public void run(String... arg0) throws Exception {
      Candidate c1 = new Candidate(1, "Sachin Tendulkar",
         "[email protected]", "1234567890");
      Candidate c2 = new Candidate(2, "Amit Saha",
         "[email protected]", "9632587410");
      Candidate c3 = new Candidate(3, "Sandip Paul",
         "[email protected]", "8527419630");
      Candidate c4 = new Candidate(4, "Rajib Kakkar",
         "[email protected]", "9876543210");
      Candidate c5 = new Candidate(5, "Rini Simon",
         "[email protected]", "8624793150");
      cdao.addCandidate(c1);
      cdao.addCandidate(c2);
      cdao.addCandidate(c3);
      cdao.addCandidate(c4);
      cdao.addCandidate(c5);

      List<Candidate> candidates = cdao.findAll();
      for (Candidate candidate : candidates) {
         System.out.println(candidate);
      }
      cdao.deleteCandidate(3);

      candidates = cdao.findAll();
      for (Candidate cc : candidates) {
         System.out.println(cc);
      }

   }

}

Application.propriétés

spring.driverClassName=com.mysql.jdbc.Driver
spring.url=jdbc:mysql://localhost:3306/testdb
spring.username=root
spring.password=secret

Exécuter l'application

Pour exécuter l'application, cliquez avec le bouton droit sur le projet dans l'Explorateur de projets volet et sélectionnez Exécuter en tant que -> Application Spring Boot . C'est tout.

Conclusion

Nous avons trois options pour travailler avec la programmation de bases de données relationnelles avec Spring :

  • Le JDBC à l'ancienne avec Spring. Cela signifie utiliser le framework Spring à toutes fins pratiques dans le programme, à l'exception du support de données de Spring.
  • Utilisation des classes de modèles JDBC. Spring propose des classes d'abstraction JDBC pour interroger les bases de données relationnelles ; c'est beaucoup plus simple que de travailler avec du code JDBC natif.
  • Spring offre également une excellente prise en charge du cadre ORM (Object Relational Mapping) et peut bien s'intégrer à une implémentation importante de l'API JPA (Java Persistent Annotation) telle que Hibernate. Il dispose également de sa propre assistance Spring Data JPA qui peut générer automatiquement l'implémentation du référentiel à la volée lors de l'exécution.

Si l'on opte pour JDBC pour une raison quelconque, il est préférable d'utiliser le support de modèle Spring tel que JdbcTemplate autre que l'utilisation d'ORM.

Références

  • Murs, rocher. Le printemps en action 4 , Manning Publications
  • Documentation API Spring 5