Spring JDBC

Spring JDBC

1. Visão geral

Neste artigo, veremos casos de uso práticos do módulo Spring JDBC.

Todas as classes no Spring JDBC são divididas em quatro pacotes separados:

  • core - a funcionalidade principal do JDBC. Algumas das classes importantes neste pacote incluemJdbcTemplate,SimpleJdbcInsert,SimpleJdbcCalleNamedParameterJdbcTemplate.

  • datasource - classes de utilitário para acessar uma fonte de dados. Ele também possui várias implementações de fonte de dados para testar o código JDBC fora do contêiner Java EE.

  • object - Acesso ao banco de dados de maneira orientada a objetos. Permite executar consultas e retornar os resultados como um objeto de negócios. Ele também mapeia os resultados da consulta entre as colunas e as propriedades dos objetos de negócios.

  • support - classes de suporte para classes nos pacotescoreeobject. E.g. fornece a funcionalidade de traduçãoSQLException.

    == Leitura adicional:

Spring Security: Explorando a autenticação JDBC

Explore os recursos oferecidos pelo Spring para executar a autenticação JDBC usando uma configuração existente do DataSource.

Read more

Introdução ao Spring Data JPA

Introdução ao Spring Data JPA com Spring 4 - a configuração do Spring, o DAO, consultas manuais e geradas e gerenciamento de transações.

Read more

2. Configuração

Para começar, vamos começar com algumas configurações simples da fonte de dados (usaremos um banco de dados MySQL para este exemplo):

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

Como alternativa, também podemos fazer bom uso de um banco de dados incorporado para desenvolvimento ou teste - aqui está uma configuração rápida que cria uma instância do banco de dados incorporado H2 e o preenche previamente com scripts SQL simples:

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

Finalmente - o mesmo pode, é claro, ser feito usando a configuração XML paradatasource:


    
    
    
    

3. OJdbcTemplatee Consultas em execução

3.1. Consultas Básicas

O modelo JDBC é a principal API por meio da qual acessaremos a maioria das funcionalidades nas quais estamos interessados:

  • criação e fechamento de conexões

  • executando instruções e chamadas de procedimento armazenado

  • iterando sobreResultSete retornando resultados

Em primeiro lugar, vamos começar com um exemplo simples para ver o queJdbcTemplate pode fazer:

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

e também aqui está um INSERT simples:

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

Observe a sintaxe padrão de fornecer parâmetros - usando o caractere?. A seguir - vamos ver uma alternativa para essa sintaxe.

3.2. Consultas com parâmetros nomeados

Para obtersupport for named parameters, usaremos o outro modelo JDBC fornecido pela estrutura - oNamedParameterJdbcTemplate.

Além disso, isso envolve oJbdcTemplatee fornece uma alternativa à sintaxe tradicional usando “?” para especificar os parâmetros. Sob o capô, ele substitui os parâmetros nomeados por JDBC “?” espaço reservado e delegados paraJDCTemplate envolvidos para executar as consultas:

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

Observe como estamos usandoMapSqlParameterSource para fornecer os valores para os parâmetros nomeados.

Por exemplo, vejamos o exemplo abaixo que usa propriedades de um bean para determinar os parâmetros nomeados:

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

Observe como agora estamos fazendo uso das implementaçõesBeanPropertySqlParameterSource em vez de especificar os parâmetros nomeados manualmente como antes.

3.3. Mapeamento de resultados de consulta para objeto Java

Outro recurso muito útil é a capacidade de mapear os resultados da consulta para objetos Java - implementando a interfacethe RowMapper.

Por exemplo - para cada linha retornada pela consulta, o Spring usa o mapeador de linhas para preencher o java bean:

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

Posteriormente, agora podemos passar o mapeador de linhas para a API de consulta e obter objetos Java totalmente preenchidos:

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

4. Tradução de exceção

O Spring vem com sua própria hierarquia de exceção de dados pronta para uso - comDataAccessException como a exceção raiz - e traduz todas as exceções brutas subjacentes a ele.

E assim, mantemos nossa sanidade por não ter que lidar com exceções de persistência de baixo nível e nos beneficiamos do fato de que o Spring envolve as exceções de baixo nível emDataAccessException ou uma de suas subclasses.

