SQLite kullanarak veri tasarrufu sağlayın

Verileri veritabanına kaydetmek, iletişim bilgileri gibi tekrarlanan veya yapılandırılmış veriler için idealdir. Bu sayfada, genel olarak SQL veritabanlarına aşina olduğunuz varsayılır ve Android'de SQLite veritabanlarını kullanmaya başlamanıza yardımcı olunur. Android'de veritabanı kullanmak için ihtiyaç duyacağınız API'ler android.database.sqlite paketinde bulunur.

Dikkat: Bu API'ler güçlü olsa da oldukça düşük düzeydedir ve kullanımı çok fazla zaman ve çaba gerektirir:

  • Ham SQL sorguları için derleme zamanı doğrulaması yoktur. Verileriniz değiştikçe etkilenen SQL sorgularını manuel olarak güncellemeniz gerekir. Bu işlem zaman alıcı ve hataya açık olabilir.
  • SQL sorguları arasında dönüşüm yapmak için çok sayıda standart kod kullanmanız gerekir. ve veri nesnelerinden bahsedeceğiz.

Bu nedenlerle, Kesinlikle Odada Kalıcılık Kitaplığı uygulamanızın SQLite'ındaki bilgilere erişmek için bir soyutlama katmanı olarak veri tabanları.

Şema ve sözleşme tanımlama

SQL veritabanlarının ana ilkelerinden biri şemadır: nasıl düzenlendiğini gösteren açıklamadır. Şema, veritabanınızı oluşturmak için kullandığınız SQL ifadelerine yansıtılır. Ekibiniz ve sizin için sözleşme sınıfı olarak bilinen ve açıkça belirten bir tamamlayıcı sınıf oluşturun. kendi kendini belgeleyen bir yöntemle düzenlemenizi sağlar.

Sözleşme sınıfı, URI'ların adlarını tanımlayan sabit değerler için bir kapsayıcıdır. tablo ve sütun yer alır. Sözleşme sınıfı, aynı sabit değerleri kullanmanıza olanak tanır aynı paketteki diğer tüm sınıflarda oluşturabilirsiniz. Bu özellik, sütununuzda kodunu tek bir yere toplamanızı ve bu adın kodunuza uygulanmasını sağlar.

Sözleşme sınıfını düzenlemenin iyi bir yolu, tüm veritabanınız için genel olan tanımları sınıfın kök düzeyine yerleştirmektir. Sonra iç içe bir sınıfını kullanır. Her iç sınıf, ilgili tablonun sütunlarını listeler.

Not: BaseColumns uygulayarak bir sonraki aşamaya geçiş yaparsanız, iç sınıfınız birincil temel alan _ID, CursorAdapter gibi bazı Android sınıflarında Zorunlu değildir ancak bu, veritabanınızın Android çerçevesiyle uyumlu çalışmasına yardımcı olabilir.

Örneğin, aşağıdaki sözleşmede bir RSS özet akışını temsil eden tek bir tablo:

Kotlin

object FeedReaderContract {
    // Table contents are grouped together in an anonymous object.
    object FeedEntry : BaseColumns {
        const val TABLE_NAME = "entry"
        const val COLUMN_NAME_TITLE = "title"
        const val COLUMN_NAME_SUBTITLE = "subtitle"
    }
}

Java

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // make the constructor private.
    private FeedReaderContract() {}

    /* Inner class that defines the table contents */
    public static class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
    }
}

SQL yardımcısı kullanarak veritabanı oluşturma

Veritabanınızın nasıl göründüğünü tanımladıktan sonra, veritabanı ve tabloları oluşturup yöneten Tablo oluşturan ve silen bazı tipik ifadeler aşağıda verilmiştir:

Kotlin

private const val SQL_CREATE_ENTRIES =
        "CREATE TABLE ${FeedEntry.TABLE_NAME} (" +
                "${BaseColumns._ID} INTEGER PRIMARY KEY," +
                "${FeedEntry.COLUMN_NAME_TITLE} TEXT," +
                "${FeedEntry.COLUMN_NAME_SUBTITLE} TEXT)"

private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${FeedEntry.TABLE_NAME}"

Java

private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
    FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

Tıpkı cihazın dahili klasörüne kaydettiğiniz dosyalar gibi depolama alanı kullanıyorsanız Android, veritabanınızı uygulamanızın gizli klasöründe depolar. Varsayılan olarak bu alana diğer uygulamalar veya kullanıcı erişemediğinden verileriniz güvendedir.

