Содержание
Tiles sqlite можно ли удалить
Можно ли безопасно удалить файл sqlite WAL?
У меня есть странная проблема с Core Data в приложении iOS, где иногда файл WAL становится огромным (
1 ГБ). Похоже, что есть и другие люди с этой проблемой (например, Core Data sqlite-Wal файл получает MASSIVE (>7GB) при вставке
Моя первоначальная мысль-удалить файл WAL при запуске приложения. Судя по тому, что я читал документацию sqlite по этому вопросу, это будет нормально. Но знает ли кто-нибудь о каких-либо недостатках в этом деле?
Я, конечно, хотел бы добраться до сути, почему файл WAL становится таким большим, но я не могу добраться до сути прямо сейчас и хочу найти обходной путь, пока я копаю глубже в проблему.
Стоит отметить, что моя база данных Core Data-это скорее кэш. Так что не имеет значения, если я потеряю данные, которые находятся в WAL. Мне действительно нужно знать, будет ли база данных полностью повреждена, если я удалю WAL? Мое подозрение-нет, иначе WAL не служит ни одной из его целей.
Режим WAL имеет проблемы, не используйте его. Проблемы различаются, но очень большой размер вашего отчета-один, другие проблемы включают сбой при миграции (с использованием NSPersistentStoreCoordinators migratePersistentStore) и сбой при импорте журналов транзакций iCloud. Так что, хотя есть сообщаемые преимущества, пока эти ошибки не будут исправлены, вероятно, неразумно использовать режим WAL.
И NO вы не можете удалить журнал опережающей записи, потому что он содержит самые последние данные.
Установите базу данных в режим журнала отката, и я думаю, что вы больше не будете иметь этих очень больших файлов При загрузке большого количества данных.
Вот выдержка, которая объясняет, как работает WAL. Если вы не можете гарантировать, что ваше приложение выполнило контрольную точку, я не вижу, как вы можете удалить файл WAL без риска удаления совершенных транзакций.
Как работает WAL
Традиционный журнал отката работает путем написания копии исходное неизмененное содержимое базы данных в отдельный журнал отката файл, а затем запись изменений непосредственно в файл базы данных. В событие аварии или ROLLBACK, исходное содержимое, содержащееся в журнал отката воспроизводится обратно в файл базы данных, чтобы вернуть файл базы данных в исходное состояние. COMMIT возникает, когда журнал отката удаляется.
Подход WAL инвертирует это. Оригинальное содержание сохраняется в файл базы данных и изменения добавляются в отдельный WAL файл. Фиксация происходит, когда специальная запись, указывающая на фиксацию, является приложено к WAL. Таким образом, a COMMIT может произойти, даже не написав кому-либо исходная база данных, которая позволяет читателям продолжать работать с исходная неизмененная база данных при одновременном внесении изменений совершено в WAL году. Несколько транзакций могут быть добавлены к конец одного файла WAL.
Копирование
Конечно, человек хочет в конечном итоге передать все транзакции, которые добавляются в файле WAL обратно в исходную базу данных. Движущийся транзакции файла WAL обратно в базу данных называются a «checkpoint».
Еще один способ задуматься о разнице между откатом и журнал опережающей записи заключается в том, что в подходе отката-журнала существуют две примитивные операции, чтение и письмо, в то время как с запись вперед журнала теперь есть три примитивные операции: чтение, написание и контрольные точки.
По умолчанию SQLite выполняет контрольную точку автоматически, когда файл WAL достигает порогового размера в 1000 страниц. (Этот Параметр времени компиляции SQLITE_DEFAULT_WAL_AUTOCHECKPOINT можно использовать для укажите другое значение по умолчанию.) Приложения, использующие WAL, не должны делать все что угодно для того, чтобы эти контрольные точки произошли. Но если они захотят . чтобы приложения могли настроить порог автоматической контрольной точки. Или они могут отключить автоматические контрольно-пропускные пункты и запускать контрольные точки во время моменты простоя или в отдельном потоке или процессе.
Я видел довольно много негативных сообщений о WAL в iOS 7. Мне пришлось отключить его на нескольких проектах, пока у меня не было времени изучить эти проблемы более тщательно.
Я бы не стал удалять файл журнала, но вы можете играть с возможностью очистки файла SQLite, который вызовет SQLite к «consume» файлу журнала. Вы можете сделать это, добавив NSSQLiteManualVacuumOption как часть опций, когда вы добавляете NSPersistentStore к NSPersistentStoreCoordinator .
Если это в конечном итоге отнимает много времени, то я бы предложил отключить WAL. Я не видел никаких вредных последствий для его отключения (пока).
Конечно, вы можете удалить файл WAL. Вы потеряете все совершенные транзакции,которые не были возвращены в основной файл. (Таким образом нарушается «durability» часть ACID, но, возможно, Вам все равно.)
Вы можете управлять размером файла WAL на диске с помощью pragma journal_size_limit (если это вас беспокоит). Возможно, вы также захотите чаще проверять вручную контрольную точку. Смотрите «Avoiding Excessively Large WAL files» здесь: https://www.sqlite.org/wal.html
Мне не нравится вся эта суеверная трепка режима WAL. Режим WAL является более быстрым, параллельным и гораздо более простым, поскольку он избавляет от всех махинаций уровня блокировки (и большинства проблем «database is busy»), которые идут с журналами отката. Режим WAL-это правильный выбор практически в любой ситуации. (Единственное место, где это проблематично, — это файловые системы flash, которые не поддерживают доступ к файлам, сопоставленным с общей памятью. В этом случае директива компиляции «unofficial» SQLITE_SHM_DIRECTORY может использоваться для перемещения файла .shm в другой тип файловой системы — например tmpfs — но это не должно быть проблемой на iOS.)
В этой теме есть довольно хорошие ответы, но я добавляю этот ответ для ссылки на официальный сайт Apple QnA о режиме ведения журнала в iOS7 Core Data: https://developer.apple.com/library/ios/qa/qa1809/_index.html
Они дают различные решения:
Для безопасного резервного копирования и восстановления хранилища Core Data SQLite можно выполнить следующие действия: следующий:
Используйте следующий метод класса NSPersistentStoreCoordinator, а не чем файловая система APIs, чтобы создать резервную копию и восстановить хранилище Core Data:
Обратите внимание, что это тот вариант, который мы рекомендуем.
Переход в режим отката ведения журнала при добавлении хранилища в постоянный координатор хранилища, если вам нужно скопировать файл хранилища. В листинге 1 приведен код, показывающий, как это сделать:
Листинг 1 Используйте режим отката ведения журнала при добавлении постоянного хранилища
Для хранилища, загруженного в режиме WAL, если как основной файл хранилища, так и соответствующий файл-wal существует, используя режим отката журнала для добавления хранилища в постоянный координатор магазине заставит Core Data для выполнения контрольно-пропускном пункте операция, которая объединяет данные из файла-wal в файл хранилища. Это фактически Core Data способ выполнения операции контрольной точки. С другой стороны, если файл-wal отсутствует, то с помощью этого подход к добавлению магазина не вызовет никаких исключений, но транзакции, записанные в файле missing-wal, будут потеряны.
ОЧЕНЬ ВАЖНАЯ ПРАВКА
Если некоторые из ваших пользователей находятся на iOS 8.1 и вы выбрали первое решение (то, которое рекомендует Apple), обратите внимание, что их отношения с данными many-to-many будут полностью отброшены. Потерянный. Удаленный. Во всей перенесенной базе данных.
Это неприятная ошибка, очевидно, исправленная в iOS 8.2 . Более подробная информация здесь http://mjtsai.com/blog/2014/11/22/core-data-relationships-data-loss-bug/
Вы никогда не должны удалять файл sqlite WAL, он содержит транзакции, которые еще не были записаны в фактический файл sqlite. Вместо этого заставьте базу данных выполнить контрольную точку, а затем очистите файл WAL для вас.
В CoreData лучший способ сделать это-открыть базу данных с помощью pragma DELETE journal mode. Это будет контрольная точка, а затем удалить файл WAL для вас.
Для сохранения здравого смысла вы должны убедиться, что у вас есть только одно соединение с постоянным магазином, когда вы это делаете, т. е. только один экземпляр постоянного хранилища в одном координаторе постоянного хранилища.
FWIW в вашем конкретном случае вы можете использовать TRUNCATE или OFF для первоначального импорта базы данных и переключиться на WAL для обновления.
Похожие вопросы:
Текущая ситуация Таким образом, у меня есть архивация WAL, настроенная на независимый внутренний жесткий диск на компьютере регистрации данных, работающем под управлением Postgres. Жесткий диск.
Мы выполняем довольно большие транзакции в базе данных SQLite, что приводит к тому, что файл WAL становится чрезвычайно большим. (Иногда до 1 ГБ для крупных транзакций.) Есть ли способ проверить.
Я использую c# с NHibernate на SQLite. System.Data.SQLite версия 1.0.88 SQLite версия 3.7.17 Я недавно узнал о режиме SQLite WAL , и это похоже на то, что мне нужно. Проблема в том, что я не могу.
Если база данных SQLite, использующая ведение журнала с опережением записи, прерывается с непроверенными транзакциями (из-за сбоя питания или чего-то еще), а затем снова открывается с отсутствующим.
Какие существуют клиенты приложений, которые могут подключать базы данных SQLite3, которые имеют ведение журнала с опережением записи (WAL)? Я не нахожу его с моим google foo сегодня, у меня есть.
С включенным параметром WAL (Write-Ahead-Logging) в SQLite 3.7 (который используется по умолчанию для основных данных на iOS 7), Как я могу объединить/зафиксировать содержимое из файла-wal обратно в.
В документации SQLite по функции write-ahead-log, представленной в версии 3.7, есть некоторые комментарии, которые меня немного смутили. На связанной странице говорится: синхронизация содержимого с.
Я беру резервную копию SQLite DB с помощью CP commmand после запуска wal_checkpoint (FULL). DB используется в режиме WAL, поэтому в моей папке есть и другие файлы, такие как-shm и-wal. Когда я.
У меня есть много проблем с резервным копированием/восстановлением базы данных на этих нескольких устройствах Android, которые имеют WAL (запись вперед) включен. Можно ли отключить это после.
Пожалуйста, будьте терпеливы со мной — я постараюсь использовать правильную терминологию в этом вопросе. У меня есть приложение iOS, которое позволило пользователям создавать резервные копии своих.
What can be done about the fact that Android automatically deletes corrupt SQLite files?
When Android opens an SQLite file, and the file is corrupt, Android deletes the file.
As surprising as it may sound, this behavior is implemented clearly in the Android source code, leading to consternation and to this Android issue.
Anyway, as app developers we just have to deal with it. What is the best strategy when opening an SQLite file?
- Corrupt files are actually often recoverable, so we can’t afford to take any risk of losing one of those corrupt files.
- Creating a backup before opening is very time-costly, and would make the app startup really slow, so anything smarter would be greatly appreciated.
The issue has been fixed starting from API level 11. Now there exists an interface: DatabaseErrorHandler which you can implement to define your own onCorruption() method. At the opening of your database you can pass this DatabaseErrorHandler as a parameter to the constructor of SQLiteOpenHelper.
For Systems with an API level below 11 and for those who dont want to use this approach there are several alternatives.
1. Android data backup
Android offers a backup service which automatically copys the application data to a remote ‘cloud’ storage. If a database gets corrupted or the application is reinstalled after factory reset. The application data can be restored from the remote data.
2. JDBC (sqldroid)
One approach could be implementing your own database connector, either native JDBC or with the sqldroid library. It is officially not supported by google and you cannot be sure whether it will be still available in future Android versions.
3. Berkley DB Java Edition
An interesting approach, also with a look to performance handling large data amounts, is the Berkley DB Java Edition.
4. Customizing the android libraries
Another more risky approach is to implement your own database class by copying or extending the SQLiteDatabase.java from the android source and reimplement or override the critical parts which are:
The dangerous part about that is, that you also would have to reimplement the helper classes that access the SQLiteDatabase such as SQLiteOpenHelper . Since the SQLiteDatabase class uses factory methods you could face unexpected side effects.
Создан 14 окт. 11 2011-10-14 09:04:01 Dyonisos
Google should indeed fix this nihilist behavior, but I am afraid it is already too late. There ARE some phones around with this behavior, so we have to make sure we don’t upset those phone’s users. – Nicolas Raoul 14 окт. 11 2011-10-14 09:07:17
Right! In this case I see only the approach with JDBC or to add the native sqlite source as jni library 😉 – Dyonisos 14 окт. 11 2011-10-14 09:11:17
Thanks a lot for the sqldroid link, I did not know it. Actually some months ago I ported «SQLJet» to Android to circumvent a different issue. It is an open source Java SQLite client. – Nicolas Raoul 14 окт. 11 2011-10-14 09:19:59
About BerkeleyDB: The app is for *.anki files, which are actually SQLite databases. That’s the standard file format for several applications on PC/Mac/Linux/ios/Android, I can’t change it. – Nicolas Raoul 14 окт. 11 2011-10-14 15:52:21
I looked at sqldroid, and if I am not mistaken it is a JDBC wrapper over Android’s SQLite implementation, so I guess it has the same issue? – Nicolas Raoul 17 окт. 11 2011-10-17 04:47:13
A simple solution to this problem would be to replicate the DB entirely.
For example in the on Destroy method of your app. Copy the DB on every Destroy, when the main db is corrupted (and deleted by android) you can switch to the backup db.
Создан 14 окт. 11 2011-10-14 08:37:17 Michele
My app is actually the open source AnkiDroid. The typical user has a dozen of *.anki files, each file can be 40 MB or even more. The total size can easily reach 100 MB, which takes time to backup. Is it still acceptable to do this in the «onDestroy» method? – Nicolas Raoul 14 окт. 11 2011-10-14 09:32:55
@NicolasRaoul oh ok, thats alot, i think its safer to do some backup with in an background task. You have to find a balance between data safeness and time/battery consumption on the phone. – Michele 14 окт. 11 2011-10-14 11:13:05
What do you refer to with «onDestroy method of your app» ? You mean the onDestroy method in an activity? That might mean a lot of backups (at an undetermined point in time — since the call for onDestroy may take some time). And as far as I know the Application class itself has no «onDestroy» method — does it? – AgentKnopf 07 дек. 12 2012-12-07 08:52:43
thats right, onDestroy of your MainActivity with an Additional Timestamp in the SharedPreferences could work. So you Backup only ones a day or a week. OnStop would probably be better because apps can last very long in ram. – Michele 07 дек. 12 2012-12-07 11:15:55
@Michele That’s indeed an option 🙂 – AgentKnopf 17 дек. 12 2012-12-17 12:43:42
I faced the same issue and asked a question about it here. I regularly backup my database to the SD-card, but I cannot recommend it. It seems as if a database that is copied from SD-cards used in newer Android phones is considered corrupt after the copy is completed on the older versions of SQLite that is still used on android 2.3.6.
If your database is small enough then I would recommend keeping a backup, but keep it on the internal memory. Unless it would anger your users, do not enable the «install to sd-card»-option, I believe it is correlated to the issue. After these precautions your database should be relatively safe.
About the slower start time: I do my backups in a background thread when the app is closed, this is the most likely time that the phone has to do some background work without troubling the user.
As I wrote (with more details) in my first comment to Michele, it can be 100MB or more. In this conditions, is it acceptable to backup in a background thread when the app is closed? – Nicolas Raoul 14 окт. 11 2011-10-14 09:36:51
Unfortunately not. Then I would have to go with Dyonisos’ answer even though it obviously have other drawbacks to stop using built in libraries (i.e. if they roll out a new and improved SQLiteDatabase-class you will have to release a new update to start using it). – pgsandstrom 14 окт. 11 2011-10-14 12:26:35
Not for at opening every time , But I think whenever our database make a changes or upgrades at that time make copy of DB files for back-up is the one of the solutions .
Also if possible to use SQLite source and modifies it and use it in our application with JNI or Library , then we can achieve it.
Создан 14 окт. 11 2011-10-14 08:47:32 user370305
Indeed, forking Android’s SQLite client could be an option. This is in the same spirit as using alternative Android SQLite client libraries SQLdroid or SQLJet (Dyonisos’ answer). – Nicolas Raoul 14 окт. 11 2011-10-14 09:40:34
What can be done about the fact that Android automatically deletes corrupt SQLite files?
When Android opens an SQLite file, and the file is corrupt, Android deletes the file.
As surprising as it may sound, this behavior is implemented clearly in the Android source code, leading to consternation and to this Android issue.
Anyway, as app developers we just have to deal with it. What is the best strategy when opening an SQLite file?
- Corrupt files are actually often recoverable, so we can’t afford to take any risk of losing one of those corrupt files.
- Creating a backup before opening is very time-costly, and would make the app startup really slow, so anything smarter would be greatly appreciated.
The issue has been fixed starting from API level 11. Now there exists an interface: DatabaseErrorHandler which you can implement to define your own onCorruption() method. At the opening of your database you can pass this DatabaseErrorHandler as a parameter to the constructor of SQLiteOpenHelper.
For Systems with an API level below 11 and for those who dont want to use this approach there are several alternatives.
1. Android data backup
Android offers a backup service which automatically copys the application data to a remote ‘cloud’ storage. If a database gets corrupted or the application is reinstalled after factory reset. The application data can be restored from the remote data.
2. JDBC (sqldroid)
One approach could be implementing your own database connector, either native JDBC or with the sqldroid library. It is officially not supported by google and you cannot be sure whether it will be still available in future Android versions.
3. Berkley DB Java Edition
An interesting approach, also with a look to performance handling large data amounts, is the Berkley DB Java Edition.
4. Customizing the android libraries
Another more risky approach is to implement your own database class by copying or extending the SQLiteDatabase.java from the android source and reimplement or override the critical parts which are:
The dangerous part about that is, that you also would have to reimplement the helper classes that access the SQLiteDatabase such as SQLiteOpenHelper . Since the SQLiteDatabase class uses factory methods you could face unexpected side effects.
Создан 14 окт. 11 2011-10-14 09:04:01 Dyonisos
Google should indeed fix this nihilist behavior, but I am afraid it is already too late. There ARE some phones around with this behavior, so we have to make sure we don’t upset those phone’s users. – Nicolas Raoul 14 окт. 11 2011-10-14 09:07:17
Right! In this case I see only the approach with JDBC or to add the native sqlite source as jni library 😉 – Dyonisos 14 окт. 11 2011-10-14 09:11:17
Thanks a lot for the sqldroid link, I did not know it. Actually some months ago I ported «SQLJet» to Android to circumvent a different issue. It is an open source Java SQLite client. – Nicolas Raoul 14 окт. 11 2011-10-14 09:19:59
About BerkeleyDB: The app is for *.anki files, which are actually SQLite databases. That’s the standard file format for several applications on PC/Mac/Linux/ios/Android, I can’t change it. – Nicolas Raoul 14 окт. 11 2011-10-14 15:52:21
I looked at sqldroid, and if I am not mistaken it is a JDBC wrapper over Android’s SQLite implementation, so I guess it has the same issue? – Nicolas Raoul 17 окт. 11 2011-10-17 04:47:13
A simple solution to this problem would be to replicate the DB entirely.
For example in the on Destroy method of your app. Copy the DB on every Destroy, when the main db is corrupted (and deleted by android) you can switch to the backup db.
Создан 14 окт. 11 2011-10-14 08:37:17 Michele
My app is actually the open source AnkiDroid. The typical user has a dozen of *.anki files, each file can be 40 MB or even more. The total size can easily reach 100 MB, which takes time to backup. Is it still acceptable to do this in the «onDestroy» method? – Nicolas Raoul 14 окт. 11 2011-10-14 09:32:55
@NicolasRaoul oh ok, thats alot, i think its safer to do some backup with in an background task. You have to find a balance between data safeness and time/battery consumption on the phone. – Michele 14 окт. 11 2011-10-14 11:13:05
What do you refer to with «onDestroy method of your app» ? You mean the onDestroy method in an activity? That might mean a lot of backups (at an undetermined point in time — since the call for onDestroy may take some time). And as far as I know the Application class itself has no «onDestroy» method — does it? – AgentKnopf 07 дек. 12 2012-12-07 08:52:43
thats right, onDestroy of your MainActivity with an Additional Timestamp in the SharedPreferences could work. So you Backup only ones a day or a week. OnStop would probably be better because apps can last very long in ram. – Michele 07 дек. 12 2012-12-07 11:15:55
@Michele That’s indeed an option 🙂 – AgentKnopf 17 дек. 12 2012-12-17 12:43:42
I faced the same issue and asked a question about it here. I regularly backup my database to the SD-card, but I cannot recommend it. It seems as if a database that is copied from SD-cards used in newer Android phones is considered corrupt after the copy is completed on the older versions of SQLite that is still used on android 2.3.6.
If your database is small enough then I would recommend keeping a backup, but keep it on the internal memory. Unless it would anger your users, do not enable the «install to sd-card»-option, I believe it is correlated to the issue. After these precautions your database should be relatively safe.
About the slower start time: I do my backups in a background thread when the app is closed, this is the most likely time that the phone has to do some background work without troubling the user.
As I wrote (with more details) in my first comment to Michele, it can be 100MB or more. In this conditions, is it acceptable to backup in a background thread when the app is closed? – Nicolas Raoul 14 окт. 11 2011-10-14 09:36:51
Unfortunately not. Then I would have to go with Dyonisos’ answer even though it obviously have other drawbacks to stop using built in libraries (i.e. if they roll out a new and improved SQLiteDatabase-class you will have to release a new update to start using it). – pgsandstrom 14 окт. 11 2011-10-14 12:26:35
Not for at opening every time , But I think whenever our database make a changes or upgrades at that time make copy of DB files for back-up is the one of the solutions .
Also if possible to use SQLite source and modifies it and use it in our application with JNI or Library , then we can achieve it.
Создан 14 окт. 11 2011-10-14 08:47:32 user370305
Indeed, forking Android’s SQLite client could be an option. This is in the same spirit as using alternative Android SQLite client libraries SQLdroid or SQLJet (Dyonisos’ answer). – Nicolas Raoul 14 окт. 11 2011-10-14 09:40:34
Использование простой базы данных SQLite в Android-приложении
В этом руководстве я подробно расскажу о том, как использовать базу данных Android SQLite .
Что такое SQLite
SQLite — это система управления реляционными базами данных, похожая на Oracle , MySQL , PostgreSQL и SQL Server . Она реализует большую часть стандарта SQL , но в отличие от четырех упомянутых выше СУБД она не поддерживает модель « клиент-сервер ». Скорее, она встроена в конечную программу. Это означает, что можно связать базу данных SQLite с приложением и получить доступ ко всем возможностям БД в своем приложении.
Данная СУБД совместима как с Android , так и с iOS , и каждое приложение может создавать и использовать базу данных SQLite . В Android контакты и медиа хранятся и ссылаются на БД SQLite . Она является наиболее используемой СУБД в мире и самым распространенным программным обеспечением . Чтобы узнать о базах данных SQLite как можно больше, посетите официальный сайт SQLite .
Подготовка
Чтобы включить привязку данных в приложении, нужно добавить в файл build.gradle следующий код:
Чтобы использовать как RecyclerView , так и CardView для отображения списков, нужно включить соответствующие библиотеки в разделе зависимостей в файле build.gradle :
Чтобы задействовать все возможности базы данных SQLite , лучше изучить синтаксис SQL .
Описание примера приложения
В нашем Android SQLite примере мы создадим две таблицы: Employer и Employee . Таблица Employee будет содержать ссылку на внешний ключ таблицы Employer . Мы рассмотрим, как вставлять, выбирать, обновлять и удалять строки из таблиц. Я также продемонстрирую, как вывести элементы, выбранные из базы данных SQLite в RecyclerView ( список ) и в Spinner .
У нас есть MainActivity , из которого можно перейти к EmployerActivity ( для работы с таблицей Employer ) или к EmployeeActivity ( для работы с таблицей Employee ):
Классы хранения базы данных SQLite
Классы определяют то, как данные хранятся в базе. SQLite сохраняют значения с помощью пяти доступных классов хранения:
- NULL — нулевое значение;
- INTEGER — для целых чисел, содержащих от 1 до 8 байтов;
- REAL — числа с плавающей запятой;
- TEXT — текстовые строки, хранящиеся с использованием кодировки базы данных ( UTF-8 или UTF-16 );
- BLOB — двоичные данные, хранящиеся точно так, как они были введены.
Определение таблиц
Поскольку база данных SQLite является локальной, нужно обеспечить, чтобы приложение создавало таблицы и по мере необходимости сбрасывало их.
Начнем с Android SQLite query создания таблицы Employer , а затем перейдем к EmployerActivity .
Рекомендуется размещать логику создания базы х SQLite в классе. Это облегчает устранение возможных неполадок. Назовем наш класс SampleDBContract :
Мы определяем частный конструктор для SampleDBContract , а затем создаем класс для представления таблицы Employer . Обратите внимание: класс Employer реализует интерфейс BaseColumns . Он предоставляет два столбца в нашей таблице. Это столбец _ID , который будет автоматически увеличиваться при добавлении каждой новой строки. И столбец _COUNT , который может использоваться ContentProviders для возврата количества записей, извлекаемых через запрос. Столбец _COUNT не является обязательным. Строка CREATE_TABLE компилируется в следующий оператор SQL :
На данный момент в нашем Android SQLite примере мы определили схему таблицы Employer .
Создание базы данных с помощью SQLiteOpenHelper
Самый простой способ управления созданием базы данных и версиями — создать подкласс SQLiteOpenHelper . Он упрощает управление базой данных SQLite , создавая БД, если они не существуют. Необходимо только переопределить методы onCreate() и onUpgrade() , чтобы указать нужное действие для создания или обновления базы данных:
Теперь в нашем примере Android database SQLite задаем для нашей базы данных SQLite имя ( sample_database ). Конструктор вызывает конструктор суперкласса с именем и версией базы данных. В onCreate мы указываем объекту SQLiteDatabase выполнить оператор Employer CREATE_TABLE SQL . Через onUpgrade мы сбрасываем таблицу Employer и создаем ее снова:
Таблица Employer имеет три столбца: name , description и founded_date . Нажатие кнопки сохранения вызывает метод saveToDB() :
В saveToDB() мы получаем ссылку на объект SQLiteDatabase , используя метод getWritableDatabase() из SQLiteOpenHelper . Этот метод создает базу данных, если она еще не существует, или открывает ее, если она уже создана. GetWritableDatabase возвращает объект SQLiteDatabase , который открывает доступ на чтение / запись:
В приведенном выше фрагменте кода есть четыре момента:
- Мы получаем объект SQLiteDatabase , который открывает доступ на запись в базу данных;
- Значения, которые будут храниться в базе данных, помещаются в объект ContentValue с именем столбца в качестве ключа;
- Мы помещаем Date в объект ContentValue , который переводится в класс хранения данных Android SQLite INTEGER ;
- При вставке строки в базу данных с помощью метода database.insert() возвращается идентификатор строки.
Выбор данных из базы данных SQLite
Подобно тому, как мы применили метод getWritableDatabase() , можно вызвать getReadableDatabase() объекта SQLiteOpenHelper для получения объекта SQLiteDatabase , который можно использовать для чтения информации из базы данных. Стоит отметить, что объект SQLiteDatabase , возвращаемый getReadableDatabase() , предоставляет собой тот же самый доступ на чтение / запись в базу данных, который был возвращен функцией getWritableDatabase() , за исключением тех случаев, когда существуют определенные ограничения. Например, файловая система, содержащая заполненную базу данных, и база данных может быть открыта только для чтения.
Метод readFromDB будет запрашивать БД, и возвращать все строки из таблицы Employer , в которых имя или описание из таблицы Employer совпадает со значением, введенным в EditText . А также строки, в которых дата основания компании совпадает со значением, введенным в EditText :
В коде Android SQLite query , приведенного выше, projection является массивом String , представляющим столбцы, которые мы хотим получить. selection является строковым представлением условия SQL WHERE , отформатированным таким образом, что символ ‘?’ будет заменен аргументами в массиве selectionArgs String . Вы также можете группировать, фильтровать и сортировать результаты запроса. Вставка данных в базу SQLite с использованием описанного выше метода защищает от SQL-инъекций .
Обратите внимание на объект, возвращаемый запросом — Cursor . В следующем разделе мы покажем, как вывести содержимое Cursor с помощью RecyclerView .
Отображение содержимого объекта Cursor в RecyclerView
Cursor предоставляет произвольный доступ к набору результатов, возвращаемому запросом к базе данных. Это означает, что через Cursor можно получить доступ к значениям в любом месте, подобно Java-спискам или массивам. Благодаря этому приему можно реализовать RecyclerView с использованием Cursor так же, как мы реализуем RecyclerView с помощью ArrayLists . Вместо вызова List.get(i) , вы перемещаете Cursor в нужную позицию, используя moveToPosition() . После этого вызываете соответствующий метод getXXX(int columnIndex) , где XXX — это Blob , Double , Float , Int , Long , Short или String .
Чтобы не беспокоиться о корректных индексах столбцов из метода readFromDB() , примененного выше, мы используем метод getColumnIndexOrThrow() , который извлекает индекс указанного столбца или генерирует исключение, если имя столбца не существует внутри объекта Cursor :
Определение внешних ключей
На данный момент в нашем Android SQLite примере мы создали таблицу Employer , которую заполнили строками. Теперь создадим таблицу Employee , которая связана с таблицей Employer через столбец _ID Employer . Мы определяем класс Employee , который расширяет BaseColumns в классе SampleDBContract . Обратите внимание, что при создании таблицы Employee использовали « FOREIGN KEY(employer_id) REFERENCES employer(_id) «:
Обновление SQLiteOpenHelper
На данный момент в Android Studio SQLite у вас должна быть создана таблица Employer и в нее добавлены значения. Если вы не изменяете версию базы данных, новая таблица Employee не будет создана. К сожалению, если вы измените версию через повторный вызов метода onUpgrade() , то таблица Employer будет сброшена. Чтобы предотвратить это, можно закомментировать или удалить оператор drop в методе onUpgrade() и добавить оператор execSQL() для создания таблицы Employee . Поскольку таблица Employee ссылается на таблицу Employer , сначала необходимо создать таблицу Employer :
Отображение данных из запроса SQLite в Spinner
Чтобы создать работника в таблице Employee , пользователю необходимо выбрать соответствующего работодателя в таблице Employer . Для этого можно предоставить пользователю Spinner . Отобразить содержимое Cursor в Spinner довольно просто.
Сначала мы выполняем Android SQLite query , как было описано выше, выбираем только name из Employer и id (queryCols) . Затем создаем экземпляр SimpleCursorAdapter , передавая ему Cursor , массив столбцов для отображения ( adapterCols ) и массив представлений, с помощью которых должны отображаться столбцы ( adapterRowViews ). Затем устанавливаем Spinner Adapter для SimpleCursorAdapter :
Вставка внешнего ключа в базу данных
Вставка строки, содержащей внешний ключ, полностью идентична вставке строк в таблицу без ограничений по внешнему ключу. Разница заключается в том, что в Android SQLite примере мы получаем ссылку на выбранный объект Cursor из Spinner , а затем — значение столбца _ID Employer :
Выборка данных из базы SQLite с помощью JOIN
Нельзя использовать метод SQLiteDatabase query() для выполнения запроса к нескольким таблицам. Для этого нужно составить собственный SQL-запрос . В приведенном ниже примере запрос определяется в классе SampleDBContract :
Обратите внимание, что в условии WHERE мы используем символ « ? ». Чтобы не нарушить синтаксис SQL нужно определить selectArgs String [] со значениями, которые будут заменять в предоставленном SQL-запросе символ « ? »:
В заключении
Полная версия исходного кода доступна на github для использования и изменения. Базы данных Android SQLite — это мощное средство, доступное для всех мобильных приложений.
Данная публикация представляет собой перевод статьи « Using a simple SQLite database in your Android app » , подготовленной дружной командой проекта Интернет-технологии.ру
