SQLite в Android на Java: создание базы, таблиц и выполнение запросов
Кратко: используйте SQLiteOpenHelper для создания и обновления БД, в onCreate выполняйте DDL через db.execSQL(), а для операций CRUD — db.query/rawQuery и методы insert/update/delete с ContentValues; обязательно закрывайте Cursor и БД.
Подготовка и DatabaseHelper
- Разрешения: для внутренней БД дополнительных разрешений не требуется (WRITE_EXTERNAL_STORAGE не нужен для стандартной локальной БД с API 19+).
- Создайте класс, наследующий SQLiteOpenHelper и инициализируйте его в Activity/Service.
Пример DatabaseHelper:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "myapp.db";
private static final int DATABASE_VERSION = 1;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// создание таблиц см. ниже
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// простая стратегия: пересоздать таблицы (для продакшна миграции лучше писать отдельно)
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
}
Инициализация:
DatabaseHelper dbHelper = new DatabaseHelper(this);
Создание таблиц и индексы
В onCreate используйте db.execSQL() с DDL. Пример таблицы users:
@Override
public void onCreate(SQLiteDatabase db) {
String create = "CREATE TABLE users (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT NOT NULL, " +
"email TEXT UNIQUE, " +
"age INTEGER DEFAULT 18)";
db.execSQL(create);
db.execSQL("CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)");
}
Пояснения:
- PRIMARY KEY AUTOINCREMENT — даёт уникальный id.
- NOT NULL — для обязательных полей.
- UNIQUE — предотвращает дублирование (например, email).
- Индексы ускоряют выборки по колонкам с фильтрами/соединениями.
Добавляйте NOT NULL для обязательных полей и индексы по часто используемым колонкам. Используйте IF NOT EXISTS при создании индексов.
CRUD: SELECT, INSERT, UPDATE, DELETE и транзакции
Чтение (безопасно):
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor c = db.query("users",
new String[]{"id","name","email","age"},
"age > ?", new String[]{"25"},
null, null, "name ASC");
if (c.moveToFirst()) {
do {
int id = c.getInt(0);
String name = c.getString(1);
// обработка
} while (c.moveToNext());
}
c.close();
db.close();
Сырой запрос (rawQuery) — для JOIN, GROUP BY и сложных выражений:
Cursor c = db.rawQuery("SELECT u.id,u.name,o.total FROM users u JOIN orders o ON u.id=o.user_id WHERE o.total>?", new String[]{"1000"});
Вставка:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues vals = new ContentValues();
vals.put("name","Иван Иванов");
vals.put("email","ivan@example.com");
vals.put("age",30);
long id = db.insert("users", null, vals);
db.close();
Массовая вставка — в транзакции для скорости:
db.beginTransaction();
try {
for (...) {
db.insert(...);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Обновление и удаление:
// update
ContentValues v = new ContentValues();
v.put("age", 31);
int cnt = db.update("users", v, "id = ?", new String[]{String.valueOf(1)});
// delete
int del = db.delete("users", "id = ?", new String[]{String.valueOf(1)});
Не забудьте всегда закрывать Cursor и вызов db.close() в finally-блоке или использовать try-with-resources (для API, где поддерживается). Не формируйте SQL через конкатенацию строк — используйте параметры, чтобы избежать инъекций.
Частые ошибки
- Забывают закрыть Cursor или SQLiteDatabase → утечки и ошибки "database locked".
- Меняют схему без корректной миграции в onUpgrade → потеря данных.
- Конкатенация параметров в SQL вместо placeholders → риск SQL‑инъекции.
- Отсутствие индексов для часто фильтруемых колонок → медленные запросы.
FAQ
- Нужно ли WRITE_EXTERNAL_STORAGE для SQLite? Нет, если вы храните БД во внутреннем хранилище приложения.
- Когда использовать rawQuery вместо db.query()? Для сложных JOIN/GROUP BY/подзапросов, db.query удобен для простых выборок.
- Как безопасно обновлять схему БД в продакшне? Реализуйте пошаговые миграции в onUpgrade, сохраняйте резервные копии при крупных изменениях.
Этот набор приёмов покрывает большинство задач по работе с SQLite в Android на Java: создавайте DatabaseHelper, проектируйте схемы с индексами, используйте параметры запросов и транзакции для производительности.