Я прихожу к вам после многих часов поиска в Google и чтения других дискуссий о SQLite в StackOverflow, но я определенно не могу найти никакого объяснения моей проблемы, поэтому вот оно:
Контекст:
Я разрабатываю приложение для iPad, которое несколько раз должно иметь дело с некоторыми "большими" объемами данных.В одном из них я должен импортировать координаты точек из файла .kml (xml от Google для географических данных) в мою базу данных, чтобы потом использовать их позже с помощью MKMapView и загружать их быстрее, чем при синтаксическом анализе xml, когда требуется показать конкретный файл.layer.
Подробности:
Импорт очень прост: при работе с этими файлами меня интересуют только 2 таблицы:
- Одна, содержащая определения и детали зон: на данный момент
integer
в качестве идентификатора и text
для именования. - Одна, содержащая две
real
для хранения координат иinteger
ссылка на первую таблицу для определения, к какой точке зоны относится часть.
Итак, пока я читаю мой файл, я сначала создаю запись для новой зоны, а затем вставляю точки во вторую таблицу с идентификаторомпоследней зоны, созданной в первой таблице ... ничего сложного!
Но ...
Проблема:
После долгого запуска я получаю исключение из SQLite сзнаменитое сообщение «Невозможно открыть файл базы данных», а потом приходит сообщение, что я больше ничего не могу сделать с базой данных.Это исключение может происходить случайным образом при создании зоны или методах вставки точек.
Мои отражения:
Учитывая многочисленные точки в этих файлах, я подозревал, что памятьили насыщение диска, но другие части моего приложения отбросили эти точки (на мой взгляд).
Во-первых, память: случается, что, когда возникает исключение, приложение использует около 10 или 12 МБ ОЗУ.Это может показаться довольно большим, но это из-за загруженного в память файла ~ 10MB .kml, так что это объяснимо.Кроме того, приложение MKMapView в моем приложении работает с тоннами слоев листов с высоким разрешением над картой и, как следствие, приводит к пикам памяти, которые могут позволить себе 20 или даже 25 МБ, не приводя к падению iPad.
Во-вторых, диск:при перезагрузке базы данных и заполнении только 2 таблиц, описанных выше, размер файла БД при возникновении исключения всегда составляет около 2,2 или 2,5 МБ, но когда я заполняю другие таблицы (другие части моих приложений работают хорошо!), файл БДоколо 6 или 7 МБ, и устройство не жалуется вообще.
И что?!
CPU-гнев и паника?Я так не думаю, потому что некоторые из других таблиц моей базы данных заполнены в том же ритме без проблем ... и запуск моего приложения в симуляторе тоже дает сбой, а ядро i7 просто смеется над работой.
SQLite badиспользовать?Вот и мы!На мой взгляд, это единственное решение!Но я действительно не могу понять, что здесь происходит, потому что я обрабатываю свои запросы так же, как и в других частях приложения, которые, повторяя себя, работают как шарм!
Подробности SQLite:
У меня есть класс DB
, который я использую во избежание создания / освобождения объекта SqliteConnection
каждый запрос Iделать, и все мои методы, связанные с базой данных, содержатся в этом классе, чтобы быть уверенным, что я не буду играть с соединением где-либо еще, не зная его.Вот соответствующие методы этого класса:
public void saveZone(ObjZone zone) { //at this point, just creates an entry with a name and let sqlite give it a new id
lock (connection) { //SqliteConnection object
try {
openConnection();
SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = zone.id == 0 ?
"insert into ZONES (Z_NAME) values (" + format(zone.name) + ") ;" :
"update ZONES set Z_NAME = " + format(zone.name) + " where Z_ID = " + format(zone.id) + " ;";
cmd.ExecuteNonQuery();
if (zone.id == 0) {
cmd.CommandText = "select Z_ID from ZONES where ROWID = last_insert_rowid() ;";
zone.id = uint.Parse(cmd.ExecuteScalar().ToString());
}
cmd.Dispose();
}
catch (Exception e) {
Log.failure("DB.saveZone(" + zone.ToString() + ") : [" + e.GetType().ToString() + "] - " +
e.Message + "\n" + e.StackTrace); //custom Console.WriteLine() method with some formating
throw e;
}
finally {
connection.Close();
}
}
}
public void setPointsForZone(List<CLLocationCoordinate2D> points, uint zone_id) { //registers points for a given zone
lock (connection) {
try {
openConnection();
SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = "delete from ZONESPOINTS where Z_ID = " + format(zone_id);
cmd.ExecuteNonQuery();
foreach(CLLocationCoordinate2D point in points) {
cmd.CommandText = "insert into ZONESPOINTS values " +
"(" + format(zi_id) + ", " + format(point.Latitude.ToString().Replace(",", ".")) + ", "
+ format(point.Longitude.ToString().Replace(",", ".")) + ");";
cmd.ExecuteNonQuery();
cmd.Dispose();
}
}
catch (Exception e) {
Log.failure("DB.setPointsForZone(" + zone_id + ") : [" + e.GetType().ToString() + "] - " + e.Message);
throw e;
}
finally {
connection.Close();
}
}
}
И чтобы было как можно яснее, вот некоторые из методов, на которые ссылаются два выше (я использую этот пользовательский метод openConnection()
, потому что я использую иностранныйограничения клавиш в большинстве моих таблиц и каскадное поведение не включены по умолчанию, но они мне нужны.):
void openConnection() {
try {
connection.Open();
SqliteCommand cmd = connection.CreateCommand();
cmd.CommandText = "PRAGMA foreign_keys = ON";
cmd.ExecuteNonQuery();
cmd.Dispose();
}
catch (Exception e) {
Log.failure("DB.openConnection() : [" + e.GetType().ToString() + "] - " + e.Message);
throw e;
}
}
public static string format(object o) {
return "'" + o.ToString().Replace("'", "''") + "'";
}