Чтение онлайн

ЖАНРЫ

JavaScript. Подробное руководство, 6-е издание
Шрифт:

Прикладной интерфейс веб-хранилища, определяемый спецификацией «Web Storage» и описанный в разделе 20.1, можно расценивать как простейшую разновидность базы данных, хранящую пары ключ/значение. Но помимо него имеются еще два прикладных интерфейса доступа к клиентским базам данных, которые являются «настоящими» базами данных. Один из них известен как «Web SQL Database» - простая реляционная база данных, поддерживающая простейшие SQL-запросы. Этот прикладной интерфейс реализован в броузерах Chrome, Safari и Opera. Он не реализован в Firefox и IE и, скорее всего, никогда не будет реализован в них. Работа над официальной спецификацией этого прикладного интерфейса была остановлена, и поддержка полноценной базы данных SQL, вероятно, никогда не приобретет статус официального стандарта или неофициальной, но широко поддерживаемой особенности веб-платформы.

В настоящее время все усилия по стандартизации сконцентрированы на другом прикладном интерфейсе к базам данных, известном как IndexedDB. Пока слишком рано описывать детали этого прикладного интерфейса (его описание отсутствует в четвертой части книги), но Firefox 4 и Chrome 11

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

IndexedDB - это объектная, а не реляционная база данных, и она намного проще, чем базы данных, поддерживающие язык запросов SQL. Однако она гораздо мощнее, эффективнее и надежнее, чем веб-хранилище пар ключ/значение, доступное посредством прикладного интерфейса Web Storage. Как и в случае прикладных интерфейсов к веб-хранилищам и файловой системе, доступность базы данных IndexedDB определяется происхождением создавшего ее документа: две веб-страницы с общим происхождением могут обращаться к данным друг друга, но вебстраницы с разным происхождением - нет.

Для каждого происхождения может быть создано произвольное число баз данных IndexedDB. Каждая база данных имеет имя, которое должно быть уникальным для данного происхождения. С точки зрения прикладного интерфейса IndexedDB база данных является простой коллекцией именованных хранилищ объектов. Как следует из этого названия, хранилище объектов хранит объекты (или любые другие значения, которые можно копировать, - смотрите врезку «Структурированные копии» выше). Каждый объект должен иметь ключ, под которым он сохраняется и извлекается из хранилища. Ключи должны быть уникальными - два объекта в одном хранилище не могут иметь одинаковые ключи, - и они должны иметь естественный порядок следования, чтобы их можно было сортировать. Примерами допустимых ключей являются строки, числа и объекты

Date
. База данных IndexedDB может автоматически генерировать уникальные ключи для каждого объекта, добавляемого в базу данных. Однако часто объекты, сохраняемые в хранилище объектов, уже будут иметь свойство, пригодное для использования в качестве ключа. В этом случае при создании хранилища объектов достаточно просто определить «путь к ключу», определяющий это свойство. Концептуально, путь к ключу - это значение, сообщающее базе данных, как извлечь ключ из объекта.

Помимо возможности извлекать объекты из хранилища по значению первичного ключа существует также возможность выполнить поиск по значениям других свойств объекта. Чтобы обеспечить эту возможность, в хранилище объектов можно определить любое количество индексов. (Способность индексировать объекты подчеркивается самим названием «IndexedDB».) Каждый индекс определяет вторичный ключ хранимых объектов. Эти индексы в целом могут быть неуникальными, и одному и тому же ключу может соответствовать множество объектов. Поэтому в операциях обращения к хранилищу объектов с использованием индекса обычно используется курсор, определяющий прикладной интерфейс для извлечения объектов из потока результатов по одному. Курсоры могут также использоваться для обращения к хранилищу объектов с использованием диапазона ключей (или индексов), и прикладной интерфейс IndexedDB включает объект, используемый для описания диапазонов (с верхней и/или с нижней границей, включающих или не включающих границы) ключей.

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

Концепция прикладного интерфейса IndexedDB чрезвычайно проста. Чтобы прочитать или изменить данные, сначала необходимо открыть требуемую базу данных (указав ее имя). Затем создать объект транзакции и с помощью этого объекта отыскать требуемое хранилище объектов в базе данных, также по имени. Наконец, отыскать объект вызовом метода

get
хранилища объектов или сохранить новый объект вызовом метода
put.
(Или вызвать метод
add,
если необходимо избежать затирания существующих объектов.) Если потребуется отыскать объекты по диапазону ключей, нужно создать объект
IDBRange
и передать его методу
openCursor
хранилища объектов. Или, если потребуется выполнить запрос по вторичному ключу, отыскать именованный индекс в хранилище объектов и затем вызвать метод
get
или
openCursor
объекта-индекса.

