Рубрики
Без рубрики

SQLite Tutorial с Node.js

Автор оригинала: Adam McQuistan.

В этом уроке я буду продемонстрировать, как использовать SQLite в сочетании с JavaScript внутри среды Node.js с помощью драйвера SQLite3 Node.js. Для тех, кто не знаком с SQLite , это простая одно файловая реляционная база данных, которая очень популярна среди смарт-устройств, встроенных систем и даже небольшого веб-приложения.

Настройка и установка

Я начну с создания нового пакета NPM, используя NPM init Внутри пустой каталог называется узлом-SQLite-учебником.

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install  --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (app) node-sqlite
version: (0.0.0) 0.1.0
description: Code for tutorial blog on node and sqlite
entry point: (index.js) main.js
test command: 
git repository: 
keywords: 
author: Adam McQuistan
license: (BSD) MIT
About to write to /node-sqlite/app/package.json:

{
  "name": "node-sqlite",
  "version": "0.1.0",
  "description": "Code for tutorial blog on node and sqlite",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": "",
  "author": "Adam McQuistan",
  "license": "MIT"
}


Is this ok? (yes) 

Далее мне нужно будет установить SQLite3 Пакет через NPM вроде так:

$ npm install --save sqlite3

В дополнение к SQLite3 я собираюсь установить Bluebird Так что я могу использовать знакомые функциональные возможности обещания в моем программировании базы данных.

$ npm install --save bluebird

Теперь я создадим пустой файл прямо рядом с файлом package.json под названием database.sqlite3, который SQLite будет хранить данные в.

Проектирование базы данных

Как и в случае почти каждого из моих других статей, я буду использовать приложение, чтобы помочь описать некоторые важные аспекты программирования базы данных с Node.js и sqlite. Для этой статьи я собираюсь с предположением, что я создающую слой доступа к данным для приложения для отслеживания проекта и задач. Основные бизнес-правила для этого уровня доступа к данным приложения следующие:

  • Приложение имеет проекты
  • Каждый проект может иметь одну или несколько задач для завершения

С заявленными правилами бизнеса я могу принять эту информацию и начать проектировать необходимые таблицы и их поля. Ясно, что мне понадобится Проекты таблица, а также Задачи Таблица. Для остальных я просто буду использовать небольшую интуицию, некоторые составляли тестовые данные, и с ним ролится (характерная характеристика работы для большинства разработчиков).

Таблица проектов

1 Написать Node.js – SQLite Tutorial

Задачи таблицы

1 Контур 1 Обзор высокого уровня разделов 1
2 Писать 1 Написать содержимое статьи и примеры кода 0

Хорошо, теперь, когда я знаю, что мне нужно создать, теперь я могу перевести это в код.

Создание базы данных

Чтобы начать мне нужно будет сделать файл main.js вместе с файлом dao.js (или объектом доступа к данным ) в том же каталоге, что и файл package.json.

Внутри dao.js я добавлю импорт для sqlite3 и bluebird’s Обещание объекты. После этого я расстроил класс доступа к данным, называемый Appdao Это будет установить соединение с базой данных внутри конструктора и назначить его в поле для элементов под названием дБ Отказ

// dao.js

const sqlite3 = require('sqlite3')
const Promise = require('bluebird')

class AppDAO {
  constructor(dbFilePath) {
    this.db = new sqlite3.Database(dbFilePath, (err) => {
      if (err) {
        console.log('Could not connect to database', err)
      } else {
        console.log('Connected to database')
      }
    })
  }
}

module.exports = AppDAO

Соединение довольно быстро. Вы просто создаете SQLite3 База данных Class Constructor, передавая его путь к файлу базы данных SQLite, который вы хотите подключиться и обязательно проверять наличие ошибок, которые могут возникнуть. Как отмечалось выше, я храним этот объект соединения в поле под названием дБ на Appdao класс.

Я буду прогрессировать, объясняя, как использовать объект подключения для отправки запросов в базу данных. Пакет SQLite3 Node.js дает горстку различных методов для выполнения запросов, но те, которые я буду сосредоточиться на этом руководстве:

  • Беги : Используется для создания или изменения таблиц и вставить или обновлять данные таблицы
  • получить : Выберите один ряд данных из одной или нескольких таблиц
  • Все : Выберите несколько строк данных из одной или нескольких таблиц

