目标听众
- 想要从 Azure Functions 连接到 Azure Database for MySQL 的用户
- 想用Java开发的人
- 那些想要在命令的基础上进行操作的人
概述
本文涵盖:
基本上,我们将在命令的基础上进行。
- 为 Azure Database for MySQL 灵活服务器创建资源
- 从 Azure Functions 连接到 MySQL
- [Bonus] 在 Azure Functions 中使用 API 密钥
前提
这是上一篇文章“使用 Azure Functions 创建 API - Java 版”的续篇。
请参阅文章了解如何构建和部署 Azure Functions 的开发环境。
<上一集>
- 为 Azure Functions (Java) 构建本地开发环境
- 创建一个返回随机虚拟 JSON 数据的 API
→[这次] 改为从 MySQL 获取此数据(添加注册/更新/删除) - 部署到 Azure
操作环境
工作环境如下。
- 操作系统
- Windows 11 专业版 (21H2)
- Ubuntu 20.04 (WLS)
- JDK
- Azure CLI 2.40.0
准备数据库
创建 Azure Database for MySQL 灵活服务器
创建 MySQL 服务器az mysql 灵活服务器创建使用命令
重击az mysql flexible-server create --name <MySQLサーバー名> --resource-group <リソースグループ名> --location eastus --admin-user <Adminユーザー名> --admin-password <Adminパスワード> --sku-name Standard_B1s --public-access 0.0.0.0 --version 8.0.21电源外壳
电源外壳az mysql flexible-server create ` --name <MySQLサーバー名> ` --resource-group <リソースグループ名> ` --location eastus ` --admin-user <Adminユーザー名> ` --admin-password <Adminパスワード> ` --sku-name Standard_B1s ` --public-access 0.0.0.0 ` --version 8.0.21可选补充
指定上述选项后,MySQL 服务器为公共访问在模式中创建。
即使它是公共的,也只有防火墙允许的 IP 地址才能连接。
如果您不配置防火墙,它将不接受来自任何 IP 的连接。
通过将--public-access设置为0.0.0.0Azure 中的资源防火墙配置为允许从公共访问 MySQL 服务器这次我就不多说了,不过VNet 集成通过使用该机制,似乎可以在 Azure Functions 和 MySQL 之间进行私有通信。
数据库创建
创建 MySQL 服务器时,会自动创建一个名为
flexibleserverdb的数据库,但我想添加一个新的。创建数据库az mysql 灵活服务器数据库创建使用命令
重击az mysql flexible-server db create --resource-group <リソースグループ名> --server-name <MySQLサーバー名> --charset utf8mb4 --collation utf8mb4_bin --database-name <データベース名>电源外壳
电源外壳az mysql flexible-server db create ` --resource-group <リソースグループ名> ` --server-name <MySQLサーバー名> ` --charset utf8mb4 ` --collation utf8mb4_bin ` --database-name <データベース名>字符代码(字符集)、排序规则等是您的偏好。
添加防火墙规则
接下来,我想在我创建的数据库中创建一个表,但是我无法从客户端访问 MySQL 服务器。
以上正如我在 中写的,您需要允许防火墙中的连接源的IP(全局IP)访问。添加防火墙规则az mysql 灵活服务器防火墙规则创建使用命令
重击az mysql flexible-server firewall-rule create --resource-group <リソースグループ名> --name <MySQLサーバー名> --rule-name <ファイアウォールルール名> --start-ip-address <クライアントのIPアドレス>电源外壳
电源外壳az mysql flexible-server firewall-rule create ` --resource-group <リソースグループ名> ` --name <MySQLサーバー名> ` --rule-name <ファイアウォールルール名> ` --start-ip-address <許可するIPアドレス>可选补充
您还可以使用
start-ip-address和end-ip-address指定范围。
如果只指定start-ip-address,则只针对指定的IP。要检查您自己的IP地址,请谷歌“IP地址确认”。
例如这里你可以检查它
(不要在这里使用命令!)创建表
您应该能够通过添加防火墙规则连接到 MySQL 服务器,因此创建一个表。
表创建az mysql 灵活服务器执行使用命令运行查询。
重击az mysql flexible-server execute --name <MySQLサーバー名> --admin-user <Adminユーザー名> --admin-password <Adminパスワード> --database-name <データベース名> --querytext " create table todo ( id int(8) auto_increment, content varchar(60), done bit(1) not null default b'0', created_at datetime not null default current_timestamp, primary key (id) ); "电源外壳
电源外壳az mysql flexible-server execute ` --name <MySQLサーバー名> ` --admin-user <Adminユーザー名> ` --admin-password <Adminパスワード> ` --database-name <データベース名> ` --querytext ` " create table todo ( id int(8) auto_increment, content varchar(60), done bit(1) not null default b'0', created_at datetime not null default current_timestamp, primary key (id) ); "Azure Functions 端的实现
既然详细执行的故事还在继续,麻烦的人设置环境变量跳到
我想在这里指出的是从环境变量中获取连接信息这就是部分。* 由于来源是摘录的,所以整个故事是Github请看
OR映射器介绍
我想介绍 OR 映射器,因为它很重要。
这次MyBatis使用。 (因为想自己写SQL,和代码分开……)
我将省略对 MyBatis 的解释,因为它不在主题范围内。
如果您有兴趣,请查看。如果您想知道选择哪个 OR 映射器,这张幻灯片将非常有帮助。 (点击展开)
Java OR 映射器选择点#jsug从仅正志添加依赖项
添加对
pom.xml的依赖以使用 MyBatis 和 MySQL 连接器。pom.xml(摘录)・・・ <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.11</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.30</version> </dependency> ・・・添加配置文件
添加 MyBatis 配置文件。
由于在源代码控制的源中包含数据库连接信息对安全性不利,因此它是可变的 (${xxx})。
这个变量包含环境变量输入值。mybatis-config.xml<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <typeAliases> <package name="com.azure_functions.models"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <!-- ↓ポイント↓ --> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <!-- ↑ポイント↑ --> </dataSource> </environment> </environments> <mappers> <package name="com.azure_functions.mappers"/> </mappers> </configuration>添加 SQL 定义
定义实际发出的 SQL 语句。
TodoItemMapper.xml(摘录)<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.azure_functions.mappers.TodoItemMapper"> <!-- TODO一覧取得 --> <select id="selectTodo" resultType="TodoItem"> select id , content , done , created_at from todo </select> ・・・ </mapper>添加了连接 SQL 和 Java 的接口
添加一个接口以从 Java 调用 SQL。
方法名称和SQL 定义id的关联。TodoItemMapper.java(摘录)/** * TodoItemのマッパー */ public interface TodoItemMapper { /** * TODO一覧を全件取得 * * @return TODO一覧 */ List<TodoItem> selectTodo(); ・・・ }围绕数据库连接添加代码
从环境变量中获取连接信息,设置文件它设置在 中可变的部分。
Sql Touching Onma Throw r。爪哇@RequiredArgsConstructor public class SqlSessionManager { /** SQLセッションファクトリー */ private static SqlSessionFactory sqlSessionFactory = sqlSessionFactory(); /** 実行コンテキスト */ private final ExecutionContext context; private static SqlSessionFactory sqlSessionFactory() { // ↓ポイント↓ // DB接続情報を環境変数から取得 Properties mybatisProps = new Properties(); mybatisProps.put("url", System.getenv("MYSQL_URL")); mybatisProps.put("username", System.getenv("MYSQL_USER")); mybatisProps.put("password", System.getenv("MYSQL_PASSWORD")); // ↑ポイント↑ try (Reader config = Resources.getResourceAsReader("mybatis-config.xml")) { SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(config, mybatisProps); return sqlSessionFactory; } catch (IOException e) { throw new RuntimeException(e); } } ・・・ }模型的修改
根据创建的表的列更改模型。
达到米。爪哇@Data @Builder @JsonDeserialize(builder = TodoItem.TodoItemBuilder.class) public class TodoItem { // ↓追加↓ /** ID */ private int id; // ↑追加↑ /** 内容 */ private String content; /** 完了フラグ */ private boolean done; /** 作成日時 */ private LocalDateTime createdAt; }添加端点
上次只有一个端点用于获取 TODO 列表,但现在我们将添加端点用于注册、更新和删除。
添加 HTTP 触发器
使用
@HttpTrigger创建一个方法。
它还支持使用 API 密钥,上次不支持。
(只需将authLevel更改为AuthorizationLevel.FUNCTION!)Function.java(摘录)public class Function { /** * TODO一覧を取得 * * @param request リクエスト * @param context 実行コンテキスト * @return レスポンス */ @FunctionName("fetchTodoItems") public HttpResponseMessage fetchTodoList( @HttpTrigger( name = "req", methods = { HttpMethod.GET }, // 承認レベルをFUNCTIONに変更(APIキーが必要となる) authLevel = AuthorizationLevel.FUNCTION, // エンドポイントを「/api/todo/list」に設定 route = "todo/list" ) HttpRequestMessage<Optional<String>> request, final ExecutionContext context ) { ・・・ } /** * TODOを登録 * * @param request リクエスト * @param context 実行コンテキスト * @return レスポンス */ @FunctionName("createTodoItem") public HttpResponseMessage createTodoItem( @HttpTrigger( name = "req", methods = { HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION, // エンドポイントを「/api/todo/create」に設定 route = "todo/create" ) HttpRequestMessage<Optional<String>> request, final ExecutionContext context ) { ・・・ } /** * TODOを更新 * * @param request リクエスト * @param context 実行コンテキスト * @return レスポンス */ @FunctionName("updateTodoItem") public HttpResponseMessage updateTodoItem( @HttpTrigger( name = "req", methods = { HttpMethod.PUT }, authLevel = AuthorizationLevel.FUNCTION, // エンドポイントを「/api/todo/<id>/update」に設定 route = "todo/{id:int}/update" ) HttpRequestMessage<Optional<String>> request, final ExecutionContext context, // パスのidをバインド @BindingName("id") int id ) { ・・・ } /** * TODOを削除 * * @param request リクエスト * @param context 実行コンテキスト * @return レスポンス */ @FunctionName("deleteTodoItem") public HttpResponseMessage deleteTodoItem( @HttpTrigger( name = "req", methods = { HttpMethod.DELETE }, authLevel = AuthorizationLevel.FUNCTION, // エンドポイントを「/api/todo/<id>/delete」に設定 route = "todo/{id:int}/delete" ) HttpRequestMessage<Optional<String>> request, final ExecutionContext context, // パスのidをバインド @BindingName("id") int id ) { ・・・ } ・・・ }HTTP 触发器设置(摘要)
函数名 HTTP方法 终点 审批级别 过程 fetchTodoItems 得到 /api/todo/list 功能 获取列表 创建待办事项 邮政 /api/todo/create 功能 登记 更新待办事项 放 /api/todo/{id}/update 功能 更新 删除待办事项 删除 /api/todo/{id}/delete 功能 删除 添加了将请求正文 (JSON) 转换为对象的反序列化过程
上次我们实现了对象→JSON转换的序列化处理,但这次我们将实现JSON→对象转换的反序列化处理。
J孙宇智l.爪哇/** * JSON関連のユーティリティ */ @RequiredArgsConstructor public class JsonUtil { /** JSONシリアライズ用のマッパー */ private static final ObjectMapper MAPPER = objectMapper(); /** 実行コンテキスト */ private final ExecutionContext context; private static ObjectMapper objectMapper() { ・・・ // ↓追加↓ // LocalDateTime用のデシリアライザを追加 javaTimeModule.addDeserializer( LocalDateTime.class, new LocalDateTimeDeserializer( DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss") ) ); // ↑追加↑ ・・・ return mapper; } ・・・ // ↓追加↓ /** * JSONからオブジェクトに変換 * * @param <T> オブジェクトの型 * @param json JSON文字列 * @param type オブジェクトの型 * @return オブジェクト */ public <T> T deserialize(String json, Class<T> type) { try { return MAPPER.readValue(json, type); } catch (JsonProcessingException e) { context .getLogger() .severe( "JSON→オブジェクトの変換に失敗しました。エラーメッセージ:" + e.getMessage() ); throw new IllegalArgumentException(e); } } // ↑追加↑ }更改数据操作逻辑
更改上次生成虚拟数据的部分以从数据库中获取它。
此外,添加这次添加的注册、更新和删除逻辑。TodoService.java(摘录)/** * TODOサービス */ @RequiredArgsConstructor public class TodoService { /** 実行コンテキスト */ private final ExecutionContext context; /** SQLセッション管理 */ private final SqlSessionManager sqlSessionManager; /** * TODO一覧を取得 * * @return TODO一覧 */ public List<TodoItem> fetchTodoItems() { context.getLogger().info("fetchTodoItemsが呼び出されました。"); // ↓DBからデータ取得するよう変更↓ // DBからTODO一覧取得 List<TodoItem> todoItems = sqlSessionManager.transaction( sqlSession -> { TodoItemMapper todoItemMapper = sqlSession.getMapper( TodoItemMapper.class ); return todoItemMapper.selectTodo(); } ); // ↑DBからデータ取得するよう変更↑ return todoItems; } /** * TODOを登録 * @param todo TODOインスタンス * @return 登録件数 */ public int insertTodo(TodoItem todo) { ・・・ } /** * TODOを更新 * @param todo TODOインスタンス * @return 更新件数 */ public int updateTodo(int id, TodoItem todo) { ・・・ } /** * TODOを削除 * @param id TODOのID * @return 削除件数 */ public int deleteTodo(int id) { ・・・ } }设置环境变量
设置环境变量的方法在本地执行和 Azure 之间有所不同。
本地执行的环境变量
将设置添加到创建Maven项目时创建的
local.settings.json中的Values。
这将在运行时读取配置并设置环境变量。local.settings.json{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "java", // ↓追加↓ "MYSQL_URL": "jdbc:mysql://<MySQLサーバー名>.mysql.database.azure.com:3306/<データベース名>?useSSL=true", "MYSQL_USER": "<Adminユーザー>", "MYSQL_PASSWORD": "<Adminパスワード>" // ↑追加↑ } }◎个人的绊脚石
local.settings.json是测试时未加载.
因此,在执行测试时需要提前在终端等中设置环境变量。mvn clean package如果用 构建,默认会执行测试,所以如果你想跳过它,指定选项如下
mvn clean package -DskipTests=true或者将设置添加到
pom.xml以更改默认行为。pom.xml<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> ・・・ <properties> ・・・ <!-- ↓追加↓ --> <skipTests>true</skipTests> <!-- ↑追加↑ --> </properties>Azure 上部署的函数应用的环境变量(应用程序设置)
为 Azure 上部署的函数应用添加环境变量(应用程序设置)
az functionapp config appsettings 设置使用命令重击az functionapp config appsettings set --resource-group <リソースグループ> --name <関数アプリ名> --settings MYSQL_URL=jdbc:mysql://<MySQLサーバー名>.mysql.database.azure.com:3306/<データベース名>?useSSL=true MYSQL_USER=<Adminユーザー> MYSQL_PASSWORD=<Adminパスワード>电源外壳
电源外壳az functionapp config appsettings set ` --resource-group <リソースグループ> ` --name <関数アプリ名> ` --settings ` MYSQL_URL=jdbc:mysql://<MySQLサーバー名>.mysql.database.azure.com:3306/<データベース名>?useSSL=true ` MYSQL_USER=<Adminユーザー> ` MYSQL_PASSWORD=<Adminパスワード>这里,我在应用设置中记下了设置值,密钥库通过使用此服务,还可以集中管理秘密并与多个应用程序共享。
下面的文章很有帮助。
[Azure] 参考 Azure Functions 中的 Azure Key Vault 值 - Qiita最终文件夹结构
azure-functions ├── README.md ├── host.json ├── local.settings.json -------------------------------- 変更 ├── pom.xml -------------------------------------------- 変更 └── src ├── main │ ├── java │ │ └── com │ │ └── azure_functions │ │ ├── Function.java ------------------ 変更 │ │ ├── db │ │ │ └── SqlSessionManager.java ----- 追加 │ │ ├── mappers │ │ │ └── TodoItemMapper.java -------- 追加 │ │ ├── models │ │ │ └── TodoItem.java -------------- 変更 │ │ ├── services │ │ │ └── TodoService.java ----------- 変更 │ │ └── utils │ │ └── JsonUtil.java -------------- 変更 │ └── resources │ ├── com │ │ └── azure_functions │ │ └── mappers │ │ └── TodoItemMapper.xml --------- 追加 │ └── mybatis-config.xml --------------------- 追加 └── test部署
将应用部署到 Azure。
部署过程和上次一样,我就省略了。[部署后]
操作检查
检查 API 密钥
由于这次我们在函数中设置了 API 密钥,所以我们需要将 API 密钥指定为参数。
提前输入命令进行确认。重击# 関数アプリのホストを取得 azFunctionHost=$(az functionapp show --resource-group <リソースグループ名> --name <関数アプリ名> --query "defaultHostName" | sed 's/"//g') # 「"」を除去 # APIキーを取得 azFunctionKey=$(az functionapp keys list --resource-group <リソースグループ名> --name <関数アプリ名> --query "functionKeys.default" | sed 's/"//g') # 「"」を除去电源外壳
电源外壳# 関数アプリのホストを取得 $azFunctionHost = (az functionapp show ` --resource-group <リソースグループ名> ` --name <関数アプリ名> ` --query "defaultHostName").Trim('"') # 「"」を除去 # APIキーを取得 $azFunctionKey = (az functionapp keys list ` --resource-group <リソースグループ名> ` --name <関数アプリ名> ` --query "functionKeys.default").Trim('"') # 「"」を除去上述命令的输出结果为 JSON,但
--query选项可用于缩小、排序和格式化。--query选项包括JMES 路径您可以为调用的 JSON 指定查询。API 调用
在
code参数中指定您的 API 密钥。列表
curl -i -X GET https://${azFunctionHost}/api/todo/list?code=${azFunctionKey}登记
重击curl -i -X POST -d '{"content":"Azureの資格取得"}' https://${azFunctionHost}/api/todo/create?code=${azFunctionKey}电源外壳
电源外壳# 日本語が文字化けするので一旦データをファイルに吐いて、ファイル指定にする $tmpFile = New-TemporaryFile Write-Output '{"content":"Azureの資格取得"}' | Set-Content $tmpFile curl -i -X POST -d @$tmpFile https://${azFunctionHost}/api/todo/create?code=${azFunctionKey} Remove-Item $tmpFile更新
重击curl -i -X PUT -d '{"content":"Azureの資格取得(AZ-900)", "done":true}' https://${azFunctionHost}/api/todo/1/update?code=${azFunctionKey}电源外壳
电源外壳# 日本語が文字化けするので一旦データをファイルに吐いて、ファイル指定にする $tmpFile = New-TemporaryFile Write-Output '{"content":"Azureの資格取得(AZ-900)", "done":true}' | Set-Content $tmpFile curl -i -X PUT -d @$tmpFile https://${azFunctionHost}/api/todo/1/update?code=${azFunctionKey} Remove-Item $tmpFile删除
curl -i -X DELETE https://${azFunctionHost}/api/todo/1/delete?code=${azFunctionKey}概括
- 在本地连接到 MySQL 服务器时允许防火墙中的 IP
- 数据库连接信息取自环境变量
现在我们已经完成了简单的后端处理,接下来我们将可以从前端调用 API 并使其感觉像一个简单的无服务器应用程序。
参考
- Azure CLI 参考
- Java OR 映射器选择点
- [Azure] 从 Azure Functions 引用 Azure Key Vault 值 - Qiita
- JMES 路径
- 操作系统
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308629958.html