Автор оригинала: 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 с фокусом на асинхронном осуществлении на основе обещания.
Как всегда, я благодарю вас за чтение и приветствовавшие комментарии и критики ниже.