Adobe

индекс
116,75

Работаем с SQLite в AIR приложениях



Здравствуйте ценители (и не только) Flash, Flex и AIR.

Сегодня я расскажу как работать с локальной базой данных (SQLite) в AIR приложениях.

Для работы нам понадобится Flex Builder, какой-нибудь, редактор SQLite (я использую приложение для FireFox SQLite Manager) и немного терпения.


Редактор нам нужен для просмотра изменений в файле базы, поскольку AIR не имеет собственного.

Для реализации задуманного нам понадобятся следующие библиотеки:

  1. import flash.data.SQLConnection;       // Необходима для соединения с файлом базы данных (БД)
  2. import flash.data.SQLStatement;        // Необходима для работы с запросами
  3. import flash.data.SQLResult;           // Необходима для обработки результатов запроса
  4. import flash.data.SQLMode;             // Нам не понадобится, а используется для пояснения, для чего мы открываем БД (чтения, записи или обновления)
  5.  
  6. import flash.events.SQLErrorEvent;     // Обрабатывает ошибки подсоединения к БД
  7. import flash.events.SQLEvent;          // Обрабатывает остальные события БД (OPEN, UPDATE и т.д.)
  8.  
  9. import flash.filesystem.File;          // Необходима для указания пути к файлу БД
* This source code was highlighted with Source Code Highlighter.


Как и другие среды работающие с БД SQLite AIR может создавать свои файлы, а может работать уже с готовыми файлами БД.

Рассмотрим вариант с созданием файла БД с нуля.

Для начала нам нужно создать сам файл базы (testDB.sqlite) для этого используем следующую конструкцию:

  1. var dbFile:File = File.desktopDirectory.resolvePath("testDB.sqlite"); // Указываем путь к файлу БД (В нашем случае это рабочий стол)
  2.  
  3. var DBConnection:SQLConnection = new SQLConnection();          // Коннектимся к базе
  4. DBConnection.addEventListener(SQLErrorEvent.ERROR, DBError);   // Добавляем обработчик события возникающего при соединении
  5. DBConnection.addEventListener(SQLEvent.OPEN, DBOpen);          // Добавляем обработчик события возникающего при удачном соединении
  6. DBConnection.open(dbFile);                                     // Собственно инициализируем открытие базы
  7.  
  8. /**
  9. * Эта функция обрабатывает ошибки соединения
  10. */
  11. private function DBError(e:SQLErrorEvent):void
  12. {
  13.   trace("Error message: ", e.error.message);
  14.   trace("Details: ", e.error.details);
  15. }
  16.  
  17. /**
  18. * Эта функция обрабатывает удачное соединение
  19. */
  20. private function DBOpen(e:SQLEvent):void
  21. {
  22.   trace("The database was created successfully");
  23. }
* This source code was highlighted with Source Code Highlighter.


Файл БД у нас уже есть осталось только заполнить его таблицами. Создание таблиц можно привязать к событию успешного создания БД (DBOpen). Далее, мы так и поступим, а пока разберемся из чего будет состоять наша БД (на примере базы которая будут хранить дни рождения). Она будет состоять из двух таблиц:

1. groups — группы в которые будут входить люди, н.п. «Друзья», «Работа», «Семья». В таблице будет два поля (id, name);
2. и собственно peoples с полями (id, lname, fname, ffname, date, group_id)

Теперь создадим, описанные выше, таблицы для этого нам понадобится переменная которая будет содержать запрос к базе:

  1. var GroupsTable:String = "CREATE TABLE IF NOT EXISTS groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT)";
* This source code was highlighted with Source Code Highlighter.


