Spring JDBC

Spring JDBC

1. Überblick

In diesem Artikel werden praktische Anwendungsfälle des Spring JDBC-Moduls behandelt.

Alle Klassen in Spring JDBC sind in vier separate Pakete unterteilt:

  • core - die Kernfunktionalität von JDBC. Einige der wichtigen Klassen in diesem Paket umfassenJdbcTemplate,SimpleJdbcInsert,SimpleJdbcCall undNamedParameterJdbcTemplate.

  • datasource - Dienstprogrammklassen für den Zugriff auf eine Datenquelle. Es gibt auch verschiedene Datenquellenimplementierungen zum Testen von JDBC-Code außerhalb des Java EE-Containers.

  • object - Objektorientierter DB-Zugriff. Es ermöglicht die Ausführung von Abfragen und die Rückgabe der Ergebnisse als Geschäftsobjekt. Außerdem werden die Abfrageergebnisse zwischen den Spalten und Eigenschaften von Geschäftsobjekten zugeordnet.

  • support - Unterstützungsklassen für Klassen untercore undobject Paketen. E.g. bietet die Übersetzungsfunktionalität vonSQLException.

    == Weiterführende Literatur:

Spring Security: Erkundung der JDBC-Authentifizierung

Informieren Sie sich über die Funktionen von Spring zur Durchführung der JDBC-Authentifizierung unter Verwendung einer vorhandenen DataSource-Konfiguration.

Read more

Einführung in Spring Data JPA

Einführung in Spring Data JPA mit Spring 4 - die Spring-Konfiguration, das DAO, manuelle und generierte Abfragen und das Transaktionsmanagement.

Read more

2. Aufbau

Beginnen wir zunächst mit einer einfachen Konfiguration der Datenquelle (für dieses Beispiel verwenden wir eine MySQL-Datenbank):

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

Alternativ können wir auch eine eingebettete Datenbank zum Entwickeln oder Testen verwenden. Hier ist eine schnelle Konfiguration, mit der eine Instanz der eingebetteten H2-Datenbank erstellt und mit einfachen SQL-Skripten vorab aufgefüllt wird:

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

Schließlich - dasselbe kann natürlich mit der XML-Konfiguration für diedatasource geschehen:


    
    
    
    

3. DieJdbcTemplate und laufenden Abfragen

3.1. Grundlegende Abfragen

Die JDBC-Vorlage ist die Haupt-API, über die wir auf die meisten Funktionen zugreifen können, an denen wir interessiert sind:

  • Herstellen und Schließen von Verbindungen

  • Ausführen von Anweisungen und Aufrufen gespeicherter Prozeduren

  • Iterieren überResultSet und Zurückgeben von Ergebnissen

Beginnen wir zunächst mit einem einfachen Beispiel, um zu sehen, was dieJdbcTemplate können:

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

und hier ist auch ein einfaches INSERT:

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

Beachten Sie die Standardsyntax für die Bereitstellung von Parametern - unter Verwendung des Zeichens?. Weiter - Schauen wir uns eine Alternative zu dieser Syntax an.

3.2. Abfragen mit benannten Parametern

Umsupport for named parameters zu erhalten, verwenden wir die andere vom Framework bereitgestellte JDBC-Vorlage -NamedParameterJdbcTemplate.

Darüber hinaus werden dieJbdcTemplate umbrochen und eine Alternative zur herkömmlichen Syntax bereitgestellt, bei der "?" zum Angeben von Parametern verwendet wird. Unter der Haube werden die genannten Parameter durch JDBC "?" Platzhalter und Delegaten an die umschlossenenJDCTemplate, um die Abfragen auszuführen:

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

Beachten Sie, wie wirMapSqlParameterSource verwenden, um die Werte für die genannten Parameter bereitzustellen.

Schauen wir uns zum Beispiel das folgende Beispiel an, in dem Eigenschaften einer Bean verwendet werden, um die benannten Parameter zu bestimmen:

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

Beachten Sie, wie wir jetzt die Implementierungen vonBeanPropertySqlParameterSourceverwenden, anstatt die benannten Parameter wie zuvor manuell anzugeben.

3.3. Zuordnen von Abfrageergebnissen zu Java-Objekten

Eine weitere sehr nützliche Funktion ist die Möglichkeit, Abfrageergebnisse Java-Objekten zuzuordnen - durch Implementierung derthe RowMapper-Schnittstelle.

Beispiel: Für jede Zeile, die von der Abfrage zurückgegeben wird, verwendet Spring den Zeilenzuordner, um die Java-Bean zu füllen:

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

Anschließend können wir den Row Mapper an die Abfrage-API übergeben und vollständig aufgefüllte Java-Objekte abrufen:

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

4. Ausnahme Übersetzung

Spring wird standardmäßig mit einer eigenen Datenausnahmehierarchie geliefert - mitDataAccessException als Stammausnahme - und übersetzt alle zugrunde liegenden Rohausnahmen in diese.

Daher behalten wir unsere Vernunft bei, indem wir keine Persistenzausnahmen auf niedriger Ebene behandeln müssen, und profitieren von der Tatsache, dass Spring die Ausnahmen auf niedriger Ebene inDataAccessException oder einer seiner Unterklassen umschließt.