Чтобы начать, я хотел бы исследовать Беги метод. Его общий синтаксис выглядит так:

db.run('SOME SQL QUERY', [param1, param2], (err) => {
  if (err) {
    console.log('ERROR!', err)
  }
})

Первый параметр передан Беги (...) Это строка SQL для выполнения и является единственным обязательным параметром. Второй является необязательным массивом параметров, которые библиотека SQLite3 будет поменяться для любого «?» Защитники в процессе (я буду продемонстрировать это немного). Финал является функцией обратного вызова ошибки.

Как вы можете подозревать, я буду использовать Беги (...) Функция для создания и обновления моих проектов и задач. Тем не менее, я на самом деле собираюсь обернуть его в мою собственную версию Беги Метод на Appdao класс, потому что я хотел бы капсулировать его в Bluebird Обещание сделать вещи явно асинхронным и обещанием, основанным так:

// dao.js

const sqlite3 = require('sqlite3')
const Promise = require('bluebird')

class AppDAO {
  // omitting constructor code
  
  run(sql, params = []) {
    return new Promise((resolve, reject) => {
      this.db.run(sql, params, function (err) {
        if (err) {
          console.log('Error running sql ' + sql)
          console.log(err)
          reject(err)
        } else {
          resolve({ id: this.lastID })
        }
      })
    })
  }
}

С моим пользовательским Appdao.run (...) Способ теперь я могу использовать его для создания таблиц продуктов и задач.

Чтобы начать добавить еще два файла в мой проект под названием Project_Repository.js и task_repository.js. Внутри проекта_Repository.js Я определяю класс под названием Проекционоспосово у которого есть конструктор, который принимает экземпляр Appdao Объект и A CreateTable Метод, который выполняет некоторую DDL (язык определения данных) SQL, как так:

// project_repository.js

class ProjectRepository {
  constructor(dao) {
    this.dao = dao
  }

  createTable() {
    const sql = `
    CREATE TABLE IF NOT EXISTS projects (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      name TEXT)`
    return this.dao.run(sql)
  }
}

module.exports = ProjectRepository;

Тогда я делаю по сути то же самое снова, но на этот раз в файле task_repository.js.

// task_repository.js

class TaskRepository {
  constructor(dao) {
    this.dao = dao
  }

  createTable() {
    const sql = `
      CREATE TABLE IF NOT EXISTS tasks (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        description TEXT,
        isComplete INTEGER DEFAULT 0,
        projectId INTEGER,
        CONSTRAINT tasks_fk_projectId FOREIGN KEY (projectId)
          REFERENCES projects(id) ON UPDATE CASCADE ON DELETE CASCADE)`
    return this.dao.run(sql)
  }
}

module.exports = TaskRepository;

SQL DDL для создания таблиц завершен, поэтому я передойду к способам вставки данных в таблицы.

Вставка данных

В Проекционоспосово Класс мне нужно добавить создать Способ, который получает имя проекта для создания и выполнения соответствующего оператора INSERT, используя Appdao.run (...) метод. Обратите внимание, как я использовал «?» представлять значение для имени проекта, а затем поставить Имя Параметр в дополнительном аргументе массива параметров для Беги (...) метод. Это известно как параметризованное утверждение запроса, которое начнет входы, чтобы минимизировать риски инжекции SQL.

// project_repository.js

class ProjectRepository {
  // omitting other methods

  create(name) {
    return this.dao.run(
      'INSERT INTO projects (name) VALUES (?)',
      [name])
  }
}

module.exports = ProjectRepository;

Подобный метод создания необходим для TaskRepository класс.

// task_repository.js

class TaskRepository {
  // omitting other methods

  create(name, description, isComplete, projectId) {
    return this.dao.run(
      `INSERT INTO tasks (name, description, isComplete, projectId)
        VALUES (?, ?, ?, ?)`,
      [name, description, isComplete, projectId])
  }
}

module.exports = TaskRepository;

Теперь, когда у меня есть возможность вставлять данные в базу данных, которую я хотел бы добавить функциональность, чтобы обновить ее.

Обновление данных

