セージ の メモ書き

メモこそ命の恩人だ

IntelliJ - Azure Functions アプリ作成

準備

docs.microsoft.com

  • サイトにある前提条件をインストールする。
  • Azure CLI も必要
  • Maven は不要(このブログでは Gradle を使用する)

OpenJDK 11

Azure CLI

Azure Functions Core Tools

IntelliJ

Gradle

  • プロキシの設定
  • 以下にファイルを作成
systemProp.http.proxyHost=プロキシのIPアドレス
systemProp.http.proxyPort=プロキシのポート番号
systemProp.https.proxyHost=プロキシのIPアドレス
systemProp.https.proxyPort=プロキシのポート番号


ローカルプロジェクトの作成

新規プロジェクト

  • IntelliJ → ファイル → 新規プロジェクト → Azure Functions
  • 画面1
    • プロジェクトSDK:11 version 11.0.12
    • HttpTrigger
  • 画面2
    • Tool:Gradle
    • 残りは任意に変更
  • 画面3
    • 任意に変更
  • しばらく待機
    • 作成後、インポートが走る
    • IntelliJ 右下に進捗が表示される
    • 初回は時間かかる。10分ほど待機。

host.json 修正

  • Azure Functions Core Tools v4 を使用する場合、修正が必要(2022/8時点)
  • 未修正の場合、実行時に以下のエラーが発生する。
    • Value cannot be null. (Parameter 'provider')
  • host.json の extensionBundle を削除する。
    • おそらくバージョン情報が邪魔してる。
{
  "version": "2.0"
}

デバッグ実行

デプロイ

  • Azure Explorer より、サインイン状態であることを確認。
  • プロジェクト名を右クリック → Azure → Deploy to Azure Functions
  • "実行" 操作後、30秒ほどでデプロイ完了。
  • 完了後、URL が表示される。以下、例


Azure SQL Database に接続

docs.microsoft.com

  • Azure Functions から Azure SQL Database へ接続する。
  • JDBC ドライバーにより接続できる。

SQL Database ファイアウォール

  • ファイアウォールを解除する。
    • Azure SQL Database → 使用を開始する → Azure の構成
    • "Azure サービスおよびリソースにこのサーバーへのアクセスを許可する" を有効。
  • これで、Azure Functions よりアクセスできる。
    • ※ Azure サービスからアクセスし放題なので注意。
    • ※ 未使用時は、チェックなしにしておく。

JDBC ドライバーの配置

Build.gradle 修正

dependencies {
    implementation 'com.microsoft.azure.functions:azure-functions-java-library:1.4.2'
    // ↓↓↓
    implementation 'com.microsoft.sqlserver:mssql-jdbc:10.2.1.jre11'
    // ↑↑↑
    testImplementation 'org.mockito:mockito-core:2.23.4'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2'
}

ソース修正

Step 3: Connecting to SQL using Java - JDBC Driver for SQL Server | Microsoft Learn

DriverManager (Java Platform SE 8)

  • DriverManager クラスを使用して接続する。
  • JDBC ドライバーの管理用クラス。
  • URL は Azure SQL Database の Portal よりコピー。
    • Portal → 接続文字列 → JDBC
    • パスワードの部分を入力
import java.sql.*;
// お試し
String connectionUrl = "jdbc:sqlserver://xxx;";
String info;
try (Connection connection = DriverManager.getConnection(connectionUrl);) {
    info = "OK";
}
catch (SQLException e) {
    info = "NG" + e.toString();
}
return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body(info).build();
  • 繋がらない場合...

ローカルDB に接続(デバッグ用)

String connectionUrl = "jdbc:sqlserver://" +
        "localhost\\SAMPLE;database=SampleDb;user=sa;password=1234;" +
        "encrypt=false;trustServerCertificate=false;loginTimeout=30;";

CRUD 操作

ローカルに以下のDBを作成する。

USE SampleDb;
IF object_id(N'SampleTable1') is not null DROP TABLE SampleTable1;
CREATE TABLE SampleTable1 (Id INT PRIMARY KEY, Data1 VARBINARY (8000)); 
-- データ登録するなら以下を使う
--DECLARE @Count INT = 1;
--WHILE @Count <= 10 BEGIN
--    INSERT INTO SampleTable1 (Id, Data1) VALUES (@Count, 'xxx');
--    SET @Count += 1;
--END

以下の関数アプリで CRUD 操作する。

