Datenbankabfragen mit Spring aus dem Java Quellcode auslagern

veröffentlicht am 28. Juli 2017

Wer Spring JDBC verwendet, kennt das Problem: längere Datenbankabfragen können nur umständlich im Java Quellcode untergebracht werden. Meist werden solche Abfragen über einen StringBuilder, einen StringJoiner oder die seit Java 8 zur Verfügung stehende String join Methode zusammengesetzt. Man kann sich jedoch das Properties System von Spring zu nutze machen, um Datenbankabfragen aus dem Quellcode auszulagern.

Als Basis für den Artikel dient die folgende (eigentlich in eine Zeile passende) Abfrage:

SELECT *
FROM   users
WHERE  username = :username

Im Java Quellcode wird die Abfrage z.B. wie folgt hinterlegt:

package de.patrickgotthard.springjdbc;

import java.util.Collections;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAO {

    private final NamedParameterJdbcTemplate jdbc;

    @Autowired
    public UserDAO(final NamedParameterJdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    public User getUser(String username) {

        // @formatter:off
        final String sql = String.join(" ",
            "SELECT *",
            "FROM   users",
            "WHERE  username = :username");
        // @formatter:on

        final Map<String, Object> params = Collections.singletonMap("username", username);

        return this.jdbc.queryForObject(sql, params, new BeanPropertyRowMapper<>(User.class));

    }

}

Statt die Abfrage wie oben dargestellt im Quellcode zu hinterlegen, wird sie in eine XML basierte Properties Datei ausgelagert:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>

    <entry key="getUserQuery">
        SELECT *
        FROM   users
        WHERE  username = :username
    </entry>

</properties>

Unter der Annahme, dass diese Properties Datei als UserDAO.xml auf oberster Ebene im classpath liegt, wird die Abfrage nun wie folgt eingebunden:

package de.patrickgotthard.springjdbc;

import java.util.Collections;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
@PropertySource("UserDAO.xml")
public class UserDAO {

    private final NamedParameterJdbcTemplate jdbc;

    @Value("${getUserQuery}")
    private String getUserQuery;

    @Autowired
    public UserDAO(final NamedParameterJdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    public User getUser(String username) {
        final Map<String, Object> params = Collections.singletonMap("username", username);
        return this.jdbc.queryForObject(this.getUserQuery, params, new BeanPropertyRowMapper<>(User.class));
    }

}

Der große Vorteil dieser Methode ist vor allem die Lesbarkeit, aber auch dass Abfragen unkompliziert zwischen Datenbank Editor und der XML Datei ausgetauscht werden können. Das gilt natürlich nur solange die Abfragen weitestgehend statisch sind. Müssen Abfragen dynamisch aufgebaut werden (z.B. Suchabfragen mit variierenden Suchkriterien), empfehle ich den Einsatz anderer Frameworks wie z.B. MyBatis oder Hibernate ORM.