В Проекционоспосово класс я добавлю Обновить Метод, который занимает Проект Объект и обновления всех полей для записи базы данных этого проекта снова используют Appdao.run (...) Метод, как и так:

// project_repository.js

class ProjectRepository {
  // omitting other methods

  update(project) {
    const { id, name } = project
    return this.dao.run(
      `UPDATE projects SET name = ? WHERE id = ?`,
      [name, id]
    )
  }
}

module.exports = ProjectRepository;

Далее – добавить соответствующий метод обновления в TaskRepository класс.

// task_repository.js

class TaskRepository {
  // omitting other methods

  update(task) {
    const { id, name, description, isComplete, projectId } = task
    return this.dao.run(
      `UPDATE tasks
      SET name = ?,
        description = ?,
        isComplete = ?,
        projectId = ?
      WHERE id = ?`,
      [name, description, isComplete, projectId, id]
    )
  }
}

module.exports = TaskRepository;

Удаление данных

Последняя мутационная функциональность для реализации состоит в том, чтобы обеспечить возможность удаления записей из базы данных. Для этого я снова буду использовать Appdao.run (...) Метод в сочетании с новым Удалить Методы для обоих Проекционоспосово и TaskRepository классы.

Для Проекционоспосово Это выглядит так:

// project_repository.js

class ProjectRepository {
  // omitting other methods

  delete(id) {
    return this.dao.run(
      `DELETE FROM projects WHERE id = ?`,
      [id]
    )
  }
}

module.exports = ProjectRepository;

И за TaskRepository Похоже, это выглядит:

// task_repository.js

class TaskRepository {
  // omitting other methods

  delete(id) {
    return this.dao.run(
      `DELETE FROM tasks WHERE id = ?`,
      [id]
    )
  }
}

module.exports = TaskRepository;

Хорошо, что обвиняет все способы, которыми я буду использовать Беги метод. Далее я буду представить двух других связанных получить и Все SQLite3 Node.js Методы пакета.

Чтение данных

В этом разделе я собираюсь перейти, как использовать получить и Все Методы библиотеки SQLite3 Node.js. Как упоминалось ранее, получить используется для извлечения одного ряда данных во время Все используется для запроса многих рядов данных.

Базовый синтаксис для использования получить Похоже, это выглядит:

db.get('SELECT ...', [param1, param2], (err, result) => {
  if (err) {
    console.log(err)
  } else {
    // do something with result
  }
})

Где дБ это объект подключения SQLite3. Вы заметите, что синтаксис по существу идентичен Беги Способ, за исключением того, что обратный вызов имеет дополнительный параметр, который содержит объект результата запроса, предполагая, что ошибка не была брошена.

Базовый синтаксис для Все По сути так же, кроме второго параметра к обратным вызовам – это массив результатов, возвращаемых запросом, например:

db.all('SELECT ...', [param1, param2], (err, results) => {
  if (err) {
    console.log(err)
  } else {
    // do something with results
  }
})

Так же, как я сделал с SQLite3 Беги Метод, который я собираюсь реализовать получить и Все Методы, использующие Bluebird Обещание в пределах Appdao класс, как показано ниже:

// dao.js

const sqlite3 = require('sqlite3').verbose()
const Promise = require('bluebird')

class AppDAO {
  // omitting other methods
  
  get(sql, params = []) {
    return new Promise((resolve, reject) => {
      this.db.get(sql, params, (err, result) => {
        if (err) {
          console.log('Error running sql: ' + sql)
          console.log(err)
          reject(err)
        } else {
          resolve(result)
        }
      })
    })
  }

  all(sql, params = []) {
    return new Promise((resolve, reject) => {
      this.db.all(sql, params, (err, rows) => {
        if (err) {
          console.log('Error running sql: ' + sql)
          console.log(err)
          reject(err)
        } else {
          resolve(rows)
        }
      })
    })
  }
}

Теперь я могу использовать эти методы в Проекционоспосово и TaskRepository Классы для извлечения данных из базы данных SQLite.

Начать я добавлю GetByid Методы для каждого класса для выбора их записей по ID.

В Проекционоспосово Я добавляю это:

// project_repository.js

class ProjectRepository {
  // omitting other methods

  getById(id) {
    return this.dao.get(
      `SELECT * FROM projects WHERE id = ?`,
      [id])
  }
}

