Tutorial: Suchen nach einer Zeichenfolge mithilfe regulärer Ausdrücke (RegEx) in Java

Gilt für: SQL Server 2019 (15.x) und höhere Versionen

In diesem Tutorial wird gezeigt, wie Sie SQL Server-Spracherweiterungen verwenden, um eine Java-Klasse zu erstellen, die zwei Spalten (ID und Text) aus SQL Server und einen RegEx als Eingabeparameter empfängt. Die Klasse gibt an SQL Server zwei Spalten (ID und Text) zurück.

Für einen bestimmten Text in der Textspalte, der an die Java-Klasse gesendet wird, prüft der Code, ob der angegebene reguläre Ausdruck erfüllt ist, und gibt diesen Text zusammen mit der ursprünglichen ID zurück.

In diesem Beispielcode wird ein RegEx verwendet, der prüft, ob ein Text das Wort Java oder java enthält.

Voraussetzungen

Die Befehlszeilenkompilierung mit javac ist für dieses Tutorial ausreichend.

Erstellen von Beispieldaten

Erstellen Sie zunächst eine neue Datenbank, und füllen Sie eine testdataTabelle mit den Spalten ID und text aus.

CREATE DATABASE javatest;
GO

USE javatest;
GO

CREATE TABLE testdata (
    [id] INT NOT NULL,
    [text] NVARCHAR(100) NOT NULL
);
GO

-- Insert data into test table
INSERT INTO testdata ([id], [text])
VALUES (1, 'This sentence contains java');

INSERT INTO testdata ([id], [text])
VALUES (2, 'This sentence does not');

INSERT INTO testdata ([id], [text])
VALUES (3, 'I love Java!');
GO

Erstellen der Hauptklasse

In diesem Schritt erstellen Sie eine Klassendatei namens RegexSample.java und kopieren den folgenden Java-Code in diese Datei.

Diese Hauptklasse importiert das SDK, was bedeutet, dass die in Schritt 1 heruntergeladene JAR-Datei aus dieser Klasse erkennbar sein muss.

package pkg;

import com.microsoft.sqlserver.javalangextension.PrimitiveDataset;
import com.microsoft.sqlserver.javalangextension.AbstractSqlServerExtensionExecutor;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.regex.*;

public class RegexSample extends AbstractSqlServerExtensionExecutor {
    private Pattern expr;

    public RegexSample() {
        // Setup the expected extension version, and class to use for input and output dataset
        executorExtensionVersion = SQLSERVER_JAVA_LANG_EXTENSION_V1;
        executorInputDatasetClassName = PrimitiveDataset.class.getName();
        executorOutputDatasetClassName = PrimitiveDataset.class.getName();
    }

    public PrimitiveDataset execute(PrimitiveDataset input, LinkedHashMap<String, Object> params) {
        // Validate the input parameters and input column schema
        validateInput(input, params);

        int[] inIds = input.getIntColumn(0);
        String[] inValues = input.getStringColumn(1);
        int rowCount = inValues.length;

        String regexExpr = (String)params.get("regexExpr");
        expr = Pattern.compile(regexExpr);

        System.out.println("regex expression: " + regexExpr);

        // Lists to store the output data
        LinkedList<Integer> outIds = new LinkedList<Integer>();
        LinkedList<String> outValues = new LinkedList<String>();

        // Evaluate each row
        for(int i = 0; i < rowCount; i++) {
            if (check(inValues[i])) {
                outIds.add(inIds[i]);
                outValues.add(inValues[i]);
            }
        }

        int outputRowCount = outValues.size();

        int[] idOutputCol = new int[outputRowCount];
        String[] valueOutputCol = new String[outputRowCount];

        // Convert the list of output columns to arrays
        outValues.toArray(valueOutputCol);

        ListIterator<Integer> it = outIds.listIterator(0);
        int rowId = 0;

        System.out.println("Output data:");
        while (it.hasNext()) {
            idOutputCol[rowId] = it.next().intValue();

            System.out.println("ID: " + idOutputCol[rowId] + " Value: " + valueOutputCol[rowId]);
            rowId++;
        }

        // Construct the output dataset
        PrimitiveDataset output = new PrimitiveDataset();

        output.addColumnMetadata(0, "ID", java.sql.Types.INTEGER, 0, 0);
        output.addColumnMetadata(1, "Text", java.sql.Types.NVARCHAR, 0, 0);

        output.addIntColumn(0, idOutputCol, null);
        output.addStringColumn(1, valueOutputCol);

        return output;
    }

