Используйте Sequelize для подключения к базе данных

Node.js база данных внешний интерфейс SQL

Sequelize.js — это ORM-фреймворк для nodejs.

Люди, которые использовали nodejs для подключения к базам данных, должны быть знакомы с базами данных. Если это прямая ссылка, вам необходимо установить соединение и управлять им самостоятельно, а также вручную написать операторы SQL. Простые проекты не имеют значения, но как только дизайн проекта будет более сложным и будет много таблиц, написание всего SQL будет потреблять много энергии.

Уже существуют облегченные фреймворки или решения для баз данных на таких языках, как Java и C#. В nodejs я рекомендую Sequelize. Это очень зрелая структура, и она также имеет преимущества в скорости и производительности. Наиболее важной частью является то, что ежедневной разработке нужно только управлять созданием объектов, вызовом методов запросов и т. д., и редко требуется писать операторы SQL. Одним из преимуществ этого является то, что он избавляет от обслуживания сложных операторов sql, а также позволяет избежать ненужных ошибок, вызванных sql.

Sequelize — это ORM-фреймворк для node.js и io.js. В частности, он выделяет широкий спектр поддержки и унифицированные методы настройки и запросов. Поддерживаемые базы данных включают: PostgreSQL, MySQL, MariaDB, SQLite и MSSQL.

Отображаемый адрес теста и API в этой статье:гитхаб-адрес

демо

Призыв к Sequelize подчеркивает быстроту и простоту. В конкретной ситуации можно почувствовать следующий код. Если у вас есть опыт разработки, вы можете его пропустить.

Table1.findById(23);
//select a,b,c,d from table1 where id=23;

Table1.findAll({
	where:{a:"test",b:76}
});
//select a,b,c,d from table1 where a="test" and "b=76;

При запросе одной таблицы для выполнения запроса требуется только простая конфигурация. Это очень просто и удобно?

Подключиться к базе данных

Соединение Sequelize должно передавать параметры, и можно настроить такие операции, как открытие пула потоков, чтение и запись вложенных библиотек и т. д.

Простой способ написать это так:new Sequelize("表名","用户名","密码",配置)

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

const sequelize = new Sequelize('database', 'username', 'password',  {
  host: 'localhost',    //数据库地址,默认本机
  port:'3306',
  dialect: 'mysql',
  pool: {   //连接池设置
    max: 5, //最大连接数
    min: 0, //最小连接数
    idle: 10000
  },
 });

Ниже приведены подробные параметры конфигурации.

const sequelize = new Sequelize('database', 'username', 'password', {
  // 数据库类型,支持: 'mysql', 'sqlite', 'postgres', 'mssql'
  dialect: 'mysql',
  // 自定义链接地址,可以是ip或者域名,默认本机:localhost
  host: 'my.server.tld',
  // 自定义端口,默认3306
  port: 12345,
  // postgres使用的参数,连接类型,默认:tcp
  protocol: null,
  // 是否开始日志,默认是用console.log
  // 建议开启,方便对照生成的sql语句
  logging: true,
  // 默认是空
  // 支持: 'mysql', 'postgres', 'mssql'
  dialectOptions: {
    socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock',
    supportBigNumbers: true,
    bigNumberStrings: true
  },
  // sqlite的存储位置,仅sqlite有用
  // - 默认 ':memory:'
  storage: 'path/to/database.sqlite',

  // 是否将undefined转化为NULL
  // - 默认: false
  omitNull: true,
  // pg中开启ssl支持
  // - 默认: false
  native: true,
  // 数据库默认参数,全局参数
  define: {
    underscored: false
    freezeTableName: false,
    charset: 'utf8',
    dialectOptions: {
      collate: 'utf8_general_ci'
    },
    timestamps: true
  },
  // 是否同步
  sync: { force: true },
  // 连接池配置
  pool: {
    max: 5,
    idle: 30000,
    acquire: 60000,
  },
  isolationLevel: Transaction.ISOLATION_LEVELS.REPEATABLE_READ
})

определить объекты модели

Обязательно создайте объект модели перед его использованием. Это имя таблицы в базе данных, используемые поля, тип поля и т. д.

