ActiveRecord: ORM и типы данных

ActiveRecord and data typesActiveRecord — удивительная библиотека, благодаря которой работа с СУБД становится необычайно лаконичной. Достигается это применением техники ORM (Object-Relational Mapping), шаблона проектирования Active record (Активная запись) и некоторыми другими архитектурными решениями.Меня сразу заинтересовал вопрос, в чём различия между ORM и шаблоном Active record? Хотя подобные вопросы носят скорее философский характер и не мешают применять библиотеку ActiveRecord на практике, мне всегда хотелось иметь более ясное представление о концепциях и технологиях, которые используются в работе. Поэтому, немного побродив по сети, выяснил, что ORM обладает более широким смыслом. Шаблон Active record призван обеспечить взаимодействие с реляционными базами данных посредством ООП. Таблица представляется в виде класса, строка — в виде объекта, поле (атрибут) — в виде свойства объекта. Для реализации CRUD-операций объект содержит соответствующий набор методов. ORM содержит схожие идеи, но вы не обязательно должны привязываться к реляционной модели данных. Другими словами, ORM является объектным представлением источника данных, в качестве которого могут выступать как реляционные СУБД, так и NoSQL-хранилища данных.Таким образом, можно сказать, что библиотека ActiveRecord — это ORM, реализующая шаблон Active record для взаимодействия с реляционными базами данных.Если теперь мы зададимся целью перечислить существующие реляционные СУБД, их количество окажется весьма внушительным. И хотя в их сердце лежит стандарт SQL, каждая СУБД использует свой собственный SQL-диалект. Поскольку ActiveRecord не привязан к какой-то конкретной СУБД, он должен учитывать существующие различия. Это достигается с помощью специального класса — адаптера. Каждый раз, когда мы создаём подключение к базе данных с помощью вызова метода ActiveRecord: Base.establish_connection или указываем конфигурацию для rails-приложения в файле config/database.yml (путь относительно корневого каталога приложения), мы указываем имя адаптера, который будет отвечать за взаимодействие с СУБД. Например так: ActiveRecord: Base.establish_connection adapter: «mysql2», host: «localhost», username: «emerald», password: «dawn», database: «music«В данном случае адаптер называется mysql2. Это означает, что ActiveRecord будет искать вот такой файл: active_record/connection_apdapters/mysql2_adapter.rb в котором подразумевается определение класса Mysql2Adapter. На самом деле, для каждого имени адаптера необходим соответствующий файл и класс. Некоторые адаптеры по умолчанию уже поставляются вместе с ActiveRecord. Если мы из корневого каталога rails-приложения выполним команду bundle open activerecord (как настроить команду bundle open можно посмотреть здесь) и заглянем в lib/active_record/connection_adapters, то увидим файлы для адаптеров sqlite, sqlite3, postgresql, mysql и mysql2. Другие адаптеры для работы с соответствующими СУБД можно поискать среди gem-ов.

Каждый конкретный класс адаптера является наследником класса AbstractAdapter и находится в пространстве имён модуля ActiveRecord: ConnectionAdapters. Помимо этого, ActiveRecord предоставляет собственные типы данных для того, чтобы избежать зависимости от типов данных конкретной СУБД:

* binary* boolean* date* datetime* decimal* float* integer* primary_key* string* text* time* timestamp

Обычно их отображение на типы данных СУБД задаётся в виде метода экземпляра адаптера #native_database_types, который возвращает хэш. Этот метод определяется в соответствующем классе адаптера. Например, для mysql2 нужно смотреть класс ActiveRecord: ConnectionAdapters: AbstractMysqlAdapter в файле lib/active_record/connection_adapters/abstract_mysql_adapter.rb. Интересные нам фрагменты кода выглядят так:

[code language=«ruby» gutter=«false» title=«lib/active_record/connection_adapters/abstract_mysql_adapter.rb»]# Line 113NATIVE_DATABASE_TYPES = {: primary_key => «int (11) DEFAULT NULL auto_increment PRIMARY KEY»,: string => { : name => «varchar», : limit => 255 },: text => { : name => «text» },: integer => { : name => «int», : limit => 4 },: float => { : name => «float» },: decimal => { : name => «decimal» },: datetime => { : name => «datetime» },: timestamp => { : name => «datetime» },: time => { : name => «time» },: date => { : name => «date» },: binary => { : name => «blob» },: boolean => { : name => «tinyint», : limit => 1 }}

# Line 173def native_database_typesNATIVE_DATABASE_TYPESend[/code]В конечном итоге, получить тот же хэш можно непосредственным вызовом метода у объекта адаптера:

ActiveRecord: Base.connection.native_database_types

В общем, если вы вдруг забыли как на самом деле будет выглядеть тип данных boolean в MySQL, вы всегда сможете найти ответ в исходном коде или вызове метода. В Rails 4 — здесь без изменений.

© Habrahabr.ru