    private void validateInput(PrimitiveDataset input, LinkedHashMap<String, Object> params) {
        // Check for the regex expression input parameter
        if (params.get("regexExpr") == null) {
            throw new IllegalArgumentException("Input parameter 'regexExpr' is not found");
        }

        // The expected input schema should be at least 2 columns, (INTEGER, STRING)
        if (input.getColumnCount() < 2) {
            throw new IllegalArgumentException("Unexpected input schema, schema should be an (INTEGER, NVARCHAR or VARCHAR)");
        }

        // Check that the input column types are expected
        if (input.getColumnType(0) != java.sql.Types.INTEGER &&
                (input.getColumnType(1) != java.sql.Types.VARCHAR && input.getColumnType(1) == java.sql.Types.NVARCHAR )) {
            throw new IllegalArgumentException("Unexpected input schema, schema should be an (INTEGER, NVARCHAR or VARCHAR)");
        }
    }

    private boolean check(String text) {
        Matcher m = expr.matcher(text);

        return m.find();
    }
}

Kompilieren und Erstellen einer JAR-Datei

Packen Sie Ihre Klassen und Abhängigkeiten in .jar-Dateien. Die meisten Java-IDEs (z. B. Eclipse oder IntelliJ) unterstützen das Erstellen von .jar-Dateien, wenn Sie das Projekt erstellen oder kompilieren. Nennen Sie die Datei .jar regex.jar.

Wenn Sie keine Java-IDE verwenden, können Sie eine .jar-Datei auch manuell erstellen. Weitere Informationen finden Sie unter Erstellen einer Java-jar-Datei aus Klassendateien.

Hinweis

In diesem Tutorial werden Pakete verwendet. Die Zeile package pkg; am Anfang der Klasse stellt sicher, dass der kompilierte Code in einem Unterordner mit dem Namen pkg gespeichert wird. Wenn Sie eine IDE verwenden, wird der kompilierte Code automatisch in diesem Ordner gespeichert. Wenn Sie javac verwenden, um die Klassen manuell zu kompilieren, müssen Sie den kompilierten Code selbst im Ordner pkg platzieren.

Erstellen einer externen Sprache

Sie müssen eine externe Sprache in der Datenbank erstellen. Die externe Sprache ist ein datenbankweites Objekt, was bedeutet, dass externe Sprachen wie Java für jede Datenbank, in der Sie sie verwenden möchten, erstellt werden müssen.

Erstellen einer externen Sprache unter Windows

Wenn Sie Windows verwenden, führen Sie die folgenden Schritte aus, um eine externe Sprache für Java zu erstellen.

  1. Erstellen Sie eine ZIP-Datei mit der Erweiterung.

    Im Rahmen des SQL Server-Setups unter Windows wird die .zip-Datei mit der Java-Erweiterung an diesem Speicherort installiert: [SQL Server install path]\MSSQL\Binn\java-lang-extension.zip. Diese ZIP-Datei enthält die javaextension.dll.

  2. Erstellen Sie eine externe Sprache „Java“ aus der ZIP-Datei:

    CREATE EXTERNAL LANGUAGE Java
    FROM
    (CONTENT = N'[SQL Server install path]\MSSQL\Binn\java-lang-extension.zip', FILE_NAME = 'javaextension.dll',
    ENVIRONMENT_VARIABLES = N'{"JRE_HOME":"<path to JRE>"}' );
    GO
    