Вот рекомендуемый путь развития. Сначала создайте объект в nodejs, а затем вызовите метод синхронизации Sequelize для автоматического создания базы данных. Это избавляет от необходимости писать код для построения таблиц и вручную создавать таблицы в базе данных. Просто рассмотрите свойства, такие как типы объектов, в вашем коде отдельно.

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

Вот простое создание объекта, которое работает в большинстве случаев.


const users = db.define('t_user'/*自定义表名*/, {
    id: {
        type: Sequelize.INTEGER,
        primaryKey: true,       //主键
        autoIncrement: true,    //自增
        comment: "自增id"       //注释:只在代码中有效
    },
    //用户名
    username: {
        type: Sequelize.STRING,
        validate:{
	        isEmail: true,   //类型检测,是否是邮箱格式
        }
    },
    //密码
    pwd: {
        type: Sequelize.STRING(10),
        allowNull: false,//不允许为null
    },
    //状态
    status: {
        type: Sequelize.INTEGER,
         defaultValue: 0,//默认值是0
    },
    //昵称
    nickname: {
        type: Sequelize.STRING
    },
    //token
    token: {
        type: Sequelize.UUID
    },
    create_time: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW
    }
}, {
    //使用自定义表名
    freezeTableName: true,
    //去掉默认的添加时间和更新时间
    timestamps: false,
    indexes:[
	    //普通索引,默认BTREE
        {
            unique: true,
            fields: ['pid']
        },
     ]
});

//同步:没有就新建,有就不变
// users.sync();
//先删除后同步
users.sync({
    force: true
});

тип данных

В предыдущем пункте было описано создание объектов, в котором использовались различные типы объектов. Вот как использовать тип.

Sequelize.STRING 		//字符串,长度默认255,VARCHAR(255)
Sequelize.STRING(1234)  //设定长度的字符串,VARCHAR(1234)
Sequelize.STRING.BINARY   //定义类型VARCHAR BINARY
Sequelize.TEXT           //长字符串,文本 TEXT
Sequelize.TEXT('tiny')   //小文本字符串,TINYTEXT

Sequelize.INTEGER      //int数字,int
Sequelize.BIGINT       //更大的数字,BIGINT
Sequelize.BIGINT(11)   //设定长度的数字,BIGINT(11)

Sequelize.FLOAT        //浮点类型,FLOAT
Sequelize.FLOAT(11)     //设定长度的浮点,FLOAT(11)
Sequelize.FLOAT(11, 12)  //设定长度和小数位数的浮点,FLOAT(11,12)

Sequelize.REAL     //REAL  PostgreSQL only.
Sequelize.REAL(11) // REAL(11)    PostgreSQL only.
Sequelize.REAL(11, 12)  // REAL(11,12) PostgreSQL only.

Sequelize.DOUBLE     // DOUBLE
Sequelize.DOUBLE(11)  // DOUBLE(11)
Sequelize.DOUBLE(11, 12) // DOUBLE(11,12)

Sequelize.DECIMAL     // DECIMAL
Sequelize.DECIMAL(10, 2)  // DECIMAL(10,2)

Sequelize.DATE    // 日期类型,DATETIME for mysql / sqlite, TIMESTAMP WITH TIME ZONE for postgres
Sequelize.DATE(6) // mysql 5.6.4+支持,分秒精度为6位
Sequelize.DATEONLY   // 仅日期部分
Sequelize.BOOLEAN   // int类型,长度为1,TINYINT(1)

Sequelize.ENUM('value 1', 'value 2')  // 枚举类型
Sequelize.ARRAY(Sequelize.TEXT)  //PostgreSQL only.
Sequelize.ARRAY(Sequelize.ENUM)  //  PostgreSQL only.

Sequelize.JSON   // JSON column. PostgreSQL, SQLite and MySQL only.
Sequelize.JSONB  // JSONB column. PostgreSQL only.

Sequelize.BLOB   // BLOB (bytea for PostgreSQL)
Sequelize.BLOB('tiny')  // TINYBLOB (bytea for PostgreSQL. Other options are medium and long)

Sequelize.UUID  // PostgreSQL和SQLite的数据类型是UUID, MySQL是CHAR(36)类型

Sequelize.CIDR  // PostgreSQL中的CIDR类型
Sequelize.INET   // PostgreSQL中的INET类型
Sequelize.MACADDR  // PostgreSQL中的MACADDR类型

