构建新应用程序时,在开始实施之前计划数据模型非常重要。这种方法可确保为您提供一个坚实的基础以构建您的应用程序。在本模块中,您将了解应用程序中的主要实体,计划数据模型并准备数据库。

计划数据模型的一种常见方法是创建一个实体关系图 (ERD)。ERD 显示应用程序中的不同实体及其属性。它还显示了实体之间的相互关系。

在简单的应用程序中,您有两个实体:用户项目用户表示游戏中的人类用户,其属性包括用户名身高体重

项目表示游戏中的可获取项目。可以是武器、装甲、服装、药剂或各种其他类型的物体。一个项目具有类型重量属性,以及特定于类型的属性,如武器的攻击点或装甲的防御点。

您可以在 ERD 中表示您的两个实体,如下所示。

inv-sys-games-erd

您可以看到,您同时拥有以各种属性表示的用户实体和项目实体。此外,这两个实体通过一条线连接。此线表示两个实体之间的关系。每个项目归一个且只有一个用户所有,而一个用户可以拥有多个项目。因此,用户项目之间存在一对多关系。

在以下步骤中,您将 ERD 转换为数据库代码。首先,创建与 ERD 匹配的表和关系。然后,将一些示例数据加载到数据库中。最后,您在数据库上运行一些查询以处理一些用例。