Dadurch bleibt der Mechanismus zur Ausnahmebehandlung auch unabhängig von der zugrunde liegenden Datenbank, die wir verwenden.

Außerdem können wir als StandardSQLErrorCodeSQLExceptionTranslator auch unsere eigene Implementierung vonSQLExceptionTranslator bereitstellen.

Hier ist ein kurzes Beispiel für eine benutzerdefinierte Implementierung, bei der die Fehlermeldung angepasst wird, wenn eine doppelte Schlüsselverletzung vorliegt, die bei Verwendung von H2 zuerror code 23505 führt:

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

Um diesen benutzerdefinierten Ausnahmeübersetzer zu verwenden, müssen wir ihn anJdbcTemplate übergeben, indem wir die MethodesetExceptionTranslator() aufrufen:

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

5. JDBC-Operationen mit SimpleJdbc-Klassen

Die Klassen vonSimpleJdbcbieten eine einfache Möglichkeit, SQL-Anweisungen zu konfigurieren und auszuführen. Diese Klassen verwenden Datenbankmetadaten, um grundlegende Abfragen zu erstellen. Die KlassenSimpleJdbcInsert undSimpleJdbcCall bieten eine einfachere Möglichkeit, Aufrufe zum Einfügen und Speichern gespeicherter Prozeduren auszuführen.

5.1. SimpleJdbcInsert

Lassen Sie uns einen Blick auf die Ausführung einfacher Einfügeanweisungen mit minimaler Konfiguration werfen.

The INSERT statement is generated based on the configuration of SimpleJdbcInsert und alles, was wir brauchen, ist die Angabe des Tabellennamens, der Spaltennamen und der Werte.

Erstellen wir zunächst einSimpleJdbcInsert:

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

Geben Sie als Nächstes die Spaltennamen und -werte ein und führen Sie die Operation aus:

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

Um diedatabase to generate the primary key zuzulassen, können wir außerdem dieexecuteAndReturnKey()-API verwenden. Wir müssen auch die tatsächliche Spalte konfigurieren, die automatisch generiert wird:

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

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

Schließlich können wir diese Daten auch mitBeanPropertySqlParameterSource undMapSqlParameterSource. übergeben

5.2. Gespeicherte Prozeduren mitSimpleJdbcCall

Schauen wir uns auch die Ausführung gespeicherter Prozeduren an. Wir verwenden die Abstraktion vonSimpleJdbcCall:

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. Stapeloperationen

Ein weiterer einfacher Anwendungsfall: Mehrere Vorgänge gleichzeitig stapeln.

6.1. Grundlegende Stapeloperationen mitJdbcTemplate

Die Verwendung vonJdbcTemplate, Batch Operations kann über diebatchUpdate()-API ausgeführt werden.

Der interessante Teil hier ist die prägnante, aber äußerst nützliche Implementierung vonBatchPreparedStatementSetter:

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. Stapeloperationen mitNamedParameterJdbcTemplate

Wir haben auch die Möglichkeit, Stapelvorgänge mit der APINamedParameterJdbcTemplate -batchUpdate() durchzuführen.

Diese API ist einfacher als die vorherige - es müssen keine zusätzlichen Schnittstellen zum Festlegen der Parameter implementiert werden, da sie über einen internen vorbereiteten Anweisungssetter zum Festlegen der Parameterwerte verfügt.

Stattdessen können die Parameterwerte als Array vonSqlParameterSource an die MethodebatchUpdate() übergeben werden.

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

7. Spring JDBC mit Spring Boot

Spring Boot bietet einen Starterspring-boot-starter-jdbc für die Verwendung von JDBC mit relationalen Datenbanken.

Wie bei jedem Spring Boot-Starter hilft uns auch dieser, unsere Anwendung schnell zum Laufen zu bringen.

7.1. Maven-Abhängigkeit

Wir benötigen die Abhängigkeit vonspring-boot-starter-jdbcals primäre Abhängigkeit sowie eine Abhängigkeit für die Datenbank, die wir verwenden werden. In unserem Fall ist diesMySQL:


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


    mysql
    mysql-connector-java
    runtime

7.2. Aufbau

Spring Boot konfiguriert die Datenquelle automatisch für uns. Wir müssen nur die Eigenschaften in einerproperties-Datei angeben:

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

Wenn Sie nur diese Konfigurationen durchführen, ist unsere Anwendung betriebsbereit und wir können sie für andere Datenbankoperationen verwenden.

Die explizite Konfiguration, die wir im vorherigen Abschnitt für eine Standard-Spring-Anwendung gesehen haben, ist jetzt Teil der automatischen Konfiguration von Spring Boot.

8. Fazit

In diesem Artikel haben wir uns mit der JDBC-Abstraktion im Spring Framework befasst und die verschiedenen Funktionen von Spring JDBC anhand praktischer Beispiele erläutert.

Außerdem haben wir uns überlegt, wie wir mithilfe eines Spring Boot JDBC-Starters schnell mit Spring JDBC beginnen können.

Der Quellcode für die Beispiele istover on GitHub verfügbar.