Azure DB連携

Spring BootのDBアクセス処理を手動で定義しMyBatisを利用してみた

Spring Bootフレームワークを利用したアプリケーションでMyBatisを利用する際、DBにアクセスするDataSource等のBean定義を自動的に行うことができるが、この機能を利用せず、手動でDBアクセス処理を定義しMyBatisを利用することもできる。

今回は、手動でDBアクセス処理を定義しMyBatisを利用してみたので、共有する。

前提条件

下記記事の実装が完了していること。

Always Encryptedで暗号化されたカラムを更新してみたJDBC接続文字列に「columnEncryptionSetting=Enabled」を追加し、Azure Key Vault に対する...

作成したサンプルプログラム(Azure Functions側)の内容

作成したサンプルプログラム(Azure Functions側)の構成は以下の通り。なお、App Services側のソースコードは修正していない。
サンプルプログラムの構成
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。

<2021年4月13日 追記>
spring-cloud-function-dependenciesのバージョンは、2021年3月16日にリリースしたバージョン3.1.2を利用すると、1つのAzure Functions内に複数のファンクションを含む場合の不具合が解消できている。


その場合、Handlerクラスの継承するクラスを「AzureSpringBootRequestHandler」クラスから「FunctionInvoker」クラスに変更する。


spring-cloud-function-dependenciesの3.1.2を利用した実装サンプルは、以下の記事を参照のこと。

spring-cloud-function-dependenciesのバージョンを最新(3.1.2)にしてみたこれまでこのブログで取り上げてきたAzure Functionsのサンプルプログラムでは、spring-cloud-function-d...

DB接続定義は、以下のXMLで定義している。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="sqldb">
    <!-- 接続先DB毎にenvironment要素を記載する -->
    <environment id="sqldb">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
        <property name="url" value="jdbc:sqlserver://azure-db-purinit.database.windows.net:1433;database=azureSqlDatabase;columnEncryptionSetting=Enabled"/>
        <property name="username" value="purinit@azure-db-purinit"/>
        <property name="password" value="(DBのパスワード)"/>
      </dataSource>
    </environment>
  </environments>
  <!-- 参照するMapperのXMLファイル名を記載する -->
  <mappers>
    <mapper resource="com/example/mybatis/UserPassMapper.xml" />
  </mappers>
</configuration>

また、application.propertiesのDB接続設定は不要になるため、削除している。

# Key Vaultへの設定
keyVaultClientId=cdbb5434-e455-4885-b2e6-911f78c4264b
keyVaultClientKey=(サービスプリンシパルのパスワード)

さらに、DB接続定義(mybatis-config.xml)を読み込み、SqlSessionファクトリを生成し返却する処理を、以下のユーティリティクラスで定義している。