SQLiteOpenHelper sınıfı yararlı bir API seti kullanıma hazırdır. Veritabanınıza referanslar almak için bu sınıfı kullandığınızda sistem, gerçeğe dönüştüğü takdirde veri tabanı oluşturma ve güncelleme gibi uzun süreli işlemleri, yalnızca uygulama başlatılırken değil. Tek yapmanız gereken telefon görüşmesi yapmak getWritableDatabase() veya getReadableDatabase().

Not: Uzun süreli olabileceği için bir arka plan ileti dizisinde getWritableDatabase() veya getReadableDatabase() adını belirttiğinizden emin olun. Daha fazla bilgi için Android'de ileti dizisi bölümüne göz atın.

SQLiteOpenHelper kullanmak için onCreate() ve onUpgrade() geri çağırma yöntemlerini geçersiz kılan bir alt sınıf oluşturun. onDowngrade() veya onOpen() yöntemlerini de uygulayabilirsiniz ancak bu zorunlu değildir.

Örneğin burada, şöyle bir SQLiteOpenHelper aşağıda gösterilen komutların bazılarını kullanır:

Kotlin

class FeedReaderDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(SQL_CREATE_ENTRIES)
    }
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES)
        onCreate(db)
    }
    override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        onUpgrade(db, oldVersion, newVersion)
    }
    companion object {
        // If you change the database schema, you must increment the database version.
        const val DATABASE_VERSION = 1
        const val DATABASE_NAME = "FeedReader.db"
    }
}

Java

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

Veritabanınıza erişmek için alt sınıfınızı örneklendirin: SQLiteOpenHelper:

Kotlin

val dbHelper = FeedReaderDbHelper(context)

Java

FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());

Bilgileri bir veritabanına ekleme

insert() yöntemine bir ContentValues nesnesi göndererek veritabanına veri ekleyin:

Kotlin

// Gets the data repository in write mode
val db = dbHelper.writableDatabase

// Create a new map of values, where column names are the keys
val values = ContentValues().apply {
    put(FeedEntry.COLUMN_NAME_TITLE, title)
    put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle)
}

// Insert the new row, returning the primary key value of the new row
val newRowId = db?.insert(FeedEntry.TABLE_NAME, null, values)

Java

// Gets the data repository in write mode
SQLiteDatabase db = dbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);

// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);

insert() için ilk bağımsız değişken tablo adıdır.

İkinci bağımsız değişken, ContentValues boş olduğunda (yani herhangi bir değer put vermediğinizde) çerçeveye ne yapacağını söyler. Bir sütunun adını belirtirseniz çerçeve bir satır ekler ve bu sütunun değerini null olarak ayarlar. Bu kod örneğinde olduğu gibi null belirtirseniz çerçeve, değer olmadığında satır eklemez.

insert() yöntemleri, yeni oluşturulan satırın kimliğini döndürür veya verileri eklerken hata oluştuysa -1 döndürür. Bu durum, veritabanındaki önceden var olan verilerle çakışma yaşıyorsanız ortaya çıkabilir.

Veritabanındaki bilgileri okuma

Bir veritabanından okumak için query() yöntemini kullanın. Bu yönteme seçim ölçütlerinizi ve istediğiniz sütunları iletin. Yöntem, insert() ve update() öğelerini birleştirir. Bununla birlikte, sütun listesi, eklenecek veriler yerine getirmek istediğiniz verileri ("projeksiyon") tanımlar. Sonuçlar bir Cursor nesnesinde size döndürülür.

Kotlin

val db = dbHelper.readableDatabase

// Define a projection that specifies which columns from the database
// you will actually use after this query.
val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE)

// Filter results WHERE "title" = 'My Title'
val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?"
val selectionArgs = arrayOf("My Title")

// How you want the results sorted in the resulting Cursor
val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC"

val cursor = db.query(
        FeedEntry.TABLE_NAME,   // The table to query
        projection,             // The array of columns to return (pass null to get all)
        selection,              // The columns for the WHERE clause
        selectionArgs,          // The values for the WHERE clause
        null,                   // don't group the rows
        null,                   // don't filter by row groups
        sortOrder               // The sort order
)

Java

SQLiteDatabase db = dbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    BaseColumns._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_SUBTITLE
    };

// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";

Cursor cursor = db.query(
    FeedEntry.TABLE_NAME,   // The table to query
    projection,             // The array of columns to return (pass null to get all)
    selection,              // The columns for the WHERE clause
    selectionArgs,          // The values for the WHERE clause
    null,                   // don't group the rows
    null,                   // don't filter by row groups
    sortOrder               // The sort order
    );

Üçüncü ve dördüncü bağımsız değişkenler (selection ve selectionArgs) bir WHERE ifadesi oluşturmak için kullanılır. Seçim sorgusundan ayrı olarak sağlanan bağımsız değişkenler, birleştirilmeden önce kaçılır. Bu sayede seçim ifadeleriniz SQL ekleme saldırılarına karşı korunur. Tüm bağımsız değişkenler hakkında daha fazla bilgi için query() referansı.