И объект для управления запросами (этот код можно вставить в функцию DBOpen):

  1. var statement:SQLStatement = new SQLStatement();          // Создаем объект
  2. statement.sqlConnection = DBConnection;                   // Указываем базу по отношению к которой будем выполнять запрос
  3. statement.text = GroupsTable;                             // Указываем текст запроса
  4. statement.addEventListener(SQLErrorEvent.ERROR, DBError); // Добавляем обработчик события возникающего при соединении
  5. statement.addEventListener(SQLEvent.RESULT, TableResult); // Добавляем обработчик события возникающего при успешном создании таблицы
  6. statement.execute();                                      // Инициализируем обработку запроса
  7.  
  8. /**
  9. * Эта функция обрабатывает удачное создание таблицы
  10. */
  11. private function TableResult(e:SQLEvent):void
  12. {
  13.   trace("Table created");
  14. }
* This source code was highlighted with Source Code Highlighter.


Теперь создаем вторую таблицу, по аналогии:

  1. var PeoplesTable:String = "CREATE TABLE peoples (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, lname TEXT, fname TEXT, ffname TEXT, date TEXT, group_id INTEGER)";
  2.  
  3. var statement:SQLStatement = new SQLStatement();            // Создаем объект
  4. statement.sqlConnection = DBConnection;                     // Указываем базу по отношению к которой будем выполнять запрос
  5. statement.text = PeoplesTable;                              // Указываем текст запроса
  6. statement.addEventListener(SQLErrorEvent.ERROR, DBError);   // Добавляем обработчик события возникающего при соединении
  7. statement.addEventListener(SQLEvent.RESULT, TableResult);   // Добавляем обработчик события возникающего при успешном создании таблицы
  8. statement.execute();                                        // Инициализируем обработку запроса
* This source code was highlighted with Source Code Highlighter.


Справились. База с таблицами создана и готова к работе. Осталось только заполнить ее данными и научится их извлекать.
Рассмотрим пример добавления информации в таблицу:

  1. var InsertIntoPeoples:String = "INSERT INTO peoples (lname, fname, ffname, date, group_id) VALUES (@column0, @column1, @column2, @column3, @column4)";
  2.  
  3. var LNameArray :Array = ["Иванов", "Сидоров", "Козлов", "Игнатенко", "Борьщев", "Кольченко", "Бузякин"];
  4. var FNameArray :Array = ["Иван", "Николай", "Артем", "Игорь", "Сергей", "Борис", "Алексей"];
  5. var FFNameArray:Array = ["Алексеевич", "Борисович", "Игоревич", "Артемович", "Николаевич", "Иванович", "Сергеевич"];
  6.  
  7. var statement2:SQLStatement = new SQLStatement();
  8. statement2.sqlConnection = DBConnection;
  9. statement2.text = InsertIntoPeoples;
  10. statement2.addEventListener(SQLErrorEvent.ERROR, DBError);
  11. statement2.addEventListener(SQLEvent.RESULT, InsertResult);
  12.  
  13. for(var j:Number = 0; j < 100; j++)
  14. {
  15.   statement2.parameters["@column0"] = LNameArray[Math.round(Math.random() * 6)];
  16.   statement2.parameters["@column1"] = FNameArray[Math.round(Math.random() * 6)];
  17.   statement2.parameters["@column2"] = FFNameArray[Math.round(Math.random() * 6)];
  18.   statement2.parameters["@column3"] = (Math.round(Math.random() * 30) + 1) + ':' + (Math.round(Math.random() * 11) + 1) + ':' + (Math.round(Math.random() * 2009) + 1);
  19.   statement2.parameters["@column4"] = Math.round(Math.random() * 3);
  20.   statement2.execute();
  21. }
  22.  
  23. /**
  24. * Эта функция обрабатывает удачное добавление данных в таблицу
  25. */
  26. private function InsertResult(e:SQLEvent):void
  27. {
  28.   trace("Add to table successfully");
  29. }
* This source code was highlighted with Source Code Highlighter.


В данном примере в конструкции INSERT используется ссылки "@column0" они нужны для упрощения добавления данных при помощи цикла. Это гораздо удобнее чем использовать строку с переменными которую перед каждым добавлением приходится переопределять.