Sequelize.RANGE(Sequelize.INTEGER)    //PostgreSQL only.
Sequelize.RANGE(Sequelize.BIGINT)     // PostgreSQL only.
Sequelize.RANGE(Sequelize.DATE)       //PostgreSQL only.
Sequelize.RANGE(Sequelize.DATEONLY)   //PostgreSQL only.
Sequelize.RANGE(Sequelize.DECIMAL)    //PostgreSQL only.

Sequelize.ARRAY(Sequelize.RANGE(Sequelize.DATE)) // PostgreSQL only.

Sequelize.GEOMETRY   //PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT')  // PostgreSQL (with PostGIS) or MySQL only.
Sequelize.GEOMETRY('POINT', 4326)// PostgreSQL (with PostGIS) or MySQL only.

Определение типа данных

Как вы можете видеть выше, поле проверки используется для проверки того, соответствует ли значение поля стандарту, чтобы вы могли узнать, соответствуют ли данные правилам, перед вводом в базу данных. В противном случае опрометчиво занести в базу данных незнакомые данные — это все равно, что привести незнакомцев домой, безопасно это или нет, зависит от судьбы.

Sequelize по-прежнему поддерживает множество встроенных проверок, и если они вас не устраивают, вы можете определить их самостоятельно.

validate: {
    is: ["^[a-z]+$",'i'],     // 全匹配字母
    is: /^[a-z]+$/i,          // 全匹配字母,用规则表达式写法
    not: ["[a-z]",'i'],       // 不能包含字母
    isEmail: true,            // 检查邮件格式
    isUrl: true,              // 是否是合法网址
    isIP: true,               // 是否是合法IP地址
    isIPv4: true,             // 是否是合法IPv4地址
    isIPv6: true,             // 是否是合法IPv6地址
    isAlpha: true,            // 是否是字母
    isAlphanumeric: true,     // 是否是数字和字母
    isNumeric: true,          // 只允许数字
    isInt: true,              // 只允许整数
    isFloat: true,            // 是否是浮点数
    isDecimal: true,          // 是否是十进制书
    isLowercase: true,        // 是否是小写
    isUppercase: true,        // 是否大写
    notNull: true,            // 不允许为null
    isNull: true,             // 是否是null
    notEmpty: true,           // 不允许为空
    equals: 'specific value', // 等于某些值
    contains: 'foo',          // 包含某些字符
    notIn: [['foo', 'bar']],  // 不在列表中
    isIn: [['foo', 'bar']],   // 在列表中
    notContains: 'bar',       // 不包含
    len: [2,10],              // 长度范围
    isUUID: 4,                // 是否是合法 uuids
    isDate: true,             // 是否是有效日期
    isAfter: "2011-11-05",    // 是否晚于某个日期
    isBefore: "2011-11-05",   // 是否早于某个日期
    max: 23,                  // 最大值
    min: 23,                  // 最小值
    isArray: true,            // 是否是数组
    isCreditCard: true,       // 是否是有效信用卡号
    // 自定义规则
    isEven: function(value) {
    if(parseInt(value) % 2 != 0) {
        throw new Error('请输入偶数!')
    }
}

Краткое введение в API

API Sequelize в основном охватывает распространенные методы использования, среди которых есть несколько часто используемых однотабличных запросов. Более сложные могут ссылаться на большее количество API.

Запросить несколько findAll(opts) или все(opts)

Параметры, используемые в запросе, обычно являются общими, и только некоторые API имеют специальные параметры. Здесь показан общий параметр, который ниже пропущен.

let list = await model.findAll({
	where:{
		id:{$gt:10},//id大于10的
		name:"test"  //name等于test
	},
	order:[
		"id",   //根据id排序
		["id","desc"]//根据id倒序
	],
	limit:10,//返回个数
	offset:20,//起始位置,跳过数量
	attributes:["attr1","attr2"], //返回的字段
});
//select attr1,attr2 from model where ......

Запрос по id findById(id,opts)

Здесь первичным ключом данных по умолчанию является id, и при запросе данные напрямую запрашиваются по id. Рекомендуется добавить id в качестве уникального первичного ключа при создании новой базы данных.

let model = await model.findById(12);
//select a,b,c from model where id=12;

Запросить запись findOne(opts)

Запросить записи по условиям, условия здесь должны быть заполнены, иначе будут возвращены первые данные.

let model = await model.findOne({
	where:{id:12}
});
//select a,b,c from model where id=12;

Пейджинговый запрос findAndCount(opts) или findAndCountAll

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

Этот метод будет выполнять 2 оператора перехода одновременно.

let data = await model.findAndCount({
	limit:10,//每页10条
	offset:0*10,//第x页*每页个数
	where:{}
});
let list = data.rows;
let count = data.count;
//select count(*) from model where ...;
//select a,b,c from model where .... limit 0,10;

Добавить новое создание данных (модель, опции)

Это очень легко добавить. Просто передайте объект модели. Здесь необходимо обеспечить согласованность атрибутов и имен полей объекта модели. Ошибка возникает, если они несовместимы. Вы также можете передать параметры конфигурации, чтобы добавить условия и т. д.

let model= {
	name:"test",
	token:"adwadfv2324"
}
 await model.create(model);
//insert into model (name,token) values("test","adwadfv2324");

Запрос, если он не существует, возвращает объект по умолчанию findOrInitialize(opts)

opts.default объект значения по умолчанию

Этот метод сначала запросит базу данных, и если нет результата, он вернет объект по умолчанию в параметре. Это больше подходит для таких сценариев, как возврат объекта по умолчанию.

Запрос, если он не существует, создайте новый findOrCreate(opts) или findCreateFind

Этот метод используется во многих случаях. Обычно используется для автоматического создания данных, которых не существует. Значение по умолчанию возвращается напрямую.

Если есть, обновите, если нет, добавьте upsert(model,opts) или insertOrUpdate(model,opts)

Сопоставление по первичному ключу или уникальному ключу ограничения

Он часто используется для добавления или обновления унифицированных операций при редактировании.

Обновление записей обновления (модель, опции)

Это наиболее часто используемый метод обновления.Вы можете передать объект модели, который нужно обновить, и в то же время использовать параметры конфигурации, чтобы условно различать объекты, которые нужно обновить.

удалить запись уничтожить (опции)

Существует два случая удаления, один из которых — физическое удаление. Удалите его из таблицы не существует. Другой — установить paranoid, то есть виртуальное удаление.По умолчанию поле указывает, удаляются ли данные.При запросе удалите это условие, чтобы запросить удаленные данные.

восстановить записи восстановить(варианты)

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

Другие распространенные API

  1. Укажите максимальное значение поля запроса max("id",opts)
  2. Укажите минимальное значение поля запроса min("id",opts)
  3. Сумма суммирования("id",opts)
  4. Массовое добавление bulkCreate([model],opts)
  5. Информация о структуре таблицы поиска описывает()
  6. приращение приращение("id",{by:1})
  7. декремент декремент ("id", {by: 1})
  8. Подсчитайте количество запросов count(opts)

дела

Транзакции в Sequelize относительно просты. Но если транзакций несколько, написанный код будет очень некрасивым. Это также относительно плохое место для оптимизации Sequelize.

Вы должны помнить, что параметры транзакции должны передаваться последовательно. Другой — обычный вызов Promise.

//调用Sequelize初始化之后的sequelize对象
return sequelize.transaction(function (t) {
	//返回最终的Promise
  return User.create({
    firstName: 'Abraham',
    lastName: 'Lincoln'
  }, {transaction: t}).then(function (user) {
    return user.setShooter({
      firstName: 'John',
      lastName: 'Boothe'
    }, {transaction: t});
  });
}).then(function (result) {
  //主动调用commit提交结果
  return t.commit();
}).catch(function (err) {
  //主动回滚操作
  return t.rollback();
});

Запрос соединения нескольких таблиц

Внешние ключи могут быть трудностью в Sequelize. Здесь немного больше, так что давайте пройдемся по этому медленно.

Точки знаний внешнего ключа

Настройка внешних ключей ---- три режима ограничений:

  1. район: строгий режим (по умолчанию), родительская таблица не может удалить или обновить запись, на которую ссылается дочерняя таблица.
  2. каскадный: каскадный режим, после обработки родительской таблицы данные, связанные с дочерней таблицей, также обрабатываются вместе. Также режим по умолчанию для Sequelize.
  3. установить нуль: пустой режим, предпосылка заключается в том, что поле внешнего ключа может быть NLL, после операции родительской таблицы соответствующее поле дочерней таблицы пусто.

Предпосылки для использования внешних ключей

Использование внешних ключей в Sequelize требует предварительной проверки следующих параметров, ошибка в которых приведет к сбою настройки.

  1. Механизм хранения таблиц должен быть innodb, в противном случае созданный внешний ключ не имеет эффекта ограничения.
  2. Тип столбца внешнего ключа должен точно совпадать с типом первичного ключа родительской таблицы.
  3. Имена внешних ключей не могут повторяться.
  4. Когда поле с существующими данными установлено как внешний ключ, необходимо убедиться, что данные в поле соответствуют данным первичного ключа родительской таблицы.

Пример использования --- по умолчанию

По умолчанию первичный ключ использует поле id основной таблицы, а внешний ключ использует внешний ключ, установленный в виде таблица+поле. В общем, его нужно указать вручную.

//主表指定关系
 test1.hasMany(test2, {
     foreignKey: "pid",//外键名称
 });
 //子表指定关系
 test2.belongsTo(test1, {
     foreignKey: "pid",//外键名称
 });

По умолчанию в дочернюю таблицу будет добавлена ​​запись внешнего ключа, указывающая на идентификатор основной таблицы. При нормальных обстоятельствах это может соответствовать нормальному использованию. Например, в основную таблицу записывается информация о продукте, а в подтаблице — несколько комментариев.

Пример использования --- пользовательский

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

Советы: Если идентификатор не используется в качестве основного отношения в основной таблице, настраиваемые поля должны добавлять условия, такие как индексы, в качестве отношения в зависимости.

 test1.hasMany(test2, {
     foreignKey: "pid",//外键字段名
     sourceKey: "pid",//主键字段名
 });
 test2.belongsTo(test1, {
     foreignKey: "pid",//关联名
     targetKey:"pid"//自定义外键字段
 });
 //等待主键建立成功再建立子表的外键关系
 setTimeout(() => {
    test2.sync({
        force: true
    });
}, 2500);

Пример использования --- псевдоотношение

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

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

 test1.hasMany(test2, {
     foreignKey: "pid",
     sourceKey: "pid",
     constraints: false //不同步建立外键关系
 });
 test2.belongsTo(test1, {
     foreignKey: "pid",
     targetKey:"pid",
     constraints: false //不同步建立外键关系
 });

Пример

Для фактической части работы вы можете увидеть test.js в github.гитхаб-адрес

работа с одной таблицей

Sequelize возвращает пользовательский объект после возврата результата запроса. Этот объект поддерживает непрерывные операции, а конкретные значения хранятся в значениях данных. Но вы можете быть уверены, что при преобразовании в строку у него не будет никаких свойств Sequelize.

//根据条件查询一条数据
let model = await test1.findOne({
	where:{
		id:5,
		name:"test"
	}
});
//修改其中的name字段的值
model.name="更新";
//保存,会自动update数据库中的值
model.save();

совместное расследование

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

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

//查询主表list的数据
//一条list中的数据对应多条item中的数据
 let data = await models.List.findAll({
	 where:{id:5},//条件,这里jiashe只需查询一条
     include: [{
         model: models.Item,
         as:"items",//返回的对象修改成一个固定的名称
     }]
 });
 let list1=data[0];//返回的第一条数据就是要查询的数据
 let list2=list1.items;//返回子表数据,items是自定义的名称

Суммировать

Приведенное выше введение решило запрос и другие операции в большинстве случаев. И я также считаю, что узкое место действительно встречается, и решение, вероятно, не в Sequelize, или, в основном, проблема не в Sequelize. Например, при наличии большого объема данных работа с подтаблицами требует больше очков знаний.

Когда дело доходит до серверной части, Nodejs все еще находится на стадии разработки. Будем надеяться, что больше интерфейсных пользователей коснутся и узнают об этом. Это не только улучшение меня в процессе развития, но и хорошее оружие для улучшения себя в долгосрочном планировании карьеры.