2. Node.js 应用:数据库

本文由清尘发表于2019-04-05 20:44最后修改于2019-04-10属于javascript分类

相关文章:

1. Node.js应用:MVC 框架 – Adonis
2. Node.js 应用:数据库
3. Node.js 应用:模型

=====================================

Tgit私有仓库: (database分支)

HTTP: https://git.code.tencent.com/testpro/shine-adonis.git
SSH:git@git.code.tencent.com:testpro/shine-adonis.git

升级项目

全局安装

npm install npm-check --global

执行命令,如果有升级的包,可以自己选择需要升级的包

npm-check --update

安装数据库引擎

adonis 框架支持很多种 sql 类型的数据库管理系统,比如 PostgreSQL,MySQL,MariaDB,Oracle ,SQLite 等等。你打算用哪个就需要在项目里安装对应的数据库引擎,然后需要再去配置一下 ..

安装sqlite3

npm install sqlite3 --save

安装mysql

npm install mysql --save

数据库引擎的配置

框架默认会使用 sqlite 这种类型的数据库 .. 先打开项目下面的 .env 这个文件 .. 注意这里有一个 DB_CONNECTION .. 它的值是 sqlite ..

在这里你可以配置项目要使用的数据库 .. 下面还有一些环境变量 .. 比如 DB_HOST,数据库主体 .. DB_PORT 数据库端口号,DB_USER .. 数据库用户 .. DB_PASSWORD,数据库密码 .. 还有一个
DB_DATABSE,使用的数据库的名字 ..

这些配置大部分会用在其它类型的数据库系统上,比如 mysql 或者 mariadb 等等 ..

sqlite 这种类型的数据库只会使用一个 DB_DATABASE,也就是数据库的名字 ..

数据库具体的配置是在 config .. database.js 这个文件里面 ..
connection .. 设置了项目要使用的数据库的类型 .. 这里用了一个 Env.get 得到环境变量里的 DB_CONNECTION 的值 .. 就是在 .env 那个文件里面设置的 .. 如果没找到,默认就会使用 sqlite
..

下面还有一些不同类型的数据库相关的配置 .. 比如我们在项目里如果使用 sqlite 数据库,相关的配置就是这里的 sqlite ..

在 connection 这里,设置了 sqlite 数据库文件的位置,filename,就是它的文件名 .. 这里读取了环境变量里的 DB_DATABASE 的值 .. 文件的扩展名是 .sqlite ..

默认情况下,你应该可以在项目的 database 这个目录下面,找到一个叫 adonis 的 sqlite 数据库文件 ..
这些配置的具体的值我们可以放在它放在 .env 这个文件里 ..

Migration:对数据库结构的修改

对应用需要的数据库结构的修改,我们是通过 Migration 完成的 .. 就是你需要修改一下应用的数据库,比如添加一个新的数据表,在数据表里添加一个字段。这些动作你需要把它们制作成 Migration .. 然后再去运行一下它们 ..

在创建项目的时候,默认已经给我创建了两个 migration .. 放在了 database .. migrations 这个目录的下面 ..

一般 migration 文件的名字的前面是一串随机的数字,然后是 migration 的名字 .. 你会看到,这里有个 user ,还有 token ..

Migration:运行与回滚

在项目的目录下执行一下

adonis migration:run

成功以后,会提示被运行的 migration 都有哪一些 ..

现在我们的项目使用的数据库是默认的 sqlite .. 在项目的 database 目录的下面,你会发现一个新的文件 .. adonis.sqlite .. 这个就是在运行 migration 以后,给我们生成的一个 sqlite 数据库文件 ..
使用一些工具,我们可以查看这个数据库文件里的东西 .. 这里我想用的是 DB Browser for SQLite ..
它是一个跨平台的软件,在 mac 上如果在系统上安装了 homebrew 可以使用它来安装这个工具

brew cask install db-browser-for-sqlite

完成以后在 launch pad 上面可以找到这个工具 ..
打开这个工具 .. 然后找到数据库文件所在的位置 .. 打开以后我们可以浏览一下这个数据库文件里的东西 ..

查看migration的状态

adonis migration:status

这里会显示已经运行的 migration 都有哪一些 .. 上面会显示 migration 的文件名,是否已经 migrated 了 .. 还有一个批次号 .. 就是这个 batch ..

回滚

adonis migration:rollback

如果想恢复做的 migration,可以执行一下 adonis migration:rollback .. 后面可以再加上一个 –batch 选项,然后指定某个具体的批次号 .. 如果不加,就会回滚最近做的一次 migration ..