module.exports = ProjectRepository;

И в TaskRepository по аналогии:

// task_repository.js

class TaskRepository {
  // omitting other methods

  getById(id) {
    return this.dao.get(
      `SELECT * FROM tasks WHERE id = ?`,
      [id])
  }
}

module.exports = TaskRepository;

Чтобы продемонстрировать Appdao.all (...) Метод добавим возможность выбирать все проекты, а также все задачи для данного проекта.

Код для выбора всех проектов выглядит так:

// project_repository.js

class ProjectRepository {
  // omitting other methods

  getAll() {
    return this.dao.all(`SELECT * FROM projects`)
  }
}

module.exports = ProjectRepository;

Затем выбирать все задачи для проекта, я буду использовать метод под названием GetTasks (ProjectID) Это ожидает идентификатора проекта, для которого вы хотите задачи.

// project_repository.js
class ProjectRepository {
  // omitting other methods

  getTasks(projectId) {
    return this.dao.all(
      `SELECT * FROM tasks WHERE projectId = ?`,
      [projectId])
  }
}

module.exports = ProjectRepository;

Установка кода доступа к данным для использования

До сих пор я в основном создал библиотеку доступа к данным для этого фиктивного проекта и приложения для отслеживания задач. То, что я хотел бы сделать сейчас, это использовать его для загрузки моих тестовых данных, отображаемых в таблицах в Проектирование базы данных раздел.

В файле main.js я хочу потянуть в Appdao , Проекционоспосово и TaskRepository Классы через требуется Отказ Затем я буду использовать их для создания таблиц, заполните их данные, извлеките данные из базы данных и отображайте на консоль.

// main.js

const Promise = require('bluebird')
const AppDAO = require('./dao')
const ProjectRepository = require('./project_repository')
const TaskRepository = require('./task_repository')

function main() {
  const dao = new AppDAO('./database.sqlite3')
  const blogProjectData = { name: 'Write Node.js - SQLite Tutorial' }
  const projectRepo = new ProjectRepository(dao)
  const taskRepo = new TaskRepository(dao)
  let projectId

  projectRepo.createTable()
    .then(() => taskRepo.createTable())
    .then(() => projectRepo.create(blogProjectData.name))
    .then((data) => {
      projectId = data.id
      const tasks = [
        {
          name: 'Outline',
          description: 'High level overview of sections',
          isComplete: 1,
          projectId
        },
        {
          name: 'Write',
          description: 'Write article contents and code examples',
          isComplete: 0,
          projectId
        }
      ]
      return Promise.all(tasks.map((task) => {
        const { name, description, isComplete, projectId } = task
        return taskRepo.create(name, description, isComplete, projectId)
      }))
    })
    .then(() => projectRepo.getById(projectId))
    .then((project) => {
      console.log(`\nRetreived project from database`)
      console.log(`project id = ${project.id}`)
      console.log(`project name = ${project.name}`)
      return projectRepo.getTasks(project.id)
    })
    .then((tasks) => {
      console.log('\nRetrieved project tasks from database')
      return new Promise((resolve, reject) => {
        tasks.forEach((task) => {
          console.log(`task id = ${task.id}`)
          console.log(`task name = ${task.name}`)
          console.log(`task description = ${task.description}`)
          console.log(`task isComplete = ${task.isComplete}`)
          console.log(`task projectId = ${task.projectId}`)
        })
      })
      resolve('success')
    })
    .catch((err) => {
      console.log('Error: ')
      console.log(JSON.stringify(err))
    })
}

main()

Запустить использование Узел как это:

$ node main.js

И вы увидите вывод, как показано ниже.

Connected to database
Retreived project from database
project id = 1
project name = 1
Retrieved project tasks from database
task id = 1
task name = Outline
task description = High level overview of sections
task isComplete = 1
task projectId = 1
task id = 2
task name = Write
task description = Write article contents and code examples
task isComplete = 0
task projectId = 1

Заключение

В этом руководстве я проверил основы API набора Package.js sqlite3 и продемонстрировал, как вы можете обернуть эту функциональность в объектно-ориентированном JavaScript с фокусом на асинхронном осуществлении на основе обещания.

Как всегда, я благодарю вас за чтение и приветствовавшие комментарии и критики ниже.