Erstellen einer externen Sprache unter Linux

Im Rahmen des Setups wird die Datei mit der Erweiterung .tar.gz unter folgendem Pfad gespeichert: /opt/mssql-extensibility/lib/java-lang-extension.tar.gz.

Führen Sie die folgende T-SQL-Anweisung aus, um eine externe Sprache „Java“ zu erstellen:

CREATE EXTERNAL LANGUAGE Java
FROM (CONTENT = N'/opt/mssql-extensibility/lib/java-lang-extension.tar.gz', file_name = 'javaextension.so',
ENVIRONMENT_VARIABLES = N'{"JRE_HOME":"<path to JRE>"}' );
GO

Berechtigungen zum Ausführen einer externen Sprache

Damit ein Benutzer Java-Code ausführen kann, benötigt dieser die Berechtigung zum Ausführen externer Skripts für diese bestimmte Sprache.

Weitere Informationen finden Sie unter CREATE EXTERNAL LANGUAGE.

Erstellen von externen Bibliotheken

Verwenden Sie CREATE EXTERNAL LIBRARY, um eine externe Bibliothek für Ihre .jar-Dateien zu erstellen. SQL Server wird auf die .jar-Dateien zugreifen können, sodass Sie keine speziellen Berechtigungen für den classpath festlegen müssen.

In diesem Beispiel erstellen Sie zwei externe Bibliotheken. Eine für das SDK und eine für den RegEx-Java-Code.

  1. Die SDK-Jar-Datei mssql-java-lang-extension.jar wird als Teil von SQL Server 2019 (15.x) und höheren Versionen sowohl unter Windows als auch unter Linux installiert.

    • Standardinstallationspfad unter Windows: <instance installation home directory>\MSSQL\Binn\mssql-java-lang-extension.jar

    • Standardinstallationspfad unter Linux: /opt/mssql/lib/mssql-java-lang-extension.jar

    Da es sich um einen Open-Source-Code handelt, ist dieser auch im GitHub-Repository für SQL Server-Spracherweiterungen verfügbar. Weitere Informationen finden Sie unter Microsoft Erweiterbarkeits-SDK für Java für Microsoft SQL Server.

  2. Erstellen Sie eine externe Bibliothek für das SDK.

    CREATE EXTERNAL LIBRARY sdk
    FROM (CONTENT = '<OS specific path from above>/mssql-java-lang-extension.jar')
    WITH (LANGUAGE = 'Java');
    GO
    
  3. Erstellen Sie eine externe Bibliothek für den RegEx-Code.

    CREATE EXTERNAL LIBRARY regex
    FROM (CONTENT = '<path>/regex.jar')
    WITH (LANGUAGE = 'Java');
    GO
    

Festlegen von Berechtigungen

Hinweis

Überspringen Sie diesen Schritt, wenn Sie im vorherigen Schritt externe Bibliotheken verwendet haben. Es wird empfohlen, eine externe Bibliothek aus der .jar-Datei zu erstellen.

Wenn Sie keine externen Bibliotheken verwenden möchten, müssen Sie die erforderlichen Berechtigungen festlegen. Die Skriptausführung ist nur erfolgreich, wenn die Prozess-IDs auf Ihren Code zugreifen können. Weitere Informationen zum Festlegen von Berechtigungen finden Sie im Installationsleitfaden.

Unter Linux

Gewähren Sie dem Benutzer mssql_satellite Berechtigungen zum Lesen/Ausführen für den Klassenpfad.

Unter Windows

Gewähren Sie SQLRUserGroup und der SID Alle Anwendungspakete Berechtigungen zum Lesen/Ausführen für den Ordner, in dem sich der kompilierte Java-Code befindet.