package com.example.util;

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisUtil {

    /** SqlSessionファクトリ */
    private static SqlSessionFactory sqlSessionFactory = null;
    
    static {
        try {
            // MyBatisの定義ファイルを読み込む
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            
            // SqlSessionファクトリを生成する
            // 第2引数には、MyBatisの定義ファイルの接続するDBを識別するidを指定する
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, "sqldb");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * SqlSessionファクトリを返却する
     * @return SqlSessionファクトリ
     */
    public static SqlSessionFactory getSqlSessionFactory(){
        return sqlSessionFactory;
    }
}



また、Mapperインタフェースはクラスに変更し、@Repositoryアノテーションを付与し、各メソッド内でDBセッションの接続/切断する処理を追加している。

package com.example.mybatis;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import com.example.mybatis.model.UserPass;
import com.example.util.MyBatisUtil;

@Repository
public class UserPassMapper {

    /**
     * USER_PASSテーブルからデータを1件取得する.
     * @return USER_PASSテーブルからの取得結果
     */
    public UserPass selectOne() {
        SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession();
        UserPass userPass = (UserPass)session.selectOne("selectOne");
        session.commit();
        session.close();
        return userPass;
    }
    
    /**
     * USER_PASSテーブルのデータを更新する.
     * @param userPass 更新対象のUSER_PASSテーブルの値
     */
    public void updateUserPass(UserPass userPass) {
        SqlSession session = MyBatisUtil.getSqlSessionFactory().openSession();
        session.update("updateUserPass", userPass);
        session.commit();
        session.close();
    }
    
}

さらに、Azure Functionsのメインクラスは以下の通りで、@EnableAutoConfigurationアノテーションを付与し、Spring Bootのデータソース自動設定機能を無効にしている。

package com.example;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Bean;

import com.example.model.GetUserPassParam;
import com.example.model.GetUserPassResult;
import com.example.model.UpdUserPassParam;
import com.example.model.UpdUserPassResult;
import com.example.service.GetUserPassService;
import com.example.service.UpdUserPassService;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionAzureKeyVaultProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider;
import com.microsoft.sqlserver.jdbc.SQLServerConnection;

// DB接続を手動管理にするため、Spring Bootのデータソース自動設定機能を無効にする
@SpringBootApplication
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
public class DemoAzureFunction {

    /** keyVaultのクライアントID */
    @Value("${keyVaultClientId}")
    private String keyVaultClientId;

    /** keyVaultのクライアントキー */
    @Value("${keyVaultClientKey}")
    private String keyVaultClientKey;
    
    /** Key Vaultの認証の接続設定が終わっているかどうかを判定するフラグ */
    private static boolean keyVaultProviderFlg = false;

    /** USER_PASSデータ取得サービスクラスのオブジェクト */
    @Autowired
    private GetUserPassService getUserPassService;
    
    /** USER_PASSデータ更新サービスクラスのオブジェクト */
    @Autowired
    private UpdUserPassService updUserPassService;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(DemoAzureFunction.class, args);
    }

    /**
     * Key Vaultの認証の接続設定を登録する.
     * @throws SQLException SQL例外
     */
    @PostConstruct
    public void postConstruct() throws SQLException {
       if (!keyVaultProviderFlg) {
           SQLServerColumnEncryptionAzureKeyVaultProvider akvProvider 
               = new SQLServerColumnEncryptionAzureKeyVaultProvider(
                    keyVaultClientId, keyVaultClientKey);
           Map<String, SQLServerColumnEncryptionKeyStoreProvider> keyStoreMap 
               = new HashMap<String, SQLServerColumnEncryptionKeyStoreProvider>();
           keyStoreMap.put(akvProvider.getName(), akvProvider);
           SQLServerConnection.registerColumnEncryptionKeyStoreProviders(keyStoreMap);
           keyVaultProviderFlg = true;
       }
    }

    /**
     * USER_PASSテーブルのデータを取得し結果を返却する関数
     * @return USER_PASSテーブルデータ取得サービスクラスの呼出結果
     */
    @Bean
    public Function<GetUserPassParam, GetUserPassResult> getUserPass() {
        return getUserPassParam -> getUserPassService.getUserPass(getUserPassParam);
    }
    
    /**
     * USER_PASSテーブルのデータを更新し結果を返却する関数
     * @return USER_PASSテーブルデータ更新サービスクラスの呼出結果
     */
    @Bean
    public Function<UpdUserPassParam, UpdUserPassResult> updUserPass(){
        return updUserPassParam -> updUserPassService.updUserPass(updUserPassParam);
    }
}

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/always-encrypted-db-manual/demoAzureFunc

サンプルプログラムの実行結果

サンプルプログラムの実行結果は、以下の記事の「サンプルプログラムの実行結果」と同じになる。

Always Encryptedで暗号化されたカラムを更新してみたJDBC接続文字列に「columnEncryptionSetting=Enabled」を追加し、Azure Key Vault に対する...

要点まとめ

  • Spring Bootフレームワークを利用したアプリケーションでMyBatisを利用する際、Spring Bootのデータソース自動設定機能を無効にして、手動でDBアクセス処理を定義しMyBatisを利用することもできる。