完成以后再回到这个 SQLite 数据库浏览工具 .. 你会发现 .. tokens 还有 users 这两个表已经不见了 …

使用 MySQL / MariaDB 数据库管理系统

在mysql数据库里新建一个数据库 adonis

修改项目.env

......
DB_CONNECTION=mysql
......

下面这些 DB 开头的东西你需要根据自己的情况去修改一下

回到项目目录运行一下

adonis migration:run

可以看到mysql数据库里面新增加了tokens和users表

创建 Migration 添加新的数据表

执行

adonis make:migration post

选择Create table

提示创建了一个新的Migration 在 database/migrations/1554442006618_post_schema.js

在这个 migration 类的 up 方法里面,已经用了 table 的 increments 在 posts 这个表里添加了一个 id 字段,还有 timestamps .. 它会添加一个 created_at 还有
updated_at ..

adonis 框架里面的这个功能用的是 knex 提供的 .. 所以具体你可以怎么样去创建数据表的结构,你需要参考 knex 的创建 Schema 相关的文档 ..

修改刚刚新建的migration 增加一个字段

......
 up () {
    this.create('posts', (table) => {
      table.increments()
      table.string('title')
      table.timestamps()
    })
  }
......

查看一下状态

adonis migration:status

可以看到刚刚新建的还没有被执行,再执行一下

adonis migration:run

再打开数据库软件可以看到刚刚新建的posts表

创建 Migration 修改已有的数据表

执行

adonis make:migration posts_add_content_column

选择 Select table

修改刚刚生成的文件 database/migrations/1554444365448_posts_add_content_column_schema.js

'use strict'

/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')

class PostsAddContentColumnSchema extends Schema {
  up () {
    this.table('posts', (table) => {
      table.text('content','longtext')
    })
  }

  down () {
    this.table('posts', (table) => {
      table.dropColumn('content')
    })
  }
}

module.exports = PostsAddContentColumnSchema

执行

adonis migration:run

成功以后可以看到数据库里posts表新增加了content字段

Node.js 应用:查询构建器

控制器,路由,布局

创建一个控制器

adonis make:controller post --resource

选择 Http 这个 resource 选项可以让命令给我们创建一个资源类型的控制器 ..

在start/routes.js 里面增加一个posts路由

......
Route.resource('posts','PostController')
......

资源类型的控制器还有路由会按照一套标准定义 .. 比如资源如果是 posts ,这样用 GET 方法访问 posts 的时候,会用控制器里的 index 方法来处理,它应该显示一个资源的列表 .. 如果用 POST 方法访问 posts ,会用 store 方法来处理,这个方法可以把请求里发送过来的资源存储在数据库里 ..

新建一个视图 resources/views/layouts/main.edge

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  {{ style('https://unpkg.com/bootstrap@4.0.0/dist/css/bootstrap.min.css') }}
  {{ style('main') }}
  <title>shine</title>
</head>
<body>
  @!section('content')
  {{ script('https://unpkg.com/jquery@3.3.1/dist/jquery.js') }}
  {{ script('https://unpkg.com/bootstrap@4.0.0/dist/js/bootstrap.min.js') }}
  {{ script('main') }}
</body>
</html>

插入数据:准备添加创建资源需要的视图

准备一个创建资源用的表单 执行

adonis make:view post.create --layout layouts.main

找到resources/views/post/create.edge修改

@layout('layouts.main')

@section('content')
  <div class="container">
    <h3 class="mt-5 mb-4">Add New Post</h3>
    <form action="/posts" method="POST">
      <div class="form-group">
        <label for="title">Title</label>
        <input type="text" class="form-controll" placeholder="Enter title here" name="title">
      </div>
      <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" id="" cols="30" rows="10" class="form-control" placeholder="Write something amazing"></textarea>
      </div>
      {{ csrfField() }}
      <button class="btn btn-primary" type="submit">Submit</button>
    </form>
  </div>
@endsection

修改控制器文件 app/Controllers/Http/PostController.js 的create方法

......
async create ({ request, response, view }) {
    return view.render('post.create')
  }
......

用浏览器访问 http://localhost:3333/posts/create 可以看到视图创建的表单

插入数据:存储单个内容资源

修改控制器文件 app/Controllers/Http/PostController.js
引入 database ,修改 store 方法插入数据库

'use strict'

