Примечание редактора. В ежедневной разработке выполняется большое количество операций запросов к базе данных, и появление модели ассоциации должно помочь разработчикам свести к минимуму дублирование усилий. Функция модели ассоциации в ThinkJS всегда хорошо воспринималась всеми, но новые студенты, которые не знакомились с ней, иногда не знают, как ее настроить. Сегодня мы пригласили пользователя ThinkJS @lscho поделиться с нами своими знаниями о моделях ассоциаций, надеясь помочь всем лучше понять модели ассоциаций в ThinkJS.
предисловие
При проектировании баз данных, особенно при проектировании реляционных баз данных, между нашими различными таблицами будут различные ассоциации. В традиционных отраслях, когда количество пользователей ограничено и поддается контролю, мы можем использовать внешние ключи для связывания, снижения затрат на разработку и использовать триггеры продуктов баз данных для достижения согласованности и обновления данных между таблицами и связанными таблицами.
Но в веб-разработке использование внешних ключей неприемлемо. Потому что в случае большого количества параллелизма база данных легко может стать узким местом производительности, ограниченной возможностями ввода-вывода, и не может быть легко масштабирована по горизонтали, и в программе будет много ограничений. Поэтому в веб-разработке связь между каждой таблицей данных обычно реализуется в приложении.
В ThinkJS модель ассоциации может очень хорошо решить эту проблему. Давайте узнаем о применении ассоциативных моделей в ThinkJS.
моделирование сцены
Мы моделируем сценарий с наиболее распространенными отношениями между учениками, классами и обществами.
Создать классную таблицу
CREATE TABLE `thinkjs_class` (
`id` int(10) NOT NULL,
`name` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Создать студенческий стол
CREATE TABLE `thinkjs_student` (
`id` int(10) NOT NULL,
`class_id` int(10) NOT NULL,
`name` varchar(20) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Создать общий стол
CREATE TABLE `thinkjs_club` (
`id` int(10) NOT NULL,
`name` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Далее следуем официальной документации сайтаАссоциативная модельДавайте поговорим о них по порядку.Если вы не знакомы с официальной документацией сайта, рекомендуется сначала прочитать документацию.
один на один
Это легко понять.Во многих случаях таблица содержит слишком много содержимого, и мы разделим ее на две таблицы: одну основную для хранения данных, которые используются чаще, и дополнительную таблицу для хранения данных, которые используются реже.
Мы можем создать расписание для формы учащегося, в котором будет храниться личная информация учащегося для проверки.
CREATE TABLE `thinkjs_student_info` (
`id` int(10) NOT NULL,
`student_id` int(10) NOT NULL,
`sex` varchar(10) NOT NULL,
`age` int(2) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
По отношению к основной таблице внешний ключstudent_id
, так что согласно каноническому именованию мы непосредственноstudent
Просто определите взаимосвязь в файле модели.
// src/model/student.js
module.exports = class extends think.Model {
get relation() {
return {
student_info: think.Model.HAS_ONE
};
}
}
Затем выполняем запрос
// src/controller/student.js
module.exports = class extends think.Controller {
async indexAction() {
const student=await this.model('student').where({id:1}).find();
return this.success(student);
}
}
Вы можете получить данные основной таблицы и связанного с ней приложения
{
"student": {
"id": 1,
"class_id": 1,
"name": "王小明",
"student_info": {
"id": 1,
"student_id": 1,
"sex": "男",
"age": 13
}
}
}
Глядя в консоль видим, что выполняются два запроса
[2018-08-27T23:06:33.760] [41493] [INFO] - SQL: SELECT * FROM `thinkjs_student` WHERE ( `id` = 1 ) LIMIT 1, Time: 12ms
[2018-08-27T23:06:33.764] [41493] [INFO] - SQL: SELECT * FROM `thinkjs_student_info` WHERE ( `student_id` = 1 ), Time: 2ms
Второй запрос выполняется автоматически функцией модели в ThinkJS.
Если мы хотим изменить ключ данных, связанных с результатом запроса, или имя нашей таблицы и имя внешнего ключа не созданы в соответствии со спецификацией. Затем мы можем настроить данные, немного изменив ассоциацию.
// src/model/student.js
module.exports = class extends think.Model {
get relation() {
return {
info:{
type:think.Model.HAS_ONE,
model:'student_info',
fKey:'student_id'
}
}
}
}
Выполните запрос еще раз, вы обнаружите, что ключ данных связанной таблицы в возвращаемых данных сталinfo
.
Конечно, помимо настройки внешних ключей и имен моделей, вы также можете настроить условия запросов, правила сортировки и даже разбиение по страницам. Для получения подробной информации см.model.relation Поддерживаемые параметры.
Один к одному (принадлежащий)
После разговора о первой связи один к одному, давайте поговорим о второй связи один к одному. Приведенное выше отношение «один к одному» заключается в том, что мы ожидаем получить данные связанной таблицы после запроса к основной таблице. То есть первичный ключ основной таблицыthinkjs_student.id
, является внешним ключом к расписаниюthinkjs_student_info.student_id
. Так как же найти данные в другой таблице с помощью внешних ключей? Это еще одно отношение один к одному.
Например, отношения между учениками и классами, из таблицы, которую мы создали выше, мы можем видеть, что в таблице учеников мы передаемthinkjs_student.class_id
ассоциироватьthinkjs_class.id
,мы вstudent
Настройка отношения в модели
// src/model/student.js
module.exports = class extends think.Model {
get relation() {
return {
class: think.Model.BELONG_TO
}
}
}
Соответствующие данные могут быть получены после запроса
{
"student": {
"id": 1,
"class_id": 1,
"name": "王小明",
"class": {
"id": 1,
"name": "三年二班"
}
}
}
Точно так же мы также можем настроить ключ данных, а также имя связанной таблицы, условия запроса и так далее.
один ко многим
Отношение "один ко многим" также легко понять. В классе несколько учеников. Если мы хотим узнать связанную информацию об ученике, когда мы запрашиваем класс, отношение между классом и учеником является отношением "один к -много отношений. В настоящее время, чтобы установить отношение модели, необходимо установить его в модели класса.
// src/model/class.js
module.exports = class extends think.Model {
get relation() {
return {
student:think.Model.HAS_MANY
}
}
}
чтобы получить связанные данные о студентах
{
"id": 1,
"name": "三年二班",
"student": [
{
"id": 1,
"class_id": 1,
"name": "王小明"
},
{
"id": 2,
"class_id": 1,
"name": "陈二狗"
}
]
}
Конечно, мы также можем выполнять пользовательские запросы, настроив параметры.
// src/model/class.js
module.exports = class extends think.Model {
get relation() {
return {
list:{
type:think.Model.HAS_MANY,
model:'student',
fKey: 'class_id',
where:'id>0',
field:'id,name',
limit:10
}
}
}
}
После настройки, давайте проверим. Мы обнаружим, что страница загружается все время. Когда мы откроем консоль, мы обнаружим, что несколько операторов sql выполняются в цикле. Почему это?
Из-за приведенного выше примера один к одному мы сделали это с учеником и классом.BELONG_TO
, и вот мы снова берем класс и студенткуHAS_MANY
, попадая таким образом в бесконечный цикл. Мы можем видеть через официальную документацию веб-сайта, что существуетrelation
может решить эту проблему. Итак, мы поместили приведенную выше модель студента вBELONG_TO
Изменить ассоциацию
// src/model/student.js
module.exports = class extends think.Model {
get relation() {
return {
class: {
type:think.Model.BELONG_TO,
relation:false
}
}
}
}
Таким образом, отношения «один ко многим» модели класса могут обрабатываться нормально. Если мы хотим продолжить использование в студенческой моделиBELONG_TO
Чтобы получить связанные данные таблицы, просто повторно включите их в коде.
// src/controller/student.js
module.exports = class extends think.Controller {
async indexAction() {
const student = await this.model('student').setRelation('class').where({id:2}).find();
return this.success(student);
}
}
Официальная документация сайтаmodel.setRelation(name, value)Существует больше способов временно открывать или закрывать ассоциации.
многие ко многим
Предыдущие «один к одному» и «один ко многим» довольно просты для понимания, но «многие ко многим» немного сбивает с толку. Представьте, что каждый студент может вступить во многие клубы, а клубы также состоят из многих студентов. Отношения между клубами и студентами представляют собой отношения «многие ко многим». В этом случае две таблицы больше не могут завершать связь, и для обработки связи необходимо добавить промежуточную таблицу.
CREATE TABLE `thinkjs_student_club` (
`id` int(10) NOT NULL,
`student_id` int(10) NOT NULL,
`club_id` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Согласно документациимногие ко многимВведение отношения, когда мы связываем клуб в студенческой модели,rModel
промежуточная таблица,rfKey
то естьclub_id
охватывать
// src/model/student.js
module.exports = class extends think.Model {
get relation() {
return {
club:{
type: think.Model.MANY_TO_MANY,
rModel: 'student_club',
rfKey: 'club_id'
}
}
}
}
Если мы хотим связать данные о студентах в клубной модели, нам нужно всего лишь поставитьrfKey
изменить наstudent_id
Вот и все.
Конечно, многие ко многим также страдают от проблем циклической ассоциации. Нам нужно только установить одну из моделейrelation:false
Вот и все.
ассоциативная петля
Мы много раз упоминали о проблеме ассоциативного цикла выше, давайте попробуем понять эту особенность из потока выполнения кода.
существуетthink-modelизстрока 30Видите, в конструкторе будет экземпляр Relation, помещенный вthis[RELATION]
.
RELATION
Это уникальное значение типа Symbol, сгенерированное функцией Symbol, которое следует использовать здесь для реализации роли частных свойств.
затем пропуститьnew Relation()
Что вы сделали, взгляните на модельselect
Давайте посмотрим на метод этого последнего запроса, встрока 576обнаружено, что он выполняетconst data = await this.db().select(options);
После запроса выполняется еще один вызовthis.afterFind
метод. а такжеthis.afterFindМетод вызывает вышеупомянутыйRelation
примерafterFind
методreturn this[RELATION].afterFind(data);
.
Видя здесь, мы почти знаем общий процесс по именованию: то есть после обычного запроса модели мы будем обрабатывать запрос связанной модели. Давайте продолжим отслеживать код, давайте посмотримRelation
изafterFindметод вызывается сноваthis.getRelationData
.this.getRelationDataЗатем начните анализировать то, что мы установили в моделиrelation
свойства, вызываемые через циклparseItemRelation
получить одинPromise
объект, в конечном итоге черезawait Promise.all(promises);
выполнить все.
а такжеparseItemRelationметод, вызываяthis.getRelationInstance
чтобы получить экземпляр и выполнить экземплярgetRelationData
метод и возврат. Итак, вышеthis.getRelationData
методPromise.all
По сути, реализацияthis.getRelationInstance
сгенерированный экземплярgetRelationData
метод.
getRelationInstanceФункция состоит в том, чтобы проанализировать ассоциации моделей, которые мы установили, для создания соответствующих экземпляров. Тогда мы можем посмотреть на соответствующийgetRelationDataметод, который, наконец, выполняет модельselect
метод формирования рекурсивного замкнутого цикла.
Из описания это может показаться сложным, но реализация очень проста и изощренна. После метода запроса модели метод запроса вызывается снова после анализа ассоциации модели. Таким образом, независимо от того, сколько моделей связано друг с другом, их можно запрашивать. Единственное, на что следует обратить внимание, это проблема корреляции, упомянутая выше, если в нашей модели есть проблема корреляции, мы можем пройтиrelation:false
закрыть.
постскриптум
Благодаря приведенной выше практике можно обнаружить, что модель ассоциаций ThinkJS утонченная и мощная, а сложные ассоциации могут быть реализованы с помощью простой конфигурации. и черезsetRelation
Метод динамически открывает и закрывает запрос ассоциации модели, обеспечивая гибкость. Пока мы понимаем взаимосвязь в структуре базы данных и проектируем ее разумно, мы можем сэкономить много работы с запросами к базе данных.
PS: приведенный выше код находится вGitHub.com/Лян Сичэн О/думаю….