Сегодня Я хотел бы поделиться своей не большой разработкой, которая помогает мне уже более чем пол года: "Модуль для работы с sqlite3". Заранее предупрежу, мне 14 лет и данный модуль разработан не для улучшения безопасности, а для ускорения работы с SQL запросами.
Концепция
Вместо написания SQL запросов мы будем передавать ключи, значения, названия таблиц, условия и callback'и, которые будут вызывать по завершению запросов(в каждый callback мы будем передавать ошибку и результат, если такой есть).
Представим модуль в виде класса.
Всего будет 4 метода:
getData() - для получения данных из таблицы.
insertData() - для добавления данных в таблицу.
updateData() - для обновления данных в таблице.
deleteData() - для удаления данных из таблицы.
Конечно же с помощью 4 методов приведенных выше мы не сможем исключить все виды запросов, но в моем случаи эти запросы самые частые.
Кодим
Для начало создадим сам класс, настроем экспорт и подключимся к БД и создадим метод, который будет добавлять кавычки у элемента, если тип элемента соответствует типу строки.
class DataBase {
/**
*
* @readonly
*/
static sqlite3 = require('sqlite3').verbose();
/**
*
* @readonly
*/
static database = new this.sqlite3.Database('./database/database.db');
static ToString(value) {
return typeof(value) === 'string' ? '\'' + value + '\'' : value;
}
}
module.exports = {
database: DataBase
};
Для начало напишем метод, который будет возвращать данные из таблицы, не обязательно, но мы будем учитывать, кол-во строк, которые нам будут возвращаться(одна или несколько).
Начинаем собирать запрос с ключевого слова "SELECT", далее перебираем все столбцы, которые нам передали и добавляем их, но не забываем, что символ "*" не нужно "заковывать" в кавычки, т к этот символ говорит нам, что нужно получить все столбцы. Заканчиваем собор указанием таблицы и условия. Далее, исходя из кол-во строк, которые нам надо вернуть вызываем соответствующий метод.
class DataBase {
/**
*
* @readonly
*/
static sqlite3 = require('sqlite3').verbose();
/**
*
* @readonly
*/
static database = new this.sqlite3.Database('./database/database.db');
/**
*
* @param {String[]} keys
* @param {String} table
* @param {String} condition
* @param {Boolean} some
* @param {Function()} callback
*/
static getData(keys, table, condition = '', some = true, callback = () => {}) {
let sql = 'SELECT ';
for (let i = 0; i < keys.length; i++) {
sql += keys[i] === '*' ? keys[i] : '`' + keys[i] + '`';
if (keys.length > i + 1)
sql += ', ';
}
sql += ' FROM `' + table + '` ' + condition;
if (some)
this.database.all(sql, (err, rows) => {
callback(err, rows);
});
else
this.database.get(sql, (err, row) => {
callback(err, row);
});
};
static ToString(value) {
return typeof(value) === 'string' ? '\'' + value + '\'' : value;
}
}
module.exports = {
database: DataBase
};
Напишем метод отвечающий за обновление данных.
Начинаем с указания таблицы, установкой ключей и значений, а завершаем добавлением условия.
class DataBase {
/**
*
* @readonly
*/
static sqlite3 = require('sqlite3').verbose();
/**
*
* @readonly
*/
static database = new this.sqlite3.Database('./database/database.db');
/**
*
* @param {String[]} keys
* @param {String} table
* @param {String} condition
* @param {Boolean} some
* @param {Function()} callback
*/
static getData(keys, table, condition = '', some = true, callback = () => {}) {
let sql = 'SELECT ';
for (let i = 0; i < keys.length; i++) {
sql += keys[i] === '*' ? keys[i] : '`' + keys[i] + '`';
if (keys.length > i + 1)
sql += ', ';
}
sql += ' FROM `' + table + '` ' + condition;
if (some)
this.database.all(sql, (err, rows) => {
callback(err, rows);
});
else
this.database.get(sql, (err, row) => {
callback(err, row);
});
};
/**
*
* @param {String[]} keys
* @param {Values[]} values
* @param {String} table
* @param {String} condition
* @param {Function()} callback
*/
static updateData(keys, values, table, condition, callback = () => {}) {
let sql = 'UPDATE `' + table + '` SET ';
for (let i = 0; i < keys.length; i++) {
sql += '`' + keys[i] + '` = ' + this.ToString(values[i]);
if (keys.length > i + 1)
sql += ', ';
}
sql += ' ' + condition;
this.database.run(sql, (err) => {
callback(err);
});
}
static ToString(value) {
return typeof(value) === 'string' ? '\'' + value + '\'' : value;
}
}
module.exports = {
database: DataBase
};
Остается совсем чуть-чуть, напишем метод для удаления данных(она максимально простой) и метод для добавления данных.
Добавления данных начнем с указания таблицы, потом передадим все ключи, которые мы хотим установить и заканчиваем установкой всех значений(значение должно иметь такой же индекс как и ключ, которому необходимо установить это значение).
class DataBase {
/**
*
* @readonly
*/
static sqlite3 = require('sqlite3').verbose();
/**
*
* @readonly
*/
static database = new this.sqlite3.Database('./database/database.db');
/**
*
* @param {String[]} keys
* @param {String} table
* @param {String} condition
* @param {Boolean} some
* @param {Function()} callback
*/
static getData(keys, table, condition = '', some = true, callback = () => {}) {
let sql = 'SELECT ';
for (let i = 0; i < keys.length; i++) {
sql += keys[i] === '*' ? keys[i] : '`' + keys[i] + '`';
if (keys.length > i + 1)
sql += ', ';
}
sql += ' FROM `' + table + '` ' + condition;
if (some)
this.database.all(sql, (err, rows) => {
callback(err, rows);
});
else
this.database.get(sql, (err, row) => {
callback(err, row);
});
};
/**
*
* @param {String[]} keys
* @param {Values[]} values
* @param {String} table
* @param {String} condition
* @param {Function()} callback
*/
static updateData(keys, values, table, condition, callback = () => {}) {
let sql = 'UPDATE `' + table + '` SET ';
for (let i = 0; i < keys.length; i++) {
sql += '`' + keys[i] + '` = ' + this.ToString(values[i]);
if (keys.length > i + 1)
sql += ', ';
}
sql += ' ' + condition;
this.database.run(sql, (err) => {
callback(err);
});
}
/**
* @param {String[]} keys
* @param {String[]} values
* @param {String} table
* @param {Function()} callback
*/
static insertData(keys, values, table, callback = () => {}) {
let sql = 'INSERT INTO `' + table + '` (';
for (let i = 0; i < keys.length; i++) {
sql += '`' + keys[i] + '`';
if (keys.length > i + 1)
sql += ', ';
}
sql += ') VALUES (';
for (let i = 0; i < values.length; i++) {
sql += this.ToString(values[i]);
if (values.length > i + 1)
sql += ', ';
}
sql += ')';
this.database.run(sql, (err) => {
callback(err);
});
};
/**
*
* @param {String} table
* @param {String} condition
* @param {Function()} callback
*/
static deleteData(table, condition = '', callback = () => {}) {
this.database.run('DELETE FROM `' + table + '` ' + condition, (err) => {
callback(err);
});
}
static ToString(value) {
return typeof(value) === 'string' ? '\'' + value + '\'' : value;
}
}
module.exports = {
database: DataBase
};
Пример эксплуатации
Представим, что мы имеем таблицу users
с полями id
, first_name
и last_name
Все совпадения случайны
id | first_name | last_name |
1 | Иван | Иванович |
2 | Николай | Николаевич |
3 | Георгий | Георгиевич |
Получение данных
Для начала давайте получим Имя самого первого пользователя в нашей системе.
const { database } = require('database');
database.getData(['first_name'], 'users', 'WHERE `id` = 1', false, (err, row) => {
if (row !== undefined)
console.log(row);
else
console.log('User not found');
});
После выполнения ранее приведенного кода, мы получим ответ показанный ниже.
{
'first_name': 'Иван'
}
Обновление данных
Обновим все имена Иван
на Игорь
.
const { database } = require('database');
database.updateData(['first_name'], ['Игорь'], 'users', 'WHERE `first_name` = \'Иван\'');
В результате мы получим следующую таблицу.
id | first_name | last_name |
1 | Игорь | Иванович |
2 | Николай | Николаевич |
3 | Георгий | Георгиевич |
Добавление данных
Добавим нового пользователя Илью Ильича
.
const { database } = require('database');
database.insertData(['first_name', 'last_name'], ['Илья', 'Ильич'], 'users');
В результате мы получим следующую таблицу.
id | first_name | last_name |
1 | Игорь | Иванович |
2 | Николай | Николаевич |
3 | Георгий | Георгиевич |
4 | Илья | Ильич |
Удаление данных
Удалим всех Николаевичей
из нашей таблицы.
const { database } = require('database');
database.deleteData('users', 'WHERE `last_name` = \'Николаевич\'');
В результате мы получим следующую таблицу.
id | first_name | last_name |
1 | Игорь | Иванович |
3 | Георгий | Георгиевич |
4 | Илья | Ильич |
На этом все, спасибо за внимание!
Проект на GitHub
debagger
Вы бы хоть пару примеров добавили, как ваш модуль использовать. Ну и в целом непонятно, зачем — если уж хочется уйти от написания SQL, что вам мешает использовать <название любой ORM по вкусу>, там хотя бы уже подумали о защите от SQL-инъекций например.
debagger
Извиняюсь за несколько токсичный комментарий выше. Илья, ты молодец, что развиваешься и не стесняешься делиться своими достижениями. По статье есть следующие замечания:
1. Опечатки. Их довольно много, кто-то не придаст значения, а кто-то сочтет признаком неаккуратности и легкомыслия (например это может быть твой будущий работодатель, когда захочет ознакомиться с твоим профилем на хабре перед тем как принять решение, стоит ли тебя брать на работу).
2. Нет примеров использования. Если ты не объясняешь, как использовать твою разработку, то ее никто не сможет использовать, а проще всего объяснить в чем удобство через примеры.
3. Ну и наконец настоятельно рекомендую почитать про ORM, а также про атаки SQL-инъекцией.
Ilya-cmd Автор
Спасибо, исправлюсь
Ilya-cmd Автор
Добавил примеры использования, спасибо