Java war lange die Sprache, in der man für formatierte Strings String.format() oder umständliche +-Ketten schreiben musste. Mit Java 21 (JEP 430) kam endlich eine elegante Lösung: String Templates. Dieser Artikel zeigt die Syntax, erklärt eigene Template-Prozessoren und demonstriert, wie String Templates SQL-Injection-Angriffe zur Compile-Time verhindern.

Wichtig vorab: String Templates sind in Java 21 (JEP 430) und 22 (JEP 459) ein Preview-Feature. Die für JDK 23 geplante Finalisierung (JEP 465) wurde zurückgezogen (Status: Withdrawn). Das Feature ist daher ab JDK 23 nicht mehr verfügbar und befindet sich im Redesign. Mit JDK 21/22 und --enable-preview ist es weiterhin nutzbar.

Grundlagen: STR und FMT

Die Basis-Syntax ist PROZESSOR."Text \{ausdruck}" — der Template-Prozessor STR ersetzt \{...} mit dem Ergebnis des Ausdrucks:

String name = "Lena";
int bestellungen = 42;

String nachricht = STR."Hallo \{name}, du hast \{bestellungen} Bestellungen.";
<em>// "Hallo Lena, du hast 42 Bestellungen."</em>

<em>// Ausdrücke sind vollständig: Methodenaufrufe, Arithmetik, sogar andere Templates</em>
String info = STR."Summe: \{bestellungen * 2 + 10}";
<em>// "Summe: 94"</em>
Code-Sprache: JavaScript (javascript)

Der FMT-Prozessor kombiniert Templates mit java.util.Formatter-Spezifikationen:

record Produkt(String name, double preis) {}

Produkt[] produkte = {
    new Produkt("Tastatur", 89.99),
    new Produkt("Monitor", 349.00),
    new Produkt("Maus", 24.50)
};

String tabelle = FMT."""
    %-15s\{produkte[0].name}  %8.2f\{produkte[0].preis}
    %-15s\{produkte[1].name}  %8.2f\{produkte[1].preis}
    %-15s\{produkte[2].name}  %8.2f\{produkte[2].preis}
    """;
<em>// Tastatur           89.99</em>
<em>// Monitor           349.00</em>
<em>// Maus               24.50</em>
Code-Sprache: PHP (php)

Text Blocks (""") und Templates ergänzen sich perfekt für mehrzeilige Strings:

String json = STR."""
    {
        "name": "\{name}",
        "bestellungen": \{bestellungen}
    }
    """;
Code-Sprache: PHP (php)

RAW: Zugriff auf unverarbeitete Templates

Der RAW-Prozessor liefert ein StringTemplate-Objekt zurück:

int x = 10, y = 20;
StringTemplate st = RAW."\{x} plus \{y} gleich \{x + y}";

st.fragments();  <em>// ["", " plus ", " gleich ", ""]</em>
st.values();     <em>// [10, 20, 30]</em>
Code-Sprache: JavaScript (javascript)

Wichtig: RAW produziert absichtlich keinen String — du musst das Template immer explizit durch einen Prozessor schicken, bevor ein String entsteht. Das verhindert versehentliche, ungeprüfte Interpolation.

SQL-Injection mit String Templates verhindern

Dieses Beispiel zeigt, warum String Templates für Datenbankzugriffe so wertvoll sind. Angenommen, ein Angreifer gibt diesen Suchtext ein:

String suchbegriff = "Müller' OR 1=1 --";
<em>// Gefährliche String-Konkatenation:</em>
String unsafe = "SELECT * FROM kunden WHERE name = '" + suchbegriff + "'";
<em>// Ergebnis: SELECT * FROM kunden WHERE name = 'Müller' OR 1=1 --'</em>
<em>// -> Gibt ALLE Datensätze zurück!</em>
Code-Sprache: JavaScript (javascript)

Ein eigener Template-Prozessor ersetzt \{...}-Stellen mit JDBC-?-Platzhaltern und übergibt die Werte sicher via PreparedStatement:

import java.sql.*;

record QueryProcessor(Connection conn)
    implements StringTemplate.Processor<PreparedStatement, SQLException> {

    public PreparedStatement process(StringTemplate st) throws SQLException {
        String query = String.join("?", st.fragments());

        PreparedStatement ps = conn.prepareStatement(query);
        int index = 1;
        for (Object value : st.values()) {
            switch (value) {
                case Integer i  -> ps.setInt(index++, i);
                case Long l     -> ps.setLong(index++, l);
                case Double d   -> ps.setDouble(index++, d);
                case Boolean b  -> ps.setBoolean(index++, b);
                case null       -> ps.setNull(index++, Types.NULL);
                default         -> ps.setString(index++, value.toString());
            }
        }
        return ps;
    }
}

<em>// Verwendung:</em>
var DB = new QueryProcessor(connection);
PreparedStatement stmt = DB."""
    SELECT * FROM kunden WHERE name = \{suchbegriff}
    """;
ResultSet rs = stmt.executeQuery();
Code-Sprache: PHP (php)

Selbst mit bösartigem Input — Müller' OR 1=1 -- — entsteht daraus:

SELECT * FROM kunden WHERE name = ?

mit suchbegriff als gebundenem Parameter. Keine SQL-Injection möglich.

Eigener Template-Prozessor

Das Interface ist einfach: StringTemplate.Processor<R, E>R ist der Rückgabetyp, E die mögliche Exception. Hier ein Prozessor, der JSON produziert und gleichzeitig auf Injection prüft:

StringTemplate.Processor<String, IllegalArgumentException> JSON =
    st -> {
        String quote = "\"";
        for (Object value : st.values()) {
            if (value instanceof String s && s.contains(quote)) {
                throw new IllegalArgumentException(
                    "String-Wert enthält Anführungszeichen: " + s);
            }
        }
        List<Object> safeValues = new ArrayList<>();
        for (Object v : st.values()) {
            if (v instanceof String s) {
                safeValues.add(quote + s + quote);
            } else {
                safeValues.add(v);
            }
        }
        return StringTemplate.interpolate(st.fragments(), safeValues);
    };

<em>// Nutzung:</em>
String person = "Lena";
int alter = 30;
String result = JSON."""
    {"name": \{person}, "alter": \{alter}}
    """;
<em>// {"name": "Lena", "alter": 30}</em>
Code-Sprache: PHP (php)

Preview-Feature aktivieren

Für Java 21 oder 22 mit --enable-preview:

javac --enable-preview --release 21 MeinProgramm.java
java --enable-preview MeinProgramm
Code-Sprache: CSS (css)

In Maven:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <compilerArgs>
            <arg>--enable-preview</arg>
        </compilerArgs>
    </configuration>
</plugin>
Code-Sprache: HTML, XML (xml)

Ausblick

Die für JDK 23 geplante Finalisierung der String Templates (JEP 465) wurde zurückgezogen (Status: Withdrawn). Die enge Kopplung von Template-Erfassung und -Verarbeitung in der Syntax PROZESSOR."template" galt als problematisch. Stand Juli 2026 gibt es noch keinen Nachfolge-JEP; ein Redesign mit möglicherweise getrennten Schritten für Capture und Processing wird in der Community diskutiert. In der Zwischenzeit bleibt JDK 21/22 die einzige Möglichkeit, mit dieser Preview zu experimentieren.

Die grundlegende Idee — typsichere, validierbare String-Interpolation mit anwendungsspezifischen Prozessoren — bleibt relevant, auch wenn das finale Design noch offen ist.