Database
 sql >> Base de Dados >  >> RDS >> Database

Trabalhando com JDBC e Spring


Em um cenário de aplicativo do mundo real, uma grande quantidade de processamento é feita no servidor de back-end, onde os dados são realmente processados ​​e persistidos em um repositório. Além de muitos recursos proeminentes do Spring, como DI (Dependency Injection), Aspects e desenvolvimento orientado a POJO, o Spring possui excelente suporte para manipulação de dados. Existem diferentes maneiras de escrever bons aplicativos de banco de dados. Ainda hoje, um grande número de aplicativos são escritos com base na capacidade de acesso a dados JDBC. Este artigo trata especificamente do JDBC em conexão com o Spring, seu suporte e prós e contras com exemplos apropriados e trechos de código.

Visão geral do JDBC


Uma das maiores vantagens de ainda usar o JDBC no mundo do ORM é que ele não exige dominar a linguagem de consulta de outro framework além de trabalhar com dados em um nível muito mais baixo. Ele permite que um programador aproveite os recursos proprietários do banco de dados. Tem suas desvantagens também. Infelizmente, as desvantagens são muitas vezes tão visíveis que não precisam ser mencionadas. Por exemplo, um deles é o código padrão . O termo código padrão basicamente significa escrever o mesmo código repetidamente sem incorporar nenhum valor no código. Isso normalmente pode ser visto quando consultamos dados de um banco de dados; por exemplo, no código a seguir, simplesmente buscamos um User registro do banco de dados.
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, toda vez que precisamos interagir com o banco de dados, devemos criar três objetos—uma conexão (Conexão ), declaração (PreparedStatement ) e conjunto de resultados (ResultSet ). Tudo isso também deve ser incluído dentro do try…catch imposto. quadra. Mesmo o fechamento da conexão também deve ser incluído dentro de try…catch . Isso é ridículo porque o código real necessário para a função é muito menor. O código é simplesmente inflado com código desnecessário, mas obrigatório, e deve ser repetido onde quer que interajamos com o banco de dados. Um esquema de codificação inteligente pode reduzir essa bagunça, mas é impossível erradicar o problema do código JDBC padrão. Este não é apenas o problema com JDBC, mas também com JMS, JNDI e REST.

Solução do Spring


A estrutura Spring forneceu uma solução para essa bagunça e forneceu um meio de eliminar o código clichê usando classes de modelo. Essas classes encapsulam o código clichê, aliviando assim o programador. Isso significa que o código clichê ainda está lá, apenas o programador que usa uma das classes de modelo fica livre do problema de escrevê-lo. O JdbcTemplate fornecido pelo Spring é a classe central do pacote principal do JDBC.

Ele simplifica o uso do JDBC e ajuda a evitar erros comuns. Ele executa o fluxo de trabalho principal do JDBC, deixando o código do aplicativo para fornecer SQL e extrair resultados. Essa classe executa consultas ou atualizações SQL, iniciando iteração sobre ResultSets e capturando exceções JDBC e traduzindo-as para a hierarquia de exceção genérica e mais informativa definida no org.springframework.dao pacote.

Precisamos implementar apenas as interfaces de retorno de chamada e dar a elas um contrato claro e definido. Por exemplo, o PreparedStatementCreator A interface de retorno de chamada é usada para criar uma instrução preparada. O ResultsetExtractor interface funciona como um ResultSet .

Portanto, o trecho de código anterior pode ser reescrito com JdbcTemplate do seguinte modo:
@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;
   }
}

O RowMapper é uma interface normalmente usada pelo JdbcTemplate para mapear uma linha por base de linhas do ResultSet . O RowMapper os objetos são sem estado e, portanto, reutilizáveis. Eles são perfeitos para implementar qualquer lógica de mapeamento de linha. Observe que, no código anterior, não tratamos exceções explicitamente como fizemos no código que não usa JdbcTemplate . A implementação do RowMapper realiza a implementação real do mapeamento de cada linha para o objeto de resultado sem a necessidade do programador se preocupar com o tratamento de exceções. Ele será chamado e tratado chamando JdbcTemplate .

As exceções


As exceções fornecidas pelo JDBC geralmente são muito imponentes do que o necessário, com pouco valor. A hierarquia de exceção de acesso a dados do Spring é mais simplificada e razoável a esse respeito. Isso significa que ele tem um conjunto consistente de classes de exceção em seu arsenal, em contraste com o tamanho único do JDBC para todas as exceções chamado SQLException para todos os problemas relacionados ao acesso a dados. As exceções de acesso a dados do Spring são enraizadas com a DataAccessException aula. Portanto, podemos ter a opção de exceção verificada versus não verificada incorporada à estrutura. Isso soa mais prático porque realmente não há soluções para muitos dos problemas que ocorreram durante o acesso a dados em tempo de execução e é inútil pegá-los quando não podemos resolver a situação com uma alternativa adequada.

O jeito do Spring de simplificar o acesso a dados


O que o Spring realmente faz é distinguir a parte fixa e variável do mecanismo de acesso a dados em dois conjuntos de classes chamadas classes de modelo e classes de retorno de chamada , respectivamente. A parte fixa do código representa a parte superficial do acesso aos dados e a parte variável é aquele método de acesso aos dados que varia de acordo com a mudança de requisito.