Die gesamte Struktur muss über Berechtigungen verfügen, vom übergeordneten Stammverzeichnis bis hin zum letzten Unterordner.

  1. Klicken Sie mit der rechten Maustaste auf den Ordner (z. B. C:\myJavaCode), und wählen Sie Eigenschaften>Sicherheit aus.
  2. Wählen Sie Bearbeiten aus.
  3. Wählen Sie Hinzufügen.
  4. Unter Benutzer, Computer, Dienstkonten oder Gruppen auswählen:
    1. Klicken Sie auf Objekttypen und stellen Sie sicher, dass Integrierte Sicherheitsprinzipien und Gruppen ausgewählt sind.
    2. Klicken Sie auf Speicherorte, um den Namen des lokalen Computers oben in der Liste auszuwählen.
  5. Geben Sie SQLRUserGroup ein, überprüfen Sie den Namen, und klicken Sie auf OK, um die Gruppe hinzuzufügen.
  6. Geben Sie ALL APPLICATION PACKAGES (Alle Anwendungspakete) ein, überprüfen Sie den Namen, und klicken Sie zum Hinzufügen auf OK. Wenn der Name nicht aufgelöst wird, gehen Sie zurück zum Schritt „Speicherorte“. Die SID wird lokal auf Ihrem Computer gespeichert.

Stellen Sie sicher, dass beide Sicherheits-IDs über Berechtigungen zum Lesen und Ausführen für den Ordner und den Unterordner pkg verfügen.

Aufrufen der Java-Klasse

Erstellen Sie eine gespeicherte Prozedur, die sp_execute_external_script aufruft, um den Java-Code von SQL Server aufzurufen. Legen Sie im Parameter script fest, welche package.class-Klasse Sie aufrufen möchten. Im folgenden Code gehört die Klasse zu einem Paket namens pkg und einer Klassendatei namens RegexSample.java.

Hinweis

Der Code definiert nicht, welche Methode aufgerufen werden soll. Standardmäßig wird die execute-Methode aufgerufen. Das bedeutet, dass Sie der SDK-Schnittstelle folgen und eine „execute“-Methode in der Java-Klasse implementieren müssen, wenn Sie die Klasse aus SQL Server abrufen möchten.

Die gespeicherte Prozedur übernimmt eine Eingabeabfrage (Eingabedataset) und einen regulären Ausdruck und gibt die Zeilen zurück, die den angegebenen regulären Ausdruck erfüllen. Es wird ein regulärer Ausdruck [Jj]ava verwendet, der prüft, ob ein Text das Wort Java oder java enthält.

CREATE OR ALTER PROCEDURE [dbo].[java_regex]
    @expr NVARCHAR(200), @query NVARCHAR(400)
AS
BEGIN
    --Call the Java program by giving the package.className in @script
    --The method invoked in the Java code is always the "execute" method
    EXEC sp_execute_external_script @language = N'Java',
        @script = N'pkg.RegexSample',
        @input_data_1 = @query,
        @params = N'@regexExpr nvarchar(200)',
        @regexExpr = @expr
    WITH result sets((
        ID INT,
        TEXT NVARCHAR(100)
    ));
END
GO

--Now execute the above stored procedure and provide the regular expression and an input query
EXECUTE [dbo].[java_regex] N'[Jj]ava',
    N'SELECT id, text FROM testdata'
GO

Ergebnisse

Nach der Ausführung des Aufrufs sollten Sie ein Resultset mit zwei der Zeilen erhalten.

Screenshot der Ergebnisse aus dem Java-Beispiel

Wenn Sie eine Fehlermeldung erhalten:

  • Wenn Sie Ihre Klassen kompilieren, sollte der Unterordner pkg den kompilierten Code für alle drei Klassen erhalten.

  • Wenn Sie keine externen Bibliotheken verwenden, überprüfen Sie die Berechtigungen für jeden einzelnen Ordner, vom root bis zum Unterordner pkg. So können Sie sicherstellen, dass die Sicherheits-IDs, die den externen Prozess ausführen, über Berechtigungen zum Lesen und Ausführen des Codes verfügen.