Теперь попробуем извлечь данные из таблицы:

  1. var q:String = "SELECT * FROM groups ORDER BY name";
  2.  
  3. var getGroupStat:SQLStatement = new SQLStatement();
  4. getGroupStat.sqlConnection = DBConnection;
  5. getGroupStat.text = q;
  6. getGroupStat.addEventListener(SQLErrorEvent.ERROR, DBError);
  7. getGroupStat.addEventListener(SQLEvent.RESULT, GetGroupResult);
  8. getGroupStat.execute();
  9.  
  10. /**
  11. * Обрабатывает результат выполнения функции GetGroup().
  12. */
  13. private function GetGroupResult(e:SQLEvent):void
  14. {
  15.   var result:SQLResult = e.target.getResult();
  16.   
  17.   var GroupArray:ArrayCollection = new ArrayCollection();
  18.   
  19.   if(result.data)
  20.     for(var i:Number = 0; i < result.data.length; i++)
  21.       GroupArray.addItem(result.data[i]);
  22. }
* This source code was highlighted with Source Code Highlighter.


В функции GetGroupResult() переменной result присваивается результат запроса SELECT он представляет из себя массив переменных типа Object:

  1. [[object Object],[object Object],[object Object],[object Object]]
* This source code was highlighted with Source Code Highlighter.


Его лучше всего использовать присвоив переменной типа ArrayCollection, для удобного обращения к данным по имени (GroupArray[i].name).
Для удаления и обновления данных используются конструкции подобные тем которые мы рассмотрели отличаются только строковые запросы к базе.

В завершении урока хотелось бы отметить, что если в вашем AIR приложении используется некое хранилище данных то лучше чем SQLite файла не найти, поскольку работа с ним гораздо быстрее чем с файлом TXT или даже XML.

Прилагаю исходник к уроку (проект для Flex Builder 3.2).

P.S. Может кто-нибудь подскажет хорошую подсветку кода для AS3?
Подсказали quickhighlighter.com, спасибо kutu
+24
26 октября 2009, 09:19
52

комментарии (46)

–5
jinzito #
А чем подсветка Flex Bulder-a Вас не устраивает? :)
0
flashguy #
Тем что ее нельзя вставить в блог :)
0
flashguy #
Имеется ввиду подсветка кода для форумов.
+2
FlashPro #
В AS принято только классы называть с заглавной буквы, а методы и свойства с прописной.
+2
flashguy #
Да я знаю, но считаю, что это кому как удобно. У меня свое понимание форматирования удобное на мой взгляд.
+3
flashguy #
В AS так же принято не переносить на новую строку открывающуюся фигурную скобку:

if(result.data){
  for(var i:Number = 0; i < result.data.length; i++){
   GroupArray.addItem(result.data[i]);
  }
}


* This source code was highlighted with Source Code Highlighter.


Мне же удобно использовать переносы, кажется код так гораздо «опрятнее» (видно что из чего растет):

if(result.data)
{
  for(var i:Number = 0; i < result.data.length; i++)
  {
   GroupArray.addItem(result.data[i]);
  }
}


* This source code was highlighted with Source Code Highlighter.
+3
FlashPro #
Насчет форматирование это пожалуй не принципиально. По поводу заглавных и прописных.
GroupArray.addItem может сразу показаться как статичный метод класса GroupArray. Поэтому и придумали эти правила. Но, конечно, кому как удобнее. Но в совместных проектах (или тех где потенциально будет дорабатывать другой программист), думаю лучше придерживаться их.
0
flashguy #
Ну если брать в расчет совместное программирование, то я пожалуй согласен с вами.
0
FirsofMaxim #
форматирование кода — индивидуальная вещь =)
0
Angelina_Joulie #
я бы поспорила.
Вот приходит ко мне код из соседнего подразделения, и что я должна делать? Тратить бюджетные деньги на то, что бы разобраться с «индивидуальными» вещами? Лучше я бы их потратила на то, что бы найти проблемные участли и попивая кофе надеяться на то, что их будет как можно меньше.
0
FirsofMaxim #
понимаю… для этого есть корпоративная стилистика кода, это уже человеку на работе объясняют.
0
fzn7 #
Может уже хватит в хэллоу ворлд с эйром и флексом играть? www.adobe.com/newsletters/edge/october2009/articles/article7/index.html?trackingid=EXBII
0
flashguy #
Вы увидели принципиальное сходство в этой статье с моей?
Плюс вас ведь никто не заставляет читать все статьи на хабре.
–1
fzn7 #
Ответьте пожалуйста для начала на мой вопрос.
+3
flashguy #
Я не считаю свою статью, статьей из серии «Hello World!!!» — это раз.