İmleçteki bir satırı görmek için Cursor taşıma işlemlerinden birini kullanın yöntemleri kullanır. Bunlar, değerleri okumaya başlamadan önce her zaman çağırmanız gerekir. İmleç -1 konumunda başladığından moveToNext() çağrıldığında "okuma konumu" sonuçlardaki ilk girişe yerleştirilir ve imlecin sonuç kümesinin son girişini geçip geçmediği döndürülür. Her satır için Cursor, getString() veya getLong() gibi yöntemleri alır. Get yöntemlerinin her biri için, istediğiniz sütunun dizin konumunu iletmeniz gerekir. Bu konumu getColumnIndex() veya getColumnIndexOrThrow()'i çağırarak alabilirsiniz. Tamamlandığında sonuçlar yineleniyor, imleçte close() işlevini çağırın serbest bırakılmasını isteyebilirsiniz. Örneğin, aşağıdaki örnekte bir imleçte depolanan tüm öğe kimliklerinin nasıl alınacağı ve bir listeye nasıl ekleneceği gösterilmektedir:

Kotlin

val itemIds = mutableListOf<Long>()
with(cursor) {
    while (moveToNext()) {
        val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID))
        itemIds.add(itemId)
    }
}
cursor.close()

Java

List itemIds = new ArrayList<>();
while(cursor.moveToNext()) {
  long itemId = cursor.getLong(
      cursor.getColumnIndexOrThrow(FeedEntry._ID));
  itemIds.add(itemId);
}
cursor.close();

Veritabanında bilgileri silme

Tablodan satır silmek için şu koşulları karşılayan seçim ölçütleri sağlamanız gerekir: satırları delete() yöntemine tanımlamak için kullanılır. Bu mekanizma, query() yönteminin seçim bağımsız değişkenleriyle aynı şekilde çalışır. Her bir seçim yan tümcesine ve seçim bağımsız değişkenlerine dönüştürme. Bu yan tümce, incelenecek sütunları tanımlar ve sütun testlerini birleştirmenize de olanak tanır. Bağımsız değişkenler, yan tümceyle bağlanan ve test edilecek değerlerdir. Sonuç, normal bir SQL ifadesiyle aynı şekilde işlenmediğinden SQL eklemeye karşı bağışıktır.

Kotlin

// Define 'where' part of query.
val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"
// Specify arguments in placeholder order.
val selectionArgs = arrayOf("MyTitle")
// Issue SQL statement.
val deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs)

Java

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);

delete() yönteminin döndürdüğü değer, veritabanından silinen satırların sayısını gösterir.

Veritabanı güncelleme

Veritabanı değerlerinizin bir alt kümesini değiştirmeniz gerektiğinde update() yöntemini kullanın.

Tablo güncellendiğinde insert()'un ContentValues söz dizimi, delete()'ın WHERE söz dizimine birleştirilir.

Kotlin

val db = dbHelper.writableDatabase

// New value for one column
val title = "MyNewTitle"
val values = ContentValues().apply {
    put(FeedEntry.COLUMN_NAME_TITLE, title)
}

// Which row to update, based on the title
val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"
val selectionArgs = arrayOf("MyOldTitle")
val count = db.update(
        FeedEntry.TABLE_NAME,
        values,
        selection,
        selectionArgs)

Java

SQLiteDatabase db = dbHelper.getWritableDatabase();

// New value for one column
String title = "MyNewTitle";
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyOldTitle" };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);

update() yönteminin döndürdüğü değer, veritabanında etkilenen satırların sayısıdır.

Kalıcı veritabanı bağlantısı

Veritabanı kapalıyken getWritableDatabase() ve getReadableDatabase()'ı aramak pahalı olduğundan, veritabanı bağlantınızı erişmeniz gerektiği sürece açık bırakmanız gerekir. Genellikle en iyi seçenek, veritabanını kapatmak bölümünde bulabilirsiniz.onDestroy()

Kotlin

override fun onDestroy() {
    dbHelper.close()
    super.onDestroy()
}

Java

@Override
protected void onDestroy() {
    dbHelper.close();
    super.onDestroy();
}

Veritabanınızda hata ayıklama

Android SDK'da, göz atmanızı sağlayan bir sqlite3 kabuk aracı bulunur tablo içeriği, SQL komutlarını çalıştırma ve SQLite'ta faydalı diğer işlevleri gerçekleştirme veri tabanları. Daha fazla bilgi için kabuk komutlarının nasıl yayınlanacağına bakın.