@FunctionName("HttpExample")
public HttpResponseMessage run(
        @HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS)
        HttpRequestMessage<Optional<String>> request, final ExecutionContext context) {
    context.getLogger().info("Java HTTP trigger processed a request.");
    final String maxTryString = request.getQueryParameters().get("maxTry");   // 試行回数
    final String typeString = request.getQueryParameters().get("type"); // CRUDタイプ
    final String sizeString = request.getQueryParameters().get("size"); // INSERT・UPDATEのデータサイズ

    final int maxTry = (maxTryString == null) ? 10 : Integer.valueOf(maxTryString);
    final int size = (sizeString == null) ? 1000 : Integer.valueOf(sizeString);

    String connectionUrl = "jdbc:sqlserver://" +
            "localhost\\SAMPLE;database=SampleDb;user=sa;password=1234;" +
            "encrypt=false;trustServerCertificate=false;loginTimeout=30;";

    Connection con = null;
    PreparedStatement ps = null;
    ResultSet rs = null;

    int maxCount = 100;
    int retCount = 0;
    String retInfo = "";
    long startTime;
    long endTime;

    try {
        con = DriverManager.getConnection(connectionUrl);
        if (typeString == null){
            // SELECT
            ps = con.prepareStatement("SELECT * FROM SampleTable1 WHERE Id = ?;");
            String tmpInfo = "";
            for (int tryCount = 1; tryCount <= maxTry; tryCount++){
                startTime = System.currentTimeMillis();
                for (int i = 1; i <= maxCount; i++){
                    ps.setInt(1, i);
                    rs = ps.executeQuery();
                    // ↑これでクエリ実行されること、SSMSにて確認済み
                    // while (rs.next()) retCount++;
                }
                endTime = System.currentTimeMillis();
                tmpInfo += "[" + tryCount + "] " + (endTime - startTime) + " ms, ";
            }
            retInfo = "SELECT, time:" + tmpInfo;
        }
        else{
            // INSERT・UPDATE・DELETE
            String binaryData = "0x";
            for (int i = 0; i < size; i++) binaryData += "00";
            switch (typeString){
                case "INSERT":
                    ps = con.prepareStatement("INSERT INTO SampleTable1 (Id, Data1) VALUES (?," + binaryData + ");");
                    startTime = System.currentTimeMillis();
                    for (int i = 1; i <= maxCount; i++){
                        ps.setInt(1, i);
                        retCount += ps.executeUpdate();
                    }
                    endTime = System.currentTimeMillis();
                    retInfo = typeString + ", count:" + retCount + ", time:" + (endTime - startTime) + " ms";
                    break;
                case "UPDATE":
                    ps = con.prepareStatement("UPDATE SampleTable1 SET Data1 = " + binaryData + " WHERE Id = ?;");
                    String tmpInfo = "";
                    for (int tryCount = 1; tryCount <= maxTry; tryCount++){
                        startTime = System.currentTimeMillis();
                        for (int i = 1; i <= maxCount; i++){
                            ps.setInt(1, i);
                            retCount += ps.executeUpdate();
                        }
                        endTime = System.currentTimeMillis();
                        tmpInfo += "[" + tryCount + "] " + (endTime - startTime) + " ms, ";
                    }
                    retInfo = "UPDATE, time:" + tmpInfo;
                    break;
                case "DELETE":
                    ps = con.prepareStatement("DELETE FROM SampleTable1 WHERE Id = ?;");
                    startTime = System.currentTimeMillis();
                    for (int i = 1; i <= maxCount; i++){
                        ps.setInt(1, i);
                        retCount += ps.executeUpdate();
                    }
                    endTime = System.currentTimeMillis();
                    retInfo = typeString + ", count:" + retCount + ", time:" + (endTime - startTime) + " ms";
                    break;
            }
        }
    }
    catch (Exception e) {
        retInfo = "NG" + e.toString();
        e.printStackTrace();
    }
    finally {
        try {
            if (rs != null) rs.close();
            if (ps != null) ps.close();
            if (con != null) con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body(retInfo).build();
}

以下の URL でアクセスする。
"size" と "type" で登録データや、CRUD のタイプを変更できる。

-- SELECT
http://localhost:***/api/HttpExample?size=1000
-- INSERT
http://localhost:***/api/HttpExample?size=1000&type=INSERT
-- UPDATE
http://localhost:***/api/HttpExample?size=1000&type=UPDATE
-- DELETE
http://localhost:***/api/HttpExample?size=1000&type=DELETE



以上