Android platformu da diğer mobil platformlar gibi veritabanı olarak SQLite kullanımını tercih etmektedir. Hem SQL komutlarını çalıştırabilmesi hem de mobil cihazlar gibi düşük kapasiteli ortamlarda kolayca çalışabilmesi SQLite’ı Android ve iOS platformlarında ilk seçenek haline getirmiştir.
Buradaki adresten Windows için indirebileceğiniz SQLite'ı komut satırından çalıştırabilirsiniz. Bunun için indirilen .exe dosyasını varsayılan şekilde kurmanız yeterli olacaktır. Aşağıda bulunan anlatımlar Mac OS X üzerinde anlatılmıştır fakat komutlar Windows işletim sistemi içinde aynıdır.
(Buradaki adresten Mac OS X için indirebileceğimiz SQLite’ı Terminal'den çalıştırabilirsiniz. Terminal penceresinde aşağıdaki komutu girdiğinizde yeni bir veritabanı yaratılacak ve diskte aynı isimle bir dosya oluşturulacaktır.)
Last login: Thu Apr 4 15:28:55 on ttys001
Ozans-MacBook-Pro:~ ozanuysal$ sqlite3 countries
Bu komutla birlikte diskte countries adında bir dosya oluşur ve biz veritabanı üzerinde SQL komutları kullanmaya başlayabiliriz. SQL komutları MySQL gibi karmaşık veritabanlarına göre daha basit olsa da SQLite, mobil uygulamalarda karşılaşabileceğimiz her türlü ihtiyacı giderecek kapasiteye sahiptir.
Şimdi aşağıdaki komutu girerek yeni bir tablo oluşturalım.
SQLite version 3.7.12 2012-04-03 19:43:07
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table country (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, code TEXT);
Yukarıdaki komutla country adında bir tablo oluşturmuş olduk. Burada ID değeri her satıra karşılık gelen anahtar değeridir ve ana anahtar (primary key) olarak tanımlanmıştır. AUTOINCREMENT ise her eklenen satırda otomatik olarak arttırılacağını gösterir. title ve code eklenen ülkenin adı ve ülke kodu için ayrılmıştır ve TEXT veri tipindedir.
Bu şekilde yeni bir tablo oluşturmuş olduk. Aşağıdaki komutla yeni bir satır ekliyoruz:
sqlite> INSERT INTO country VALUES (1,'Almanya','49');
Yukarıdaki INSERT ifadesi ile country tablosuna Almanya adında 49 koduna sahip bir ülke girmiş olduk. Bundan sonra diğer ülkeleri de girerek tablomuzu dolduralım.
sqlite> INSERT INTO country VALUES (1,'Almanya','49');
sqlite> INSERT INTO country VALUES (2,'Turkiye','90');
sqlite> INSERT INTO country VALUES (3,'Ingiltere','44');
sqlite> INSERT INTO country VALUES (4,'Amerika','49');
Bu şekilde tablomuza dört adet ülke girmiş olduk. Son adımda aşağıdaki komutu girerek veritabanından çıkıyoruz.
sqlite> .quit
Ozans-MacBook-Pro:~ ozanuysal$
Şimdi basit bir Android uygulamasıyla benzer işlemleri Android SDK üzerinden gerçekleştireceğiz. Android SDK, SQLiteOpenHelper sınıfı üzerinden SQLite ile ilgili işlemlerde bize yardımcı olacaktır. İlk olarak yeni bir proje oluşturarak DBHelper adında yardımcı bir sınıf yaratalım ve SQLiteOpenHelper sınıfına alt sınıf olarak belirleyelim.
package com.turkcell.sqlexample;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "turkcellDB";
// Contacts table name
private static final String TABLE_COUNTRIES = "countries";
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE " + TABLE_COUNTRIES + "(id INTEGER PRIMARY KEY,country_name TEXT,country_code TEXT" + ")";
Log.d("DBHelper", "SQL : " + sql);
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_COUNTRIES);
onCreate(db);
}
}
Bu işlem yapıldığında SQLiteOpenHelper içerisinde yer alan onCreate ve onUpgrade metotlarını oluşturmamız gerekecektir. Aynı zamanda SQLiteOpenHelper (Context context, String name, SQLiteDatabase.CursorFactory factory, int version) yapıcısını da oluşturmamız gerekir.
onCreate metodu eğer uygulamayla ilgili SQLite veritabanı oluşturulmamışsa (örneğin uygulama ilk defa çalışıyorsa) bir SQLite veritabanı yaratır ve üzerinde metot içerisinde yer alan sorguları çalıştırır. Biz burada daha önce anlattığımıza benzer şekilde bir CREATE sql ifadesiyle ülke bilgilerine dair bir tablo oluşturduk. execSQL komutu da oluşturduğumuz SQL sorgusunu veritabanında çalıştırarak, ilgili tabloları yaratmamızda bize yardımcı oldu. Siz de uygulamanızda bu şekilde veritabanı yapısını oluşturabilirsiniz.
onUpgrade metodu, uygulamanın veritabanı güncellendiyse (örneğin yeni bir sürüm geldiyse) harekete geçecektir. SQLiteOpenHelper’ın yapıcısına dikkat edersek, son değerin veritabanı sürümü olduğunu görürüz. Veritabanında tablo yapısında bir değişiklik varsa ve eski sürümlerde güncelleme gerekiyorsa, bu metot içerisinde ilgili sorgular çalıştırılır. Hangi sürümler arasında geçiş olacağını oldVersion ve newVersion değişkenleriyle anlayabiliriz. Gerekli kontrol yapıları ile veritabanı güncelleme işlemlerini bu metod altında yapmalıyız. Biz burada basitçe tabloyu silip baştan yaratmayı tercih ettik.
Şimdi ülkelerle ilgili Country adında bir sınıf yaratalım:
package com.turkcell.sqlexample;
import java.io.Serializable;
public class Country implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String countryName;
private String countryCode;
public Country() {
super();
}
public Country(String countryName, String countryCode) {
super();
this.countryName = countryName;
this.countryCode = countryCode;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Yukarıdaki sınıfta ülkeye ait isim ve kod değerleri yer alıyor. ID ise veritabanındaki birincil anahtar değerine karşılık geliyor. Şimdi DBHelper sınıfına geri dönelim ve bir insertCountry metodu hazırlayalım. Bu metot veritabanına yeni bir ülke eklemeye yarayacaktır.
public void insertCountry(Country country) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("country_name", country.getCountryName());
values.put("country_code", country.getCountryCode());
db.insert(TABLE_COUNTRIES, null, values);
db.close();
}
getWritableDatabase metodu veritabanıyla bağlantı kurarak yapacağımız sorgular için ortam hazırlar. ContentValues sınıfı, tabloda yer alan sütun adı ve bu sütunlara doldurulacak değerleri girdiğimiz HashMap sınıfına benzer bir yapıdır. insert metodu ise tablo adı ve ContentValues içerisinde yer alan değerler ile bir INSERT sorgusu hazırlar ve veritabanına bir satır eklenmesini sağlar. Burada country_name adlı sütuna ülke adı girilirken, country_code sütununa ülke kodu giriliyor.
Şimdi de bütün ülkeleri listeleyen bir metot hazırlayalım:
public List<Country> getAllCountries() {
List<Country> countries = new ArrayList<Country>();
SQLiteDatabase db = this.getWritableDatabase();
// String sqlQuery = "SELECT * FROM " + TABLE_COUNTRIES;
// Cursor cursor = db.rawQuery(sqlQuery, null);
Cursor cursor = db.query(TABLE_COUNTRIES, new String[]{"id", "country_name", "country_code"}, null, null, null, null, null);
while (cursor.moveToNext()) {
Country country = new Country();
country.setId(cursor.getInt(0));
country.setCountryName(cursor.getString(1));
country.setCountryCode(cursor.getString(2));
countries.add(country);
}
return countries;
}
Burada veritabanından gelen sonuçları List tipinde bir dizi içerisinde saklamayı hedefliyoruz. Yine daha önceki örnekte olduğu gibi veritabanına erişim açarak bir Cursor üzerinden veritabanına sorgumuzu atıyoruz. query metodu basit bir SELECT sorgusu oluşturmak için idealdir ve veritabanından istenilen sütunları bir String dizisi şeklinde alır. Yukarıdaki metot aslında SELECT id, country_name, country_code FROM country; anlamında bir SQL sorgusu üretecektir. Sorgu sonucunda oluşan Cursor objesi ise bize sonuçlar içerisinde dolaşma olanağı sağlayacaktır. Burada ilk satırdan başlayarak bütün satırları dolaşma işlemini moveToNext metodu ile bir while döngüsü içerisinde yapıyoruz. Sonuç satırları bitene kadar true değeri dönülürken son satıra gelindiğinde moveToNext metodundan false değeri gelir ve döngü biter. Döngü içerisinde ise ilgili sütunlara sütun sırasıyla ulaşırız. Burada ID ilk sütun olduğundan 0 sıra sayısına sahiptir. ID aynı zamanda sayısal bir şekilde tutulduğundan getInt metoduyla çağrılır. country_name ise ikinci değerdir ve getString(1) metodu ile çağırılır. Veritabanında TEXT tipinde saklandığından getString metodu yardımıyla alınır.
NOT: Alternatif olarak kendi oluşturduğumuz sorguları rawQuery metodu kullanarak veritabanına gönderebiliriz. Ancak kullanıcıdan girdi aldığımız ya da sorgu içerisindeki değişkenlerin kontrolümüz dışında değiştirildiği ortamlarda bu şekilde sorgu göndermemiz güvenlik açığı yaratacaktır.
Her satır bir Country nesnesi içerisinde saklandıktan sonra countries dizisine eklenir ve metodumuz bütün ülkeleri bir dizi şeklinde döndürür.
Şimdi daha önceki örneklerden hatırlayacağımız ve ListView tablosu doldurmak kullandığımız MyListAdapter’ı hatırlayalım:
package com.turkcell.sqlexample;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class MyListAdapter extends BaseAdapter {
private LayoutInflater inflater;
private List<Country> countryList;
public MyListAdapter(Activity activity, List<Country> countries) {
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
countryList = countries;
}
@Override
public int getCount() {
return countryList.size();
}
@Override
public Object getItem(int position) {
return countryList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.listview_row, null); // create layout from
TextView textView = (TextView) vi.findViewById(R.id.row_textview); // user name
Country country = countryList.get(position);
textView.setText(country.getCountryName());
return vi;
}
}
Bu sınıf bize ülke listesinden bir tablo dolduracaktır. Tablo ile ilgili satır tasarımı ise şu şekildedir:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" >
<TextView
android:id="@+id/row_textview"
android:layout_width="fill_parent"
android:layout_height="30dp"
android:layout_gravity="center" />
</LinearLayout>
Son olarak MainActivity sınıfı içerisinde onCreate metodumuza göz atalım:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ListView customListView = (ListView) findViewById(R.id.listview);
DBHelper dbHelper = new DBHelper(getApplicationContext());
SharedPreferences settings = getSharedPreferences("SQL", 0);
boolean firstTime = settings.getBoolean("firstTime", true);
if (firstTime) {
dbHelper.insertCountry(new Country("Turkiye", "90"));
dbHelper.insertCountry(new Country("Amerika", "1"));
dbHelper.insertCountry(new Country("Ingiltere", "44"));
dbHelper.insertCountry(new Country("Almanya", "49"));
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("firstTime", false);
editor.commit();
}
List<Country> countries = dbHelper.getAllCountries();
MyListAdapter myListAdapter = new MyListAdapter(MainActivity.this, countries);
customListView.setAdapter(myListAdapter);
}
Uygulama çalıştığında yeni bir DBHelper yaratılarak veritabanı bağlantısı oluşturuluyor. Uygulamanın ilk defa çalışması ise (Google Play’den ilk yükleme) SharedPreferences üzerinden bir değişkenle kontrol ediliyor. Burada firstTime adında bir değerin cihazda daha önce bulunup bulunmadığı sorgulanıyor. Eğer yoksa değer doğru (true) dönüyor ve if içerisindeki kod çalışıyor. Bu kod ise dört adet ülkeyi veritabanına yukarıda oluşturduğumuz insertCountry metodu ile ekliyor. İşlem bittikten sonra SharedPrefences.Editor ile firstTime değeri yanlış (false) olarak değiştiriliyor ve commit ile kaydediliyor. Bu şekilde uygulama daha sonraki açılışlarında bu değerleri tekrar tekrar kaydetmeden çalıştıracaktır. Daha sonra ülkeler getAllCountries metoduyla veritabanından çağrılarak yine yukarıda oluşturduğumuz MyListAdapter ile listeleniyor.