Эта статья на русском, что во многом поможет тем у кого с этим проблемы (например таким как я) — это два.

Я считаю что в моей статье есть неплохие примеры + исходник, и опять же таки на русском — это три.

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

P.S. Чем вас «оскорбила» данная статья? Тем что вы все знаете? Или же стилем или…
+2
aleks_raiden #
не обращайте внимания. Статья хорошая, по AIR вообще мало чего на русском есть и почти каждый материал на вес золота
+1
Angelina_Joulie #
А вы спрашивайте и вам ответят.
0
fzn7 #
Предлагаю перейти от эмоций к конструктивной критике:

1) Выше были замечания по стилю оформления кода. У нас есть конвенция и неплохо было-бы писать по ней, особенно если пишете обучающие статьи. opensource.adobe.com/wiki/display/flexsdk/Coding+Conventions

2) Для того, что-бы обращаться к индексу массива как arr[i] не нужно создавать коллекцию. Это стандартный синтаксис.

Конкретно по статье больше замечаний нет.
+1
Angelina_Joulie #
Да?
А если вместо:
var GroupArray:ArrayCollection = new ArrayCollection(); //code guidelines говорит о том, что переменные должны начинаться с маленькой буквы.

if(result.data)
for(var i:Number = 0; i < result.data.length; i++) //так же лучше использовать int на небольших объёмах, т.к. быстрее.
{
GroupArray.addItem(result.data[i]);
} //мы помним о том, что хорошие мальчики стараются писать в скобках, даже если это одна строчка.

заменить на
GroupArray.source = result.data; // и что же произойдёт, о великий Гуру в данном случаи?
grid.dataProvider = GroupArray;

зачем цикл-то?
0
flashguy #
Вот вы «прицепились» к регистру в названиях переменных. Скажите есть или какая-нибудь разница в работоспособности программ с переменными с Заглавными буквами?
+1
fzn7 #
Не забудьте сделать Array(result.data) и refresh() коллекции после смены source. Стоит так-же попробовать for each цикл, если гонимся за скоростью.
0
flashguy #
Вот это, я понимаю, конструктивная критика. За это спасибо.
0
flashguy #
Вот это, я понимаю, конструктивная критика. За это спасибо.
0
flashguy #
Очень конструктивно :)

И где в вашем первом комментарии про все это было написано? Я просто наверное не увидел.

Хотя я за то, что у каждого свое мнение и мне это даже нравится (вот такой вот я садамаза), поскольку люблю критику.

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

Пишите еще замечания (но конкретизируйте их).
0
fzn7 #
Вы ниче не путаете? С каких пор коллекции используются для глобального доступа к данным? Коллекция предоставляет вам интерфейс для удобной работы с данными, включая в себя отсылку специфических эвентов, фильты, сортировку и курсор. Просто удобная обертка над массивом не более.
0
flashguy #
Angelina_Joulie причем здесь глобальность данных и коллекции???
Если речь идет о конкретном выражении:

  1. var result:SQLResult = e.target.getResult();
  2.    
  3. var GroupArray:ArrayCollection = new ArrayCollection();
  4.  
  5. if(result.data)
  6. {
  7.   for(var i:Number = 0; i < result.data.length; i++)
  8.   {
  9.     GroupArray.addItem(result.data[i]);
  10.   }
  11. }


