Практика модели ассоциации ThinkJS

Node.js задняя часть база данных MySQL GitHub SQL MariaDB ThinkJS
Практика модели ассоциации ThinkJS

Примечание редактора. В ежедневной разработке выполняется большое количество операций запросов к базе данных, и появление модели ассоциации должно помочь разработчикам свести к минимуму дублирование усилий. Функция модели ассоциации в 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/Лян Сичэн О/думаю….