지역 저장소에 대한 훨씬 더 정교하면서 가장 강력한 방법은 SQLite 데이터베이스를 사용하는 것이다. 모든 애플리케이션은 자신의 SQLite
데이버베이스를 장착하고 있어 애플리케이션 내의 어떠한 클래스에든 접근할 수 있지만, 외부 애플리케이션에서는 그렇지 못하다.
복잡한 쿼리나 코드 블록으로 넘어가기에 앞서 SQLite 데이터베이스가 무엇인지 간단히 알아보자.
SQL(Structured Query Language)은 관계형 데이터베이스의 데이터를 관리하기 위한 목적으로 설계된 프로그래밍 언어다. 관계형 데이터베이스는
입력과 삭제, 수정, 쿼리를 수행할 수 있게 해주며, 또한 스킴(테이블로 생각하자)을 생성하거나 수정할 수 있게 해준다. SQLite는 단순히
MSSQL과 PostgreSQL 등의 다른 유명한 데이터베이스 시스템의 축소판이라 할 수 있다. SQLite는 완전히 독립적이고 서버 기능을 제공하지 않으며,
트랜잭션을 지원하며 쿼리를 수행할 수 있는 표준 SQL 언어를 사용한다. 독립적이며 실행가능하다는 즉성으로 인해 SQLite는 매우 효율적이고
유연하며 광범위한 플랫폼에 걸쳐 다양한 분야의 프로그래밍 언어에서 접근 가능하다.
이제 다음 예제 코드를 통해 새로운 SQLite 데이터베이스 스킴 인스턴스와 간단한 테이블을 생성하는 과정을 가볍게 살펴보자.
package jwei.apps.dataforandroid.ch1;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class SQLiteHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "my_database.db";
// 테이블과 데이터베이스 업데이트를 위해 이 숫자를 토글한다.
private static final int DATABASE_VERSION = 1;
// 생성하고자 하는 테이블 이름
public static final String TABLE_NAME = "my_table";
// 일부 예제 필드
public static final String UID = "_id";
public static final String NAME = "name";
SQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + UID + " INTEGER PRIMARY KEY AUTOINCREMENT," + NAME
+ " VARCHAR(255));");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("LOG_TAG", "Upgrading database from version " + oldVersion + " to " + newVersion
+ ", which will destroy all old data");
// 업그레이드되면 이전 테이블을 제거한다.
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
// 테이블의 새 인스턴스를 생성한다.
onCreate(db);
}
}
여기서 주목해야 한 첫 번째는 사용자가 정의 가능한 데이터베이스 스킴을 생성하기 위해 반드시 SQLiteOpenHelper 클래스를 재정의해야 한다는 점이다.
SQLiteOpenHelper를 재정의한 후 onCreate() 메소드를 재정의해 테이블의 구조를 지시할 수 있다. 예제의 경우 두 개의 칼럼, 즉 ID 칼럼과 name 칼럼을
갖는 테이블을 생성한다. 쿼리는 SQL로 다음 명령을 실행하는 것과 동일하다.
CRATE TABLE my_table(_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(255));
또한 ID 칼럼에 PRIMARY_KEY와 AUTOINCREMENT 속성이 지정돼 있음을 확인했을 것이다. 이는 안드로이드에서 생성된 모든 테이블에 대해 권장되며,
앞으로도 이 표준을 지켜나갈 것이다. 마지막으로 name 칼럼이 최대 255 길이의 문자를 갖는 string 타입으로 선언된 것을 확인할 수 있다.
onCreate() 메소드를 재정의한 후 onUpgrade() 메소드도 재정의할 수 있다. 이 메소드를 통해 테이블의 구조를 빠르고 간단하게 변경할 수 있다.
필요한 작업은 DATABASE_VERSION 정수형을 증가시키는 것이 전부이며, 다음에 SQLiteHelper 인스턴스가 생성될 때 자동으로 onUpgrage() 메소드가
호출되며, 이때 제일 먼저 구 버전의 데이터베이스를 삭제한 후 새로운 버전의 데이터베이스를 생성한다.
마지막으로 거의 뼈대만 있는 테이블에 아주 기본적인 값을 입력하거나 쿼리하는 방법을 간단히 살펴보자.
package jwei.apps.dataforandroid.ch1;
import jwei.apps.dataforandroid.R;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
public class SQLiteExample extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// SQLITE HELPER를 초기화한다.
SQLiteHelper sqh = new SQLiteHelper(this);
// 읽고 쓸 수 있는 데이터베이스를 가져온다.
SQLiteDatabase sqdb = sqh.getWritableDatabase();
// 방법 #1: CONTENTVALUE CLASS를 사용한 입력
ContentValues cv = new ContentValues();
cv.put(SQLiteHelper.NAME, "jason wei");
// INSERT METHOD 호출
sqdb.insert(SQLiteHelper.TABLE_NAME, SQLiteHelper.NAME, cv);
// 방법 #2: SQL 쿼리를 사용해 입력
String insertQuery = "INSERT INTO " + SQLiteHelper.TABLE_NAME + " (" + SQLiteHelper.NAME + ") VALUES ('jwei')";
sqdb.execSQL(insertQuery);
// 방법 #1: 래퍼 메소드를 사용한 쿼리
Cursor c = sqdb.query(SQLiteHelper.TABLE_NAME, new String[] { SQLiteHelper.UID, SQLiteHelper.NAME }, null,
null, null, null, null);
while (c.moveToNext()) {
// 칼럼 인덱스와 해당 칼럼의 값을 얻어온다.
int id = c.getInt(c.getColumnIndex(SQLiteHelper.UID));
String name = c.getString(c.getColumnIndex(SQLiteHelper.NAME));
Log.i("LOG_TAG", "ROW " + id + " HAS NAME " + name);
}
c.close();
// 방법 #2: SQL SELECT QUERY사용하기
String query = "SELECT " + SQLiteHelper.UID + "," + SQLiteHelper.NAME + " FROM " + SQLiteHelper.TABLE_NAME;
Cursor c2 = sqdb.rawQuery(query, null);
while (c2.moveToNext()) {
int id = c2.getInt(c2.getColumnIndex(SQLiteHelper.UID));
String name = c2.getString(c2.getColumnIndex(SQLiteHelper.NAME));
Log.i("LOG_TAG", "ROW " + id + " HAS NAME " + name);
}
c2.close();
// 데이터베이스 연결 종료
sqdb.close();
sqh.close();
}
}
이 예제에 주의를 기울이기 바란다. 이 예제는 이후 글에서도 사용될 것이다. 이 예제에서는 제일 먼저 SQLiteHelper를 초기화하고 쓰기 가능한
SQLiteDatabase객체를 얻는다. 그런 후 아주 편리한 래퍼 메소드인 ContentValues 클래스를 사용해 테이블에 행을 입력하거나 갱신, 삭제를
빨리 수행할 수 있다. 여기서 ID 칼럼을 AUTOINCREMENT 필도로 생성했기 때문에 행을 입력할 때 수작업으로 ID를 할당하거나 증가시킬 필요가
없다는 점을 기억해두자. 따라서 ID 필드 없이(예제에서는 name 칼럼) ContentValues를 전달하기만 하면 된다.
그런 다음 SQLiteDatabase 객체로 다시 돌아가 insert() 메소드를 호출한다. 첫 번째 인자는 단순한 데이터베이스 이름이며, 세 번째 인자는 방금
생성한 ContentValue다. 두 번째 인자는 좀 까다롭다. 기본적으로 비어있는 CententValue가 전달되는 경우에 SQLite 데이터베이스는 빈 행을
입력할 수 없기 때문에 두 번째 인자로 전달된 칼럼이 무엇이든지 SQLite 데이터베이스는 자동으로 해당 칼럼의 값을 null로 설정한다.
이렇게 함으로써 SQLite의 예외 발생을 회피할 수 있다.
추가적으로 execSQL() 메소드를 활용한 두 번째 방법처럼 기본 SQL 쿼리를 전달해 데이터베이스에 행을 입력할 수 있다.
최종적으로 이제 테이블에 두 개의 행을 입력했고, 이제 이 행을 가져와 읽는 연습을 해보자. 여기서도 두 가지 방법을 알아본다.
첫 번째는 SQLiteDatabase 도우미 메소드 query()를 사용하는 것이고, 두 번째는 기본 SQL 쿼리를 실행하는 것이다.
두 경우 모두 Cursor 객체가 반환된다. Cursor는 쿼리에 의해 반환된 하위 테이블의 행을 순차적으로 반복하는 것으로 생각할 수 있다.
while (c.moveToNext()) {
// 칼럽 인덱스와 관련 칼럼의 값을 얻어온다.
int id = c.getInt(c.getColumnIndex(SQLiteHelper.UID));
String name = c.getString(c.getColumnIndex(SQLiteHelper.NAME));
Log.i("LOG_TAG", "ROW " + id + " HAS NAME " + name);
}
원하는 Cursor를 확보했다면 나머지는 직관적이다. 커서는 while 루프로 전달해야할 각 행을 가져올 수 있게 반복자처럼 동작하기 때문에
각 루프내에서 행 단위로 커서를 아래로 이동시켜야 한다. 그 다음 while 루프 내에서 가져오고자 하는 데이터의 칼럼 인덱스를 얻어야 한다.
예제에서는 단순히 두 칼럼 모두 얻어왔지만, 실전에서는 주어진 시간에 특정한 칼럼으로부터 필요한 데이터만 가져오는 편이 좋다.
마지막으로 커서의 get() 메소드로 칼럼의 인덱스를 전달한 후 칼럼의 타입이 정수형인 경우 getInt() 메소드를 호출한다.
문자열이라면 getString() 메소드를 호출한다.
'Programing > Android' 카테고리의 다른 글
[SQL/DB] 외부 저장소 메소드 (0) | 2013.11.15 |
---|---|
[SQL/DB] 내부 저장소 메소드 (0) | 2013.11.15 |
[SQL/DB] SharedPreferences의 일반적인 사용 사례 (0) | 2013.11.15 |
[SQL/DB] SharedPreferences의 사용 (0) | 2013.11.13 |