И о конкретном объяснении для чего оно такое вообще нужно.
+1
flashguy #
А если вам Adobe напишет в этом топике, что нужно все свои программы им отправлять для проверки на правильность написание имен переменных то вы будете следовать их требованиям?
0
deerares #
Спасибо!
0
nesesser #
А не знает ли кто-нибудь — есть ли уже CMS полностью на Flash? Возможности Flex + Flash уже позволяют подобное, однако я еще не встречал.
0
Angelina_Joulie #
А зачем?
0
flashguy #
Полностью нет (необходимо уточнить что есть полностью). Есть с использованием различных видов специализированных и не очень серверов типа: FMS, RED5 или просто с PHP+MySQL напривер вот этот.
0
kivsiak #
Мне вот другой вопрос интересен в официальных доках в качестве префикса для именованных параметрах используют ":" а не "@". Есть ли какая разница?
0
Angelina_Joulie #
Нет ни какой разницы. Можно использовать и то, и другое.
Сделано это для обратной совместимости запросов.
0
flashguy #
Разницы нет можно использовать и двоеточие и «собаку».
0
Angelina_Joulie #
Вроде бы по делу написали, но при этому совершенно ни чего важного о чём бы стоило писать.

Мы бы лучше написали об асинхронной работе с базой данных и о проблемах конкурентности данных, транзакциях. О том что с базой данных можно работыть in-memory. Обралити внимание на то, что SQL Ligth поддерживает разного рода параметры :paramName и @paramName. Могли бы так же рассказать о том, что можно использовать некое подобие ORM подхода, для сохранения и получения ввиде объектов.

А писать, о том, что import — это ключевое слово, дак ещё и с русскими комментариями — не стоит. Смысла в этом большого нет.
0
flashguy #
А где у меня написано «что import — это ключевое слово»?
0
Angelina_Joulie #
имелось ввиду reserved word, старая привычка осталась после русских книжек.
Сорри.
0
flashguy #
Да все нормально. Иногда приятно иметь дело с человеком который всегда с чем-то не согласен, так сказать, имеет к тебе противоречие. Согласись так жить веселее ;) И сразу стремишься к чему-то лучшему.
0
Castro #
Видно, Вы человек знающий. Предлагаю Вам написать статью на такие темы. Было бы любопытно
0
vladsm #
Аццкий ад, бессмысленный и беспощадный…
0
flashguy #
И что же тут бессмысленного и беспощадного?
–1
vladsm #
Да смысла нету в таком «обрезке» подачи информации…

«…
# import flash.events.SQLErrorEvent; // Обрабатывает ошибки подсоединения к БД
# import flash.events.SQLEvent; // Обрабатывает остальные события БД (OPEN, UPDATE и т.д.)
…»


Это *Event-то что-то там обрабатывает? :)

Как создать файл с базой данных? Какие параметры при этом используются? Можно ли защитить данные в базе от чужого глаза?
А как подключиться к уже созданной базе?
Синхроннный/асинхронный режимы работы? Преимущества/недостатки?
0
flashguy #
Если для вас нет в чем-то смысла — это не значит что его нет для всех.
0
flashguy #
А про «преимущество и недостатки синхронного и ...» могли бы и сами написать в своем топике. Если уж вы все знаете.
0
vladsm #
Ну это же не я, а вы взялись писать пост, рассказывающий о том, как работать с локальной базой данных (SQLite) в AIR приложениях…
0
vladsm #
Ок.
Какие «ошибки подсоединения» обрабатывает SQLErrorEvent?
Какие «остальные события БД» обрабатывает SQLEvent?

Поскольку база у нас открыта в синхронном режиме (DBConnection.open(…)), должен ли я прописывать каждому стейтменту хендлер события SQLEvent.RESULT или же у меня результат и без этого будет доступен через statement.getResult() сразу же после вызова statement.execute(…)?

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.