チュートリアル:Java で正規表現 (regex) を使用して文字列を検索する
適用対象: SQL Server 2019 (15.x) 以降のバージョン
このチュートリアルでは、SQL Server の言語拡張を使用し、SQL Server から 2 つの列 (ID とテキスト) と、正規表現の入力パラメーターを受け取る Java クラスを作成する方法について説明します。 このクラスでは、SQL Server に 2 つの列 (ID とテキスト) を返します。
Java クラスに送信されるテキスト列内の指定のテキストが、指定した正規表現を満たしているかどうかをコードが確認し、元の ID と共にそのテキストが返されます。
このサンプル コードでは、テキストに単語 Java
または java
が含まれているかどうかを確認する正規表現を使用しています。
前提条件
Windows または Linux 上の拡張性フレームワークと Java プログラミング拡張機能を備えた、SQL Server 2019 (15.x) 以降のバージョン上のデータベース エンジン インスタンス。 詳細については、「SQL Server 言語拡張機能」を参照してください。 コーディング要件の詳細については、「SQL Server 言語拡張機能で Java ランタイムを呼び出す方法」を参照してください。
T-SQL 実行用の SQL Server Management Studio または Azure Data Studio。
Windows または Linux 用の Java SE Development Kit (JDK) 8 または JRE 8。
Microsoft Extensibility SDK for Java for SQL Server の
mssql-java-lang-extension.jar
ファイル。
このチュートリアルでは、javac
を使用したコマンドライン コンパイルで十分です。
サンプル データの作成
まず、新しいデータベースを作成し、testdata
テーブルに ID
列と 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 をインポートします。つまり、手順 1 でダウンロードした jar ファイルをこのクラスで検出できるようにする必要があります。
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
ファイルにパッケージ化します。 (Eclipse や IntelliJ などの) ほとんどの Java IDE は、プロジェクトのビルドまたはコンパイル時の .jar
ファイルの生成をサポートしています。 .jar
ファイルに regex.jar
という名前を付けます。
Java IDE を使用していない場合は、.jar
ファイルを手動で作成できます。 詳細については、「クラス ファイルから Java .jar ファイルを作成する」を参照してください。
Note
このチュートリアルではパッケージを使用します。 クラスの先頭にある package pkg;
行により、コンパイルされたコードが pkg
というサブフォルダーに保存されます。 IDE を使用している場合、コンパイルしたコードはこのフォルダーに自動的に保存されます。 javac
を使用してクラスを手動でコンパイルする場合は、コンパイルされたコードを pkg
フォルダーに配置する必要があります。
外部言語の作成
データベースには、外部言語を作成する必要があります。 外部言語とは、データベースで Java などの外部言語を使用する場合、使用する各データベースに作成する必要があるデータベース スコープのオブジェクトです。
Windows での外部言語の作成
Windows を使用している場合は、次の手順に従って Java の外部言語を作成します。
拡張機能を含む .zip ファイルを作成します。
Windows での SQL Server セットアップの一部として、Java 拡張機能
.zip
ファイルが次の場所:[SQL Server install path]\MSSQL\Binn\java-lang-extension.zip
にインストールされます。 この zip ファイルにはjavaextension.dll
が含まれています。.zip ファイルから外部言語 Java ファイルを作成する方法は次のとおりです。
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
に保存されます。
Linux で次の T-SQL ステートメントを実行し、外部言語の Java を作成します。
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
に特別なアクセス許可を設定する必要はありません。
このサンプルでは、2 つの外部ライブラリを作成します。 1 つは SDK 用で、もう 1 つは RegEx Java コード用です。
SDK jar ファイル
mssql-java-lang-extension.jar
は、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 リポジトリに関するページにあります。 詳細については、「Microsoft Extensibility SDK for Java for SQL Server」を参照してください。
SDK 用の外部ライブラリを作成します。
CREATE EXTERNAL LIBRARY sdk FROM (CONTENT = '<OS specific path from above>/mssql-java-lang-extension.jar') WITH (LANGUAGE = 'Java'); GO
RegEx コード用の外部ライブラリを作成します。
CREATE EXTERNAL LIBRARY regex FROM (CONTENT = '<path>/regex.jar') WITH (LANGUAGE = 'Java'); GO
アクセス許可を設定する
Note
前の手順で外部ライブラリを使用している場合は、この手順を省略してください。 外部ライブラリは、ご自分の .jar
ファイルから作成することをお勧めします。
外部ライブラリを使用したくない場合は、必要な権限を設定する必要があります。 スクリプトの実行は、プロセス ID がお使いのコードにアクセスできる場合にのみ成功します。 アクセス許可の設定の詳細については、インストール ガイドを参照してください。
Linux の場合
クラスパスに対する読み取り/実行権限を mssql_satellite
ユーザーに付与します。
Windows の場合
コンパイルされたご自分の Java コードを含むフォルダー上で SQLRUserGroup と、All application packages SID に対して "読み取りと実行" アクセス許可を付与します。
アクセス許可は、ルートの親から最後のサブ フォルダーまで、ツリー全体で必要です。
- フォルダーを右クリックし (
C:\myJavaCode
など)、[プロパティ]>[セキュリティ] の順に選択します。 - [編集] を選択します。
- [追加] を選択します。
- [ユーザー、コンピューター、サービス アカウントまたはグループの選択] で次を実行します。
- [オブジェクト タイプ] を選択し、[組み込みセキュリティ原則] と [グループ] が選択されていることを確認します。
- [場所] を選択して、リストの先頭にあるローカル コンピューター名を選択します。
- 「SQLRUserGroup」と入力し、名前を確認し、[OK] をクリックしてグループを追加します。
- 「ALL APPLICATION PACKAGES」と入力し、名前を確認し、[OK] をクリックして追加します。 名前が解決されない場合は、[場所] の手順を再実行します。 SID は、お使いのコンピューターに対してローカルです。
両方のセキュリティ ID に、フォルダーと pkg
サブフォルダーに対する読み取り権限と実行権限があることを確認してください。
Java クラスの呼び出し
SQL Server から Java コードを呼び出す、sp_execute_external_script
を呼び出すストアド プロシージャを作成します。 script
パラメータで、どの package.class
を呼び出すかを定義します。 次のコードでは、クラスは pkg
というパッケージと RegexSample.java
というクラス ファイルに属しています。
Note
コードでは、どのメソッドを呼び出すかが定義されていません。 デフォルトでは、execute
メソッドが呼び出されます。 つまり、SQL Server からクラスを呼び出したい場合には、SDK インターフェイスに従い、メソッドをお使いの 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
結果
呼び出しを実行すると、2 つの行の結果セットが返されるはずです。
エラーが表示された場合
クラスをコンパイルするとき、
pkg
サブフォルダーには、3 つのクラスすべてのコンパイルされたコードが含まれている必要があります。外部ライブラリを使用していない場合は、
root
からpkg
サブ フォルダーまでの各フォルダーのアクセス許可を確認して、外部プロセスを実行しているセキュリティ ID にコードの読み取りと実行のアクセス許可があることを確認します。