const Database = use('Database')
...... 
  async store ({ request, response }) {
    const newPost = request.only(['title','content'])
    const postID = await Database.insert(newPost).into('posts')
    console.log('postID:',postID)
  }
...... 

用浏览器访问 http://localhost:3333/posts/create 输入数据提交 在终端可以看到打印出来的ID
打开数据库客户端,可以看到刚刚新增的数据

读取数据:显示单个内容资源

处理单个资源的请求用的是控制器的 show 这个方法,在 PostController 这个控制器里,找到它里面的 show 这个方法 .. 它可以渲染一个显示资源用的视图 .. 还得在数据库里查询出需要访问的某个资源的数据 ..

修改控制器方法

......
 async show ({ params, request, response, view }) {
    const post = await Database
      .from('posts')
      .where('id',params.id)
      .first()
    return view.render('post.show',{post})
  }
......

创建视图 resources/views/post/show.edge 用来显示单个内容

@layout('layouts.main')

@section('content')
  <div class="container">
  <h1 class="mt-5 mb-4">{{ post.title }}</h1>
  <div>
    {{post.content}}
  </div>
  </div>
@endsection

用浏览器访问 http://localhost:3333/posts/1 可以看到内容

现在成功创建了内容以后,可以把用户重定向到新创建的内容页面上 ..

......
async store ({ request, response }) {
    const newPost = request.only(['title','content'])
    const postID = await Database.insert(newPost).into('posts')
    console.log('postID:',postID)
    return response.redirect(`/posts/${postID[0]}`)
  }
......

更新数据:修改单个内容资源

修改单个资源的内容会用到资源控制器里的 edit 方法,它需要给我们渲染一个修改资源需要用的表单 .. 提交这个表单以后,会用控制器的 update 方法来处理 .. 在这个方法里要把用户修改之后的资源内容存储在数据库里 ..

......
  async edit ({ params, request, response, view }) {
    const post = await Database
      .from('posts')
      .where('id',params.id)
      .first()

    return view.render('post.edit',{post})
  }
......

创建一个视图resources/views/post/edit.edge

@layout('layouts.main')

@section('content')
  <div class="container">
    <h3 class="mt-5 mb-4">Edit Post</h3>
    <form action="/posts/{{post.id}}?_method=PUT" method="POST">
      <div class="form-group">
        <label for="title">Title</label>
        <input value="{{post.title}}" type="text" class="form-controll" placeholder="Enter title here" name="title">
      </div>
      <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" id="" cols="30" rows="10" class="form-control" placeholder="Write something amazing">{{post.content}}</textarea>
      </div>
      {{ csrfField() }}
      <button class="btn btn-primary" type="submit">Submit</button>
    </form>
  </div>
@endsection

用浏览器访问 http://localhost:3333/posts/1/edit 可以看到编辑表单

修改控制器 update方法

......
 async update ({ params, request, response }) {
    const updatedPost = request.only(['title','content'])
    await Database
      .table('posts')
      .where('id',params.id)
      .update(updatedPost)
  }
......

访问http://localhost:3333/posts/1/edit 修改一条记录提交,可以看到数据库里的内容被修改

删除数据:准备删除单个内容用的视图
修改 resources/views/post/show.edge 增加一个删除按钮

@layout('layouts.main')

@section('content')
  <div class="container">
  <h1 class="mt-5 mb-4">{{ post.title }}</h1>
  <div>
    {{post.content}}
  </div>
  </div>
  <ul class="nav justify-content-end fixed-bottom pb-4">
    <li class="nav-item">
      <button id="delete" data-csrf="{{ csrfToken }}" data-id="{{ post.id }}" class="nav-link btn btn-link">Delete</button>
    </li>
  </ul>
@endsection

浏览器访问:http://localhost:3333/posts/1 可以看到右下角的删除按钮

删除数据:删除单个内容资源

修改public/main.js

(function(){
  'use strict'

  const deleteButton = $('#delete')

  deleteButton.click(() => {
    const id = deleteButton.data('id')
    const _csrf = deleteButton.data('csrf')

    $.ajax({
      url: `/posts/${id}`,
      method: 'DELETE',
      data: {
        _csrf
      },
      success: (response) => {
        console.log(response)
      }
    })

  })


}())

修改控制器里的destroy方法

......
  async destroy ({ params, request, response }) {
    await Database
      .table('posts')
      .where('id',params.id)
      .delete()
      return 'success'
  }
......

访问 http://localhost:3333/posts/1 点击delete删除,可以看到控制台输出success ,查看数据库,数据被删除