Руководство. Поиск строки с помощью регулярных выражений (regex) в Java

Область применения: SQL Server 2019 (15.x) и более поздних версий

В этом руководстве показано, как использовать расширения языка SQL Server и создать класс Java, принимающий два столбца (идентификатор и текст) из SQL Server и регулярное выражение (regex) в качестве входного параметра. Класс возвращает в SQL Server два столбца (идентификатор и текст).

Для заданного текста в текстовом столбце, отправленном в класс Java, код проверяет, выполнено ли данное регулярное выражение, и возвращает этот текст вместе с исходным идентификатором.

В этом примере кода используется регулярное выражение, которое проверка, если текст содержит слово Java илиjava.

Необходимые компоненты

Для работы с этим руководством достаточно использовать javac компиляцию командной строки.

Создание примера набора данных

Сначала создайте новую базу данных и заполните таблицу testdataID столбцами.text

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

Создание основного класса

На этом шаге создайте файл класса с именем RegexSample.java и скопируйте следующий код Java в этот файл.

Этот основной класс импортирует пакет SDK — это значит, что JAR-файл, скачанный в шаге 1, должен быть доступен для обнаружения из этого класса.

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

Компиляция и создание JAR-файла

Упакуйте классы и зависимости в файлы .jar. Большинство IDE Java (например, Eclipse или IntelliJ) поддерживают создание файлов .jar при сборке или компиляции проекта. Присвойте файлу .jarregex.jarимя.

Если вы не используете интегрированную среду разработки Java, можно создать .jar файл вручную. Дополнительные сведения см. в статье "Создание файла java .jar из файлов классов".

Примечание.

В этом учебнике используются пакеты. Строка package pkg; в верхней части класса гарантирует, что скомпилированный код сохраняется в вложенной папке pkg. Если используется IDE, скомпилированный код автоматически сохраняется в этой папке. Если вы используете javac для ручной компиляции классов, необходимо поместить скомпилированный код в папку pkg .

Создание внешнего языка

Необходимо создать внешний язык в базе данных. Внешний язык — это объект области базы данных. Это значит, что внешние языки, такие как Java, необходимо создавать для каждой базы данных, в которой вы хотите их использовать.

Создание внешнего языка в Windows

Если вы используете Windows, выполните следующие действия, чтобы создать внешний язык для Java.

  1. Создайте ZIP-файл, содержащий расширение.

    В рамках установки SQL Server в Windows файл расширения .zip Java устанавливается в этом расположении: [SQL Server install path]\MSSQL\Binn\java-lang-extension.zip Этот ZIP-файл содержит файл javaextension.dll.

  2. Создайте внешний язык Java из ZIP-файла:

    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
    

Создание внешнего языка в Linux

В рамках установки файл расширения .tar.gz сохраняется по следующему пути: /opt/mssql-extensibility/lib/java-lang-extension.tar.gz

Чтобы создать внешний язык Java, выполните в Linux следующую инструкцию T-SQL:

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

Разрешения на выполнение внешнего языка

Для выполнения кода Java пользователю необходимо предоставить разрешение на выполнение внешнего скрипта на этом языке.

Дополнительные сведения см. в разделе CREATE EXTERNAL LANGUAGE.

Создание внешних библиотек

Для создания внешней библиотеки для файлов .jar используйте инструкцию CREATE EXTERNAL LIBRARY. SQL Server имеет доступ к .jar файлам, и вам не нужно задавать специальные разрешения classpath.

В этом примере вы создадите две внешние библиотеки. одна для пакета SDK, вторая для кода регулярных выражений Java.

  1. Jar-файл mssql-java-lang-extension.jar пакета SDK устанавливается как часть SQL Server 2019 (15.x) и более поздних версий в Windows и Linux.

    • Путь установки по умолчанию в Windows: <instance installation home directory>\MSSQL\Binn\mssql-java-lang-extension.jar

    • Путь установки по умолчанию в Linux: /opt/mssql/lib/mssql-java-lang-extension.jar

    Это открытый код, который можно найти в репозитории расширений языка SQL Server в GitHub. Дополнительные сведения см. в пакете SDK расширяемости Microsoft для Java для SQL Server.

  2. Создайте внешнюю библиотеку для пакета SDK.

    CREATE EXTERNAL LIBRARY sdk
    FROM (CONTENT = '<OS specific path from above>/mssql-java-lang-extension.jar')
    WITH (LANGUAGE = 'Java');
    GO
    
  3. Создайте внешнюю библиотеку для кода RegEx.

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

Настройка разрешений

Примечание.

Пропустите этот шаг, если на предыдущем этапе вы использовали внешние библиотеки. Рекомендуемый способ — создать внешнюю библиотеку из файла .jar.

Если вы не хотите использовать внешние библиотеки, необходимо задать необходимые разрешения. Скрипты выполняются успешно, только если удостоверения процессов имеют доступ к вашему коду. Дополнительные сведения о настройке разрешений см. в руководстве по установке.

В Linux

Предоставьте пользователю разрешения на чтение и выполнение для пути mssql_satellite к классу.

В Windows

Предоставьте разрешения на чтение и выполнение для SQLRUserGroup и индикаторов безопасности всех пакетов приложений в папке, содержащей скомпилированный вами код Java.

Все дерево, от корневого родительского каталога до последней вложенной папки, должно иметь разрешения.

  1. Щелкните папку (например, C:\myJavaCode) правой кнопкой мыши и выберите Свойства>Безопасность.
  2. Выберите Изменить.
  3. Выберите Добавить.
  4. В разделе "Выбор пользователей", "Компьютер", "Учетные записи службы" или "Группы":
    1. Выберите типы объектов и убедитесь, что выбраны встроенные принципы безопасности и группы .
    2. Выберите расположения , чтобы выбрать имя локального компьютера в верхней части списка.
  5. Введите SQLRUserGroup, проверка имя и нажмите кнопку "ОК", чтобы добавить группу.
  6. Введите ВСЕ ПАКЕТЫ ПРИЛОЖЕНИй, проверка имя и нажмите кнопку "ОК", чтобы добавить. Если имя разрешить не удается, верните к шагу "Расположения". Идентификатор безопасности является локальным для вашего компьютера.

Убедитесь, что оба удостоверения безопасности имеют разрешения на чтение и выполнение в папке и вложенной папке pkg .

Вызов класса Java

Создайте хранимую процедуру, которая вызывает sp_execute_external_script для вызова кода Java из SQL Server. В параметре определите script , какой package.class вызов требуется вызвать. В следующем коде класс принадлежит пакету, вызываемому pkg и файлу RegexSample.javaкласса.

Примечание.

Код не определяет, какой метод следует вызывать. По умолчанию execute метод будет вызываться. В связи с этим, чтобы иметь возможность вызывать класс из SQL Server, вам нужно следовать интерфейсу SDK и реализовать метод execute в классе Java.

Хранимая процедура принимает входной запрос (входной набор данных) и регулярное выражение и возвращает строки, которые выполнили данное регулярное выражение. Он использует регулярное выражение[Jj]ava, которое проверка, если текст содержит слово Java или java.

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

Результаты

После выполнения вызова необходимо получить результирующий набор с двумя строками.

Снимок экрана: пример результатов из примера Java.

Если возникла ошибка

  • При компиляции классов вложенная pkg папка должна содержать скомпилированный код для всех трех классов.

  • Если вы не используете внешние библиотеки, проверка разрешения для каждой папки из rootpkg вложенной папки, чтобы убедиться, что удостоверения безопасности, выполняющие внешний процесс, имеют разрешение на чтение и выполнение кода.