Einstieg in Spring Boot, Teil 14 JDBCTemplate für Fortgeschrittene
Mit der Klasse JDBCTemplate lassen sich Datenbankabfragen in Spring Boot ohne hässlichen Boilerplate-Code formulieren. Die Klasse kann aber noch mehr als das, was wir im vorangegangenen Beitrag schon gezeigt haben.
Anbieter zum Thema

Das folgende Codestück demonstriert das Einfügen eines Datensatzes in die Datenbank mithilfe der update()-Methode von JDBCTemplate:
String INSERT = "INSERT INTO Person (id, firstname, lastname) VALUES (?, ?, ?)";
jdbcTemplate.update(INSERT, 3, "Rainer", "Wain");
Auch hier fällt der ganze Boilerpate-Code zum Öffnen der Datenbank-Connection, Erstellen des Statements und Behandeln der IOException weg. Die Instanz von JDBCTemplate wird wie im vorhergehenden Beitrag gezeigt, zum Beispiel mithilfe von @Autowired injiziert.
Das Hantieren mit langen SQL-Strings wie oben ist üblich, aber auch recht fehlerträchtig. Besonders gefährlich wird es, wenn die Strings zur Laufzeit zusammengebaut werden. Aber auch in dem einfachen Beispiel hier gibt es schon Fallstricke.
Die Reihenfolge der Parameter muss beim Übergeben der Argumente zwingend eingehalten werden, damit das richtige Fragezeichen ersetzt wird. Wenn dabei ein Fehler unterläuft, fällt das womöglich nicht gleich auf und die Datenbank füllt sich mit fehlerhaften Daten.
Einfache Inserts mit SimpleJdbcInsert
Glücklicherweise geht das auch eleganter, beispielsweise mit der Klasse SimpleJdbcInsert. Einer Instanz dieses Typs wird dazu das JDBCTemplate übergeben und der Tabellenname mitgeteilt. Das Ausführen des Statements erfolgt mit der Methode execute(), die als Argument eine Map mit Spaltennamen und zu setzenden Werten übergeben bekommt:
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate).withTableName("Person");
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("ID", 3);
parameters.put("firstname", "Wilma");
parameters.put("lastname", "Ruhe");
simpleJdbcInsert.execute(parameters);
Das Ablegen von Spaltennamen und Werten in der Map führt zwar zu etwas mehr Code, ist dafür aber weniger fehleranfällig. Der Primärschlüssel wurde oben übergeben, man kann ihn aber auch generieren lassen. Den erzeugten Wert erhält man nach dem Ausführen des Statements mit der Methode executeAndReturnKey() zurück:
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(jdbcTemplate)
.withTableName("Person")
.usingGeneratedKeyColumns("Id");
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("firstname", "Wilma");
parameters.put("lastname", "Ruhe");
Number newId = simpleJdbcInsert.executeAndReturnKey(parameters);
Zum Aktualisieren von Datensätzen existiert leider kein “SimpleJdbcUpdate”. Hier muss also auf jdbcTemplate.update() zurückgegriffen werden:
String UPDATE = "UPDATE Person SET firstname = ?, lastname = ? WHERE Id = ?";
jdbcTemplate.update(UPDATE, "Frank", "Reich", 3);
Das Gleiche gilt für das Löschen von Datensätzen.
Das Kind beim Namen nennen: NamedParameterJdbcTemplate
Um Fehler mit der Parameterreihenfolge zu minimieren und mehr Lesbarkeit in den Code zu bringen, bietet sich als Alternative die Klasse NamedParameterJdbcTemplate an. Dabei werden die Namen der Parameter im SQL-String mit vorangestelltem Doppelpunkt ausgeschrieben. Anstelle der einfachen Werte bekommt die update()-Methode eine Map mit den Parameternamen als Keys:
String UPDATE = "UPDATE Person SET firstname = :firstname, lastname = :lastname WHERE Id = :id";
Map<String, Object> params = new HashMap<>();
params.put("id", 2);
params.put("firstname", "Andy");
params.put("lastname", "Tür");
namedParameterJdbcTemplate.update(UPDATE, params);
NamedParameterJdbcTemplate lässt sich wie JdbcTemplate auch für Inserts, Deletes und Queries verwenden.
Batch-Verarbeitung
Zur Massendatenverarbeitung wie sie beispielsweise im Bankenumfeld in nächtlichen Läufen üblich ist, bietet JDBCTemplate eine eigene Methode: batchUpdate(). Damit lässt sich das gleiche Statement wiederholt mit verschiedenen Daten ausführen. Im folgenden Beispiel werden die Datensätze in der Tabelle Person mit einer übergebenen Liste von Personen aktualisiert.
Die verwendete batchUpdate()-Methode erwartet nach dem SQL-String eine Implementierung des Interfaces BatchPreparedStatementSetter. Darin enthalten sind die Methoden setValues() zum Setzen der einzelnen Attribute und die Methode getBatchSize(), die die Anzahl der zu aktualisierenden Datensätze zurückgibt:
public int[] batchUpdate(final List<Person> persons) {
int[] updateCounts = jdbcTemplate.batchUpdate("UPDATE Person SET firstname = ?, lastname = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, persons.get(i).getFirstname());
ps.setString(2, persons.get(i).getLastname());
ps.setLong(3, persons.get(i).getId().longValue());
}
public int getBatchSize() {
return persons.size();
}
});
return updateCounts;
}
Eine analoge Methode mit angepasstem SQL-Statement lässt sich auch zum Einfügen von Datensätzen verwenden:
public int[] batchInsert(final List<Person> persons) {
int[] updateCounts = jdbcTemplate.batchUpdate("INSERT INTO Person (firstname, lastname ) VALUES (?, ?)",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, persons.get(i).getFirstname());
ps.setString(2, persons.get(i).getLastname());
}
public int getBatchSize() {
return persons.size();
}
});
return updateCounts;
}
Wie die vorgestellten Beispiele zeigen helfen JDBCTemplate und die verwandten Klassen nicht nur bei der Reduzierung von stupidem Code ohne Fachbezug, sie bieten darüber hinaus clevere Lösungen für gängige Probleme bei der Arbeit mit Datenbanken an. Wer aus welchen Gründen auch immer nicht JPA einsetzen kann oder möchte, hat somit eine leistungsfähige Erweiterung der JDBC-API zur Hand.
Wie die Überwachung von Anwendungen unter Spring Boot mit dem Actuator funktioniert, werden wir uns im Teil 15 dieser Serie zu Gmüte führen.
(ID:47397822)