完成模块所需时间:20 分钟


  • 第 1 步:创建数据库表

    首先,创建两个数据库表:用户项目。在 scripts/ 目录中,有一个名为 createTables.js 的文件。该文件的内容如下:

    const AWS = require('aws-sdk')
    
    const rdsdataservice = new AWS.RDSDataService();
    
    const params = {
      resourceArn: process.env.DATABASE_ARN,
      secretArn: process.env.SECRET_ARN,
      sql: `CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    height INTEGER NOT NULL,
    weight INTEGER NOT NULL
    );
    
    CREATE TABLE items (
    item_id SERIAL PRIMARY KEY,
    owner_id INTEGER REFERENCES users(user_id),
    type VARCHAR(20) NOT NULL,
    properties VARCHAR(256)
    );`
    }
    
    rdsdataservice.executeStatement(params, function(err, data) {
      if (err) {
        console.log(err, err.stack)
      } else {
        console.log(‘Tables created successfully!’)
      }
    })
    

    此脚本类似于您在上一个模块中运行的 testDatabase.js 脚本。在此脚本中,SQL 包含用于创建两个表的数据定义语言 (DDL) 语句。每个表都有一个自动递增整数的主键。项目表还有一个 owner_id 属性,该属性引用用户表中的 user_id

    请注意,您的项目表上有一个属性列,其中包含有关项目属性的任意数据。您不会直接查询此数据,但它可以包括有关该项目特定于类型的信息,例如武器的攻击点数量或装甲的防御点数量。

    执行脚本并使用以下命令创建表:

    node scripts/createTables.js

    您应该能在终端中看到以下输出:

    Tables created successfully!
  • 第 2 步:加载样本数据

    现在您已经创建了表,可加载一些示例数据。

    在 scripts/ 目录中,有两个具有 JSON 数据的文件:users.jsonitems.json。它们包含要插入到表中的随机数据。

    查看 scripts/insertUsers.js 中的文件。它包含以下代码:

    const AWS = require('aws-sdk')
    
    const rdsdataservice = new AWS.RDSDataService();
    const fs = require('fs');
    
    const raw = fs.readFileSync('users.json');
    const users = JSON.parse(raw)
    const values = users.map((user) => { return `('${user.username}', ${user.height}, ${user.weight})`}).join(',\n')
    const sql = `INSERT INTO users (username, height, weight) VALUES ${values}`
    
    const params = {
      resourceArn: process.env.DATABASE_ARN,
      secretArn: process.env.SECRET_ARN,
      sql
    }
    
    rdsdataservice.executeStatement(params, function(err, data) {
      if (err) {
        console.log(err, err.stack)
      } else {
        console.log('Users inserted successfully!')
      }
    })
    

    createTables.js 脚本一样,它使用 RDSDataService 客户端访问 Data API。在此脚本中,您正在从 users.json 文件中读取假冒用户,然后在 SQL 中编写 INSERT 语句,将用户插入表中。

    您可以使用以下命令运行脚本: 

    node scripts/insertUsers.js

    您应该能在终端中看到以下输出:

    Users inserted successfully!

    scripts/ 目录中的 insertItems.js 脚本十分类似,因为它会将您的假项目加载到表中。

    您可以使用以下命令运行此脚本:

    node scripts/insertItems.js

    您应该能在终端中看到以下输出:

    Items inserted successfully!

    您现在已将 50 个用户和 106 个项目加载到表中。在下一步,您将了解如何使用 Data API 处理其中一种常见访问模式。

  • 第 3 步:测试数据访问

    加载数据后,您可以使用 Data API 执行比上一个模块中执行的 Select 1 查询更复杂的操作。

    常见的访问模式是获取用户。您可以在此处尝试。

    查看 scripts/fetchUser.js 中的代码。此代码包含应用程序调用以获取用户的内部方法。代码如下所示:

    const AWS = require('aws-sdk')
    
    const rdsdataservice = new AWS.RDSDataService();
    
    const fetchUser = async (userId) => {
      const params = {
        resourceArn: process.env.DATABASE_ARN,
        secretArn: process.env.SECRET_ARN,
        includeResultMetadata: true,
        sql: 'SELECT user_id, username, height, weight from users where user_id = :user_id',
        parameters: [
          {
            name: 'user_id',
            value: { longValue: userId }
          }
        ]
      }
      const results = await rdsdataservice.executeStatement(params).promise()
      return results
    }
    
    fetchUser(22).then((results) => console.log(JSON.stringify(results, null, 2)))

    您的 fetchUser 函数采用一个参数 -- 要获取的用户的用户 ID。然后,它使用 Data API 进行查询以获取您的用户。

    文件底部是一个使用 fetchUser 函数的示例,方法是使用用户 userId 22的用户调用该函数。

    通过在终端中运行以下命令来执行此脚本:

    node scripts/fetchUser.js

    您应该能在终端中看到以下输出:

    {
      "columnMetadata": [
        {
          "arrayBaseColumnType": 0,
          "isAutoIncrement": true,
          "isCaseSensitive": false,
          "isCurrency": false,
          "isSigned": true,
          "label": "user_id",
          "name": "user_id",
          "nullable": 0,
          "precision": 10,
          "scale": 0,
          "schemaName": "",
          "tableName": "users",
          "type": 4,
          "typeName": "serial"
        },
        {
          "arrayBaseColumnType": 0,
          "isAutoIncrement": false,
          "isCaseSensitive": true,
          "isCurrency": false,
          "isSigned": false,
          "label": "username",
          "name": "username",
          "nullable": 0,
          "precision": 50,
          "scale": 0,
          "schemaName": "",
          "tableName": "users",
          "type": 12,
          "typeName": "varchar"
        },
        {
          "arrayBaseColumnType": 0,
          "isAutoIncrement": false,
          "isCaseSensitive": false,
          "isCurrency": false,
          "isSigned": true,
          "label": "height",
          "name": "height",
          "nullable": 0,
          "precision": 10,
          "scale": 0,
          "schemaName": "",
          "tableName": "users",
          "type": 4,
          "typeName": "int4"
        },
        {
          "arrayBaseColumnType": 0,
          "isAutoIncrement": false,
          "isCaseSensitive": false,
          "isCurrency": false,
          "isSigned": true,
          "label": "weight",
          "name": "weight",
          "nullable": 0,
          "precision": 10,
          "scale": 0,
          "schemaName": "",
          "tableName": "users",
          "type": 4,
          "typeName": "int4"
        }
      ],
      "numberOfRecordsUpdated": 0,
      "records": [
        [
          {
            "longValue": 22
          },
          {
            "stringValue": "tonya13"
          },
          {
            "longValue": 83
          },
          {
            "longValue": 208
          }
        ]
      ]
    }

    此输出非常详细。Data API 包含有关结果的大量信息,包括返回的每列的详细列元数据。

    在每个数据访问方法中,此信息可能难以解析。在下一步中,您将使用实用方法来打包 Data API。

  • 第 4 步:解析 Data API 响应

    在上一步中,您看到了获取单个用户的示例方法,以及来自 Data API 的详细响应。在此步骤中,您将学习如何解析该响应。

    scripts/ 目录中,查看 fetchUser2.js 文件。此文件的内容如下:

    const AWS = require('aws-sdk')
    
    const rdsdataservice = new AWS.RDSDataService();
    
    const parseRecords = (records, columnMetadata) => {
      // Format the results into key-value pairs with the column name and value
      const parsed = records.map((result) => {
        const obj = {}
        result.forEach((elem, idx) => {
          const columnName = columnMetadata[idx].name
          const [ columnValue, ]= Object.values(elem)
          obj[columnName] = columnValue
        })
        return obj
      })
      return parsed
    
    }
    
    const executeReadSql = async (sql, parameters) => {
      const params = {
        resourceArn: process.env.DATABASE_ARN,
        secretArn: process.env.SECRET_ARN,
        includeResultMetadata: true,
        sql
      }
      if (parameters) {
        params.parameters = parameters
      }
      const rawResults = await rdsdataservice.executeStatement(params).promise()
      let results = []
      if (rawResults.records) {
        results = parseRecords(rawResults.records, rawResults.columnMetadata)
      }
      return results
    }
    
    const fetchUser = async (userId) => {
      parameters = [
        {
          name: 'user_id',
          value: { longValue: userId }
        }
      ]
      sql = `SELECT user_id, username, height, weight from users where user_id = :user_id`
      const result = await executeReadSql(sql, parameters)
      return result[0]
    }
    
    fetchUser(22).then((result) => console.log(JSON.stringify(result, null, 2)))

    在此脚本中,有两个帮助程序函数:executeReadSqlparseRecordsexecuteReadSql 函数简化了调用 Data API 的一些样板文件。它处理外部参数,如 Database ARN 和 Secret ARN,以便您只能专注于数据访问函数中的 SQL 和参数。

    parseRecords 函数帮助将返回的行更改为具有键值对的 Javascript 对象。这种结果在您的应用程序中更容易处理。

    尝试运行脚本以获取单个用户。在终端中执行以下命令:

    node scripts/fetchUser2.js

    您应该能看到以下输出:

    {
      "user_id": 22,
      "username": "tonya13",
      "height": 83,
      "weight": 208
    }
    

    与初始结果相比,此输出更易于在应用程序中读取和使用。

    您可以在应用程序中的数据访问功能中使用这些实用功能。


    在本模块中,您使用实体关系图 (ERD) 设计了数据模型。然后,您使用 Amazon Aurora Serverless 数据库中的 Data API 创建表。接下来,您将一些示例数据加载到新表中。最后,您还学习了一些使用 Data API 的示例。您已经了解了一些实用函数在使用 Data API 时有何帮助。

    在下一个模块中,您将学习使用 Amazon Cognito 为应用程序配置身份验证。