Resumindo, as classes de modelo lidar com:
  • Controle de transações
  • Gerenciamento de recursos
  • Tratamento de exceção

E as classes de retorno de chamada lidar com:
  • Criando instrução de consulta
  • Associação de parâmetros
  • Marshalling do conjunto de resultados

Podemos escolher uma entre muitas classes de template, de acordo com a escolha da tecnologia persistente utilizada. Por exemplo, para JDBC podemos escolher JdbcTemplate , ou para ORM podemos escolher JpaTemplate , Modelo do Hibernate , e assim por diante.

Agora, ao conectar ao banco de dados, temos três opções para configurar a fonte de dados, como:
  • Definido pelo driver JDBC
  • Procurado por JNDI
  • Buscado do pool de conexões

Um aplicativo pronto para produção normalmente usa um pool de conexões ou JNDI. As fontes de dados definidas pelo driver JDBC são de longe as mais simples, embora sejam usadas principalmente para fins de teste. O Spring oferece três classes no pacote org.springframework.jdbc.datasource desta categoria; eles estão:
  • DriverManagerDataSource: Implementação simples do padrão JDBC DataSource interface, configurando o antigo JDBC DriverManager via propriedades do bean e retornando uma nova Conexão de cada solicitação.
  • SingleConnectionDataSource: Retorna a mesma conexão em cada solicitação. Esse tipo de conexão destina-se principalmente a testes.
  • SimpleDriverDataSource: O mesmo que DriverManagerDataSource exceto que tem problemas especiais de carregamento de classe, como OSGi; esta classe funciona diretamente com o driver JDBC.

A configuração dessas fontes de dados é semelhante. Podemos configurá-los em uma 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 modelo JDBC


O Spring oferece algumas classes de modelo para simplificar o acesso a dados com JDBC:
  • JdbcTemplate: Esta é a classe básica do pacote principal do JDBC org.springframework.jdbc.core que fornece o acesso mais simples ao banco de dados por meio de consultas indexadas.
  • NamedParameterJdbcTemplate: Essa classe de modelo também fornece um conjunto básico de operações JDBC em que os valores são vinculados a parâmetros nomeados em vez de espaços reservados '?' tradicionais em consultas SQL.

Classes de retorno de chamada JDBC


As principais interfaces funcionais de retorno de chamada JDBC definidas em org.springframework.jdbc.core estão:
  • CallableStatementCallback: Opera no JDBC CallableStatement. Este retorno de chamada é usado internamente por JdbcTemplate e permite a execução em um único CallableStatement como uma ou várias chamadas de execução de SQL com parâmetros diferentes.
  • PreparedStatementCallback: Opera em JDBC PreparedStatement. Este retorno de chamada é usado internamente por JdbcTemplate e permite a execução de mais de uma operação em um único PreparedStatement como uma ou várias chamadas SQL executeUpdate com parâmetros diferentes.
  • StatementCallback: Opera na Instrução JDBC . Este retorno de chamada também é usado internamente pelo JdbcTemplate para executar mais de uma operação em uma única Instrução como uma ou várias chamadas SQL executeUpdate.

Um exemplo simples de JDBC Spring Boot


Vamos tentar um exemplo simples de inicialização do Spring. Um projeto de inicialização do Spring lida automaticamente com muitas das complexidades de configuração em que um desenvolvedor é aliviado de todos os problemas quando uma dependência correta é incluída no arquivo Maven pom.xml . Para manter o tamanho do artigo curto, não incluiremos explicações de código. Por favor, use as referências fornecidas no final do artigo para uma descrição mais detalhada.

Para trabalhar no exemplo a seguir, crie um banco de dados e uma tabela no MySQl da seguinte forma:

Faça login no banco de dados MySQL e crie um banco de dados e uma tabela com o seguinte 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)
);

Comece como um projeto inicial do Spring do Spring Tool Suite (STS) com a dependência JDBC e MySQL. O arquivo de configuração do Maven, pom.xml , do projeto é a seguinte:
<?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 de modelo: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 de objeto de acesso a dados: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 de implementação do objeto de acesso a dados: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 do carregador de inicialização do 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);
      }

   }

}

Aplicativo.propriedades

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

Execute o aplicativo


Para executar o aplicativo, clique com o botão direito do mouse no projeto no Project Explorer painel e selecione Executar como -> Aplicativo Spring Boot . Isso é tudo.

Conclusão


Temos três opções para trabalhar com programação de Banco de Dados Relacional com Spring:
  • O antiquado JDBC com Spring. Isso significa usar o framework Spring para todos os propósitos práticos do programa, exceto o suporte a dados do Spring.
  • Usando classes de modelo JDBC. O Spring oferece classes de abstração JDBC para consultar bancos de dados relacionais; eles são muito mais simples do que trabalhar com código JDBC nativo.
  • O Spring também tem excelente suporte para a estrutura ORM (Object Relational Mapping) e pode se integrar bem com uma implementação proeminente da API JPA (Java Persistent Annotation), como o Hibernate. Ele também tem sua própria assistência Spring Data JPA que pode gerar automaticamente a implementação do repositório em tempo real em tempo de execução.

Se alguém optar por JDBC por algum motivo, é melhor usar o suporte a modelos Spring, como JdbcTemplate além de usar ORM.

Referências

  • Paredes, Penhasco. Primavera em ação 4 , Manning Publicações
  • Documentação da API Spring 5