Além disso, isso mantém o mecanismo de tratamento de exceções independente do banco de dados subjacente que estamos usando.

Além do padrãoSQLErrorCodeSQLExceptionTranslator, também podemos fornecer nossa própria implementação deSQLExceptionTranslator.

Aqui está um exemplo rápido de uma implementação personalizada, personalizando a mensagem de erro quando há uma violação de chave duplicada, o que resulta emerror code 23505 ao usar 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;
    }
}

Para usar este tradutor de exceção personalizado, precisamos passá-lo paraJdbcTemplate chamando o métodosetExceptionTranslator():

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

5. Operações JDBC usando classes SimpleJdbc

As classesSimpleJdbc fornecem uma maneira fácil de configurar e executar instruções SQL. Essas classes usam metadados do banco de dados para criar consultas básicas. As classesSimpleJdbcInserteSimpleJdbcCall fornecem uma maneira mais fácil de executar chamadas de inserção e procedimento armazenado.

5.1. SimpleJdbcInsert

Vamos dar uma olhada na execução de instruções de inserção simples com configuração mínima.

The INSERT statement is generated based on the configuration of SimpleJdbcInserte tudo o que precisamos é fornecer o nome da tabela, nomes das colunas e valores.

Primeiro, vamos criar umSimpleJdbcInsert:

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

A seguir, vamos fornecer os nomes e valores das colunas e executar a operação:

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

Além disso, para permitirdatabase to generate the primary key, podemos fazer uso da APIexecuteAndReturnKey(); também precisaremos configurar a coluna real que é gerada automaticamente:

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

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

Finalmente - também podemos passar esses dados usandoBeanPropertySqlParameterSourceeMapSqlParameterSource.

5.2. Procedimentos armazenados comSimpleJdbcCall

Além disso, vamos dar uma olhada na execução de procedimentos armazenados - faremos uso da abstraçãoSimpleJdbcCall:

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. Operações em lote

Outro caso de uso simples - agrupando várias operações em conjunto.

6.1. Operações básicas em lote usandoJdbcTemplate

O uso deJdbcTemplate, Batch Operations pode ser executado por meio da APIbatchUpdate().

A parte interessante aqui é a implementação concisa, mas altamente útil 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. Operações em lote usandoNamedParameterJdbcTemplate

Também temos a opção de operações em lote com a APINamedParameterJdbcTemplate -batchUpdate().

Essa API é mais simples que a anterior - não é necessário implementar nenhuma interface extra para definir os parâmetros, pois possui um configurador de instruções preparado interno para definir os valores dos parâmetros.

Em vez disso, os valores dos parâmetros podem ser passados ​​para o métodobatchUpdate() como uma matriz 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 com Spring Boot

Spring Boot fornece um inicialspring-boot-starter-jdbc para usar JDBC com bancos de dados relacionais.

Como em todos os iniciantes do Spring Boot, este também nos ajuda a colocar nosso aplicativo em funcionamento rapidamente.

7.1. Dependência do Maven

Precisaremos da dependênciaspring-boot-starter-jdbc como a principal, bem como uma dependência para o banco de dados que usaremos. Em nosso caso, éMySQL:


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


    mysql
    mysql-connector-java
    runtime

7.2. Configuração

O Spring Boot configura a fonte de dados automaticamente para nós. Precisamos apenas fornecer as propriedades em um arquivoproperties:

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

É isso, apenas fazendo essas configurações apenas, nosso aplicativo está instalado e funcionando e podemos usá-lo para outras operações de banco de dados.

A configuração explícita que vimos na seção anterior para um aplicativo Spring padrão agora está incluída como parte da configuração automática do Spring Boot.

8. Conclusão

Neste artigo, examinamos a abstração do JDBC no Spring Framework, cobrindo os vários recursos fornecidos pelo Spring JDBC com exemplos práticos.

Além disso, analisamos como podemos iniciar rapidamente o Spring JDBC usando um iniciador do Spring Boot JDBC.

O código-fonte dos exemplos está disponívelover on GitHub.