Однако эта концептуальная простота осложняется тем фактом, что прикладной интерфейс должен быть асинхронным, чтобы веб-приложения могли пользоваться им, не блокируя основной поток выполнения броузера, управляющий пользовательским интерфейсом. (Спецификация IndexedDB определяет синхронную версию прикладного интерфейса для использования в фоновых потоках выполнения, но на момент написания этих строк ни один броузер еще не реализовал эту версию, поэтому она не рассматривается здесь.) Создание транзакции, а также поиск хранилища объектов и индексов являются простыми синхронными операциями. Но открытие базы данных, обновление хранилища объектов с помощью метода

put
и получение хранилища или индекса с помощью метода
get
или
openCursor
являются асинхронными операциями. Все эти асинхронные методы немедленно возвращают объект запроса. В случае успешного или неудачного выполнения запроса броузер генерирует событие «success» или «error» в объекте запроса, которые можно обработать, определив обработчики событий с помощью свойств
onsuccess
и
onerror
. В обработчике
onsuccess
результат операции доступен в виде свойства
result
объекта запроса.

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

onsuccess
. В этом обработчике создается объект транзакции, и затем этот объект используется для поиска хранилища или хранилищ объектов, которые предполагается использовать. После этого производится серия вызовов методов
get
и
put
хранилищ объектов. Они также действуют асинхронно, поэтому непосредственно при их вызове ничего не происходит, но запросы, сгенерированные этими методами
get
и
put,
автоматически будут связаны с объектом транзакции. При необходимости можно отменить все операции в транзакции, ожидающие выполнения, и откатить любые уже выполненные операции вызовом метода
abort
объекта транзакции. Во многих других прикладных интерфейсах к базам данных объект транзакции обычно имеет метод
commit,
подтверждающий транзакцию. Однако в IndexedDB транзакция подтверждается после выхода из обработчика
onsuccess
, создавшего транзакцию, когда броузер вернется в цикл обработки событий, и после выполнения всех операций, запрошенных в транзакции (без запуска новых операций в их функциях обратного вызова). Такая схема, на первый взгляд, кажется слишком сложной, но в практическом применении она очень проста. При использовании прикладного интерфейса IndexedDB программист вынужден создавать объекты транзакций, чтобы получить доступ к хранилищам объектов, но в обычных ситуациях ему даже не приходится задумываться о транзакциях.

Наконец, существует один особый вид транзакций, обеспечивающий возможность работы очень важной части прикладного интерфейса IndexedDB. Создать новую базу данных с использованием интерфейса IndexedDB API очень просто: достаточно выбрать имя и запросить открытие этой базы данных. Но новая база данных создается абсолютно пустой, и она совершенно бесполезна, пока в нее не будет добавлено одно или более хранилищ объектов (и, возможно, нескольких индексов). Создавать хранилища объектов и индексы можно только внутри обработчика события

onsuccess
объекта запроса, возвращаемого методом
setVersion
объекта базы данных. Метод
setVersion
позволяет указать номер версии базы данных - в обычной ситуации номер версии должен изменяться при каждом изменении структуры базы данных. Однако более важно, что метод
setVersion
неявно запускает специальную транзакцию, позволяющую вызвать метод
сreateObjectStore
объекта базы данных и метод
createlndex
хранилища объектов.

Теперь, получив представление о прикладном интерфейсе IndexedDB, вы сможете самостоятельно разобраться в примере 22.15. Этот пример использует IndexedDB для создания базы данных, отображающей почтовые индексы США в названия городов, и выполнения запросов к ней. Он демонстрирует многие, хотя и не все, основные особенности IndexedDB. На момент написания этих строк пример действовал в Firefox 4 и Chrome 11, но из-за того, что спецификация все еще продолжала меняться и реализации находились на предварительной стадии разработки, велика вероятность, что он не будет работать именно так, как описывается здесь, когда вы будете читать эти строки. Однако общая структура примера должна сохранить свою полезность для вас. Пример 22.15 получился достаточно длинным, но в нем имеется большое количество комментариев, которые облегчат его изучение.

Пример 22.15. База данных IndexedDB с почтовыми индексами США

<!DOCTYPE html>

<html>

<head>

<title>Zipcode Database</title>

<script>

// Реализации IndexedDB все еще используют префиксы в именах

var indexedDB = window.indexedDB || // Использовать стандартный API БД

window.mozIndexedDB || // Или раннюю версию в Firefox

window.webkitlndexedDB; // Или раннюю версию в Chrome

// В Firefox не используются префиксы для следующих двух объектов:

var IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction;

Поделиться с друзьями: