sql >> Base de Datos >  >> RDS >> Database

Trabajando con JDBC y Spring

En un escenario de aplicación del mundo real, se realiza una gran cantidad de procesamiento en el servidor back-end donde los datos se procesan y se conservan en un repositorio. Además de muchas características destacadas de Spring, como DI (Inyección de dependencia), Aspectos y desarrollo orientado a POJO, Spring tiene un excelente soporte para el manejo de datos. Hay diferentes formas de escribir buenas aplicaciones de bases de datos. Todavía hoy, una gran cantidad de aplicaciones se escriben en función de la capacidad de acceso a datos de JDBC. Este artículo trata específicamente sobre el JDBC en relación con Spring, su soporte y los pros y los contras con ejemplos y fragmentos de código apropiados.

Descripción general de JDBC

Una de las mayores ventajas de seguir usando JDBC en el mundo de ORM es que no requiere dominar el lenguaje de consulta de otro marco, además de trabajar con datos en un nivel mucho más bajo. Permite a un programador aprovechar las características propietarias de la base de datos. Tiene sus desventajas también. Desafortunadamente, las desventajas son a menudo tan visibles que no necesitan mencionarse. Por ejemplo, uno de ellos es código repetitivo . El término código repetitivo básicamente significa escribir el mismo código una y otra vez sin incorporar ningún valor en el código. Esto normalmente se puede ver cuando consultamos datos de una base de datos; por ejemplo, en el siguiente código, simplemente obtenemos un Usuario registro de la base de datos.

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

Observe que, cada vez que necesitamos interactuar con la base de datos, debemos crear tres objetos:una conexión (Conexión ), instrucción (PreparedStatement ), y conjunto de resultados (ResultSet ). Todos estos también tienen que estar encerrados dentro del impuesto intentar... atrapar cuadra. Incluso el cierre de la conexión también debe incluirse dentro de intentar... atrapar . Esto es ridículo porque el código real requerido para la función es mucho menor. El código simplemente está inflado con un código innecesario pero obligatorio y debe repetirse cada vez que interactuamos con la base de datos. Un esquema de codificación inteligente puede reducir este desorden, pero es imposible erradicar el problema del código JDBC repetitivo. Este no es solo el problema con JDBC sino también con JMS, JNDI y REST.

Solución de Spring

El marco Spring proporcionó una solución a este lío y proporcionó un medio para eliminar el código repetitivo mediante el uso de clases de plantilla. Estas clases encapsulan el código repetitivo, aliviando así al programador. Esto significa que el código repetitivo todavía está allí, solo el programador que usa una de las clases de plantilla se libera del problema de escribirlo. La plantilla Jdbc proporcionada por Spring es la clase central del paquete principal de JDBC.

Simplifica el uso de JDBC y ayuda a evitar errores comunes. Ejecuta el flujo de trabajo central de JDBC, dejando el código de la aplicación para proporcionar SQL y extraer resultados. Esta clase ejecuta consultas SQL o actualizaciones, iniciando la iteración sobre ResultSets y capturando excepciones JDBC y traduciéndolas a la jerarquía de excepciones genérica y más informativa definida en org.springframework.dao paquete.

Necesitamos implementar solo las interfaces de devolución de llamada y darles un contrato claro y definido. Por ejemplo, el PreparedStatementCreator La interfaz de devolución de llamada se utiliza para crear una declaración preparada. El Extractor de conjunto de resultados la interfaz actúa como un ResultSet .

Por lo tanto, el fragmento de código anterior se puede reescribir con JdbcTemplate de la siguiente manera:

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

El Mapeador de filas es una interfaz utilizada normalmente por JdbcTemplate para mapear una fila por base de filas del ResultSet . El Mapeador de filas los objetos no tienen estado y, por lo tanto, son reutilizables. Son perfectos para implementar cualquier lógica de mapeo de filas. Observe que, en el código anterior, no manejamos las excepciones explícitamente como lo hicimos en el código que no usa JdbcTemplate . La implementación de RowMapper realiza la implementación real de mapear cada fila al objeto de resultado sin que el programador tenga que preocuparse por el manejo de excepciones. Será llamado y manejado llamando a JdbcTemplate .

Las excepciones

Las excepciones proporcionadas por JDBC suelen ser demasiado imponentes de lo necesario, con poco valor. La jerarquía de excepción de acceso a datos de Spring es más racionalizada y razonable a este respecto. Esto significa que tiene un conjunto consistente de clases de excepción en su arsenal en contraste con la excepción única de JDBC llamada SQLException para todos los problemas relacionados con el acceso a los datos. Las excepciones de acceso a datos de Spring están enraizadas con DataAccessException clase. Por lo tanto, podemos tener la opción de excepción marcada o no marcada arraigada en el marco. Esto suena más práctico porque realmente no hay soluciones para muchos de los problemas que ocurrieron durante el acceso a los datos en tiempo de ejecución y no tiene sentido si los detectamos cuando no podemos abordar la situación con una alternativa adecuada.

La manera de Spring de simplificar el acceso a los datos

Lo que Spring realmente hace es que distingue la parte fija y variable del mecanismo de acceso a datos en dos conjuntos de clases llamados clases de plantilla y clases de devolución de llamada , respectivamente. La parte fija del código representa la parte superficial del acceso a los datos y la parte variable es el método de acceso a los datos que varía según los requisitos cambiantes.

En resumen, las clases de plantilla manejar:

  • Control de transacciones
  • Administración de recursos
  • Manejo de excepciones

Y, las clases de devolución de llamada manejar:

  • Crear declaración de consulta
  • Enlace de parámetros
  • Marshalling del conjunto de resultados

Podemos elegir una entre muchas clases de plantillas, según la elección de la tecnología persistente utilizada. Por ejemplo, para JDBC podemos elegir JdbcTemplate , o para ORM podemos elegir JpaTemplate , Plantilla de Hibernate , y así sucesivamente.

Ahora, mientras nos conectamos a la base de datos, tenemos tres opciones para configurar la fuente de datos, tales como:

  • Definido por el controlador JDBC
  • Buscado por JNDI
  • Obtenido del grupo de conexiones

Una aplicación lista para producción generalmente usa un grupo de conexiones o JNDI. Las fuentes de datos definidas por el controlador JDBC son, con diferencia, las más sencillas, aunque se utilizan principalmente con fines de prueba. Spring ofrece tres clases en el paquete org.springframework.jdbc.datasource de esta categoría; ellos son:

  • DriverManagerDataSource: Implementación simple del estándar JDBC DataSource interfaz, configurando el viejo JDBC DriverManager a través de las propiedades del bean y devolviendo una nueva Conexión de cada solicitud.
  • Fuente de datos de conexión única: Devuelve la misma conexión en cada solicitud. Este tipo de conexión está diseñado principalmente para pruebas.
  • Fuente de datos del controlador simple: Igual que DriverManagerDataSource excepto que tiene problemas especiales de carga de clases como OSGi; esta clase funciona directamente con el controlador JDBC.

La configuración de estas fuentes de datos es similar. Podemos configurarlos en una clase de bean o vía 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"/>

Clases de plantilla JDBC

Spring ofrece un par de clases de plantilla para simplificar el acceso a datos con JDBC:

  • Plantilla Jdbc: Esta es la clase básica del paquete central JDBC org.springframework.jdbc.core que proporciona el acceso más simple a la base de datos a través de consultas indexadas.
  • TemplateJdbcParámetroNombrado: Esta clase de plantilla también proporciona un conjunto básico de operaciones JDBC donde los valores están vinculados con parámetros con nombre en lugar de los tradicionales marcadores de posición '?' en las consultas SQL.

Clases de devolución de llamada de JDBC

Las interfaces funcionales clave de devolución de llamada de JDBC definidas en org.springframework.jdbc.core son:

  • CallableStatementCallback: Opera en JDBC CallableStatement. Esta devolución de llamada es utilizada internamente por JdbcTemplate y permite la ejecución en un solo CallableStatement como llamadas de ejecución de SQL únicas o múltiples con diferentes parámetros.
  • Devolución de llamada de declaración preparada: Opera en JDBC PreparedStatement. Esta devolución de llamada es utilizada internamente por JdbcTemplate y permite la ejecución de más de una operación en una sola PreparedStatement tales como llamadas de actualización de ejecución de SQL únicas o múltiples con diferentes parámetros.
  • Devolución de llamada de declaración: Opera en la Declaración de JDBC . Esta devolución de llamada también es utilizada internamente por JdbcTemplate para ejecutar más de una operación en una sola Declaración como una o varias llamadas de actualización de ejecución de SQL.

Un ejemplo simple de Spring Boot JDBC

Probemos un ejemplo simple de arranque de Spring. Un proyecto Spring Boot maneja automáticamente muchas de las complejidades de la configuración donde un desarrollador se libera de todos los problemas una vez que se incluye una dependencia correcta en el archivo Maven pom.xml . Para acortar la longitud del artículo, no incluiremos explicaciones del código. Utilice las referencias proporcionadas al final del artículo para obtener una descripción más detallada.

Para trabajar en el siguiente ejemplo, cree una base de datos y una tabla en MySQl de la siguiente manera:

Inicie sesión en la base de datos MySQL y cree una base de datos y una tabla con el siguiente comando:

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

Comience como un proyecto inicial de Spring desde Spring Tool Suite (STS) con la dependencia JDBC y MySQL. El archivo de configuración de Maven, pom.xml , del proyecto es el siguiente:

<?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>

Clase de modelo:Candidato.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 + "]";
   }

}

Interfaz de objetos de acceso a datos: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();
}

Clase de implementación de objetos de acceso a datos: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;
   }

}

Clase de cargador de arranque 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);
      }

   }

}

Propiedades.de.aplicación

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

Ejecutar la aplicación

Para ejecutar la aplicación, haga clic con el botón derecho en el proyecto en el Explorador de proyectos panel y seleccione Ejecutar como -> Aplicación Spring Boot . Eso es todo.

Conclusión

Tenemos tres opciones para trabajar con la programación de Bases de Datos Relacionales con Spring:

  • El antiguo JDBC con Spring. Esto significa usar el marco Spring para todos los propósitos prácticos en el programa, excepto el soporte de datos de Spring.
  • Uso de clases de plantilla JDBC. Spring ofrece clases de abstracción JDBC para consultar bases de datos relacionales; estos son mucho más simples que trabajar con código JDBC nativo.
  • Spring también tiene una excelente compatibilidad con el marco ORM (Mapeo relacional de objetos) y puede integrarse bien con una implementación destacada de la API JPA (Anotación persistente de Java) como Hibernate. También tiene su propia asistencia Spring Data JPA que puede generar automáticamente la implementación del repositorio sobre la marcha en tiempo de ejecución.

Si uno opta por JDBC por alguna razón, es mejor usar el soporte de plantilla de Spring como JdbcTemplate además de usar ORM.

Referencias

  • Paredes, peñasco. Primavera en acción 4 , Publicaciones de Manning
  • Documentación de la API de Spring 5