본문 바로가기

데이터 관리/SQLite3

데이터베이스 이용하기 - (3) 데이터베이스 어댑터 만들기


데이터베이스 강좌로는 꽤 오래간만에 찾아뵙는군요.
제가 요즘 병행하고 있는 일이 한두개가 아닌데다가, 강좌를 자유롭게(?) 쓸 여건은 되지 않다보니 계속 미뤄지기만 했네요.
아무튼, 이번 강좌에서는 실제로 데이터베이스 어댑터를 생성하여 어플리케이션에 적용하는 것에 대해 알아보겠습니다.

데이터베이스의 어댑터의 역할

"어댑터"에 대한 개념은 예전에 리스트를 설명할 때 처음 등장했습니다. (참고 : 2009/06/08 - [안드로이드 입문/GUI 구성하기] - #11. List 집중공략! - (3) Custom ArrayAdapter를 이용한 ListView)

리스트 어댑터는 실제 데이터와 그 데이터를 표시해주는 리스트뷰 사이에 위치하면서 데이터를 리스트에 표시해주는 역할을 합니다. 즉, 다른 체계를 가지고 있는 개체 사이에서 서로가 호환이 될 수 있도록 적절히 데이터의 형태를 변환해주는 것이 어댑터의 역할이라고 할 수 있죠.

그렇다면, 데이터베이스 어댑터의 역할은 무엇일까요? - 데이터베이스 어댑터 사실 어댑터의 기본 개념과는 약간 다른 개념을 가지고 있습니다. 서로 다른 두 개체를 호환시켜주는 역할이라기보다는, 데이터베이스에 접근하여 수행하는 작업들을 추상화시켜주는 역할을 합니다.

데이터베이스를 다루다보면 데이터베이스에 접근하여 데이터 추가, 수정, 삭제 등의 작업을 하게 되는데 이러한 작업의 내용은 겉으로 드러내지 않아야 데이터베이스 구조의 노출도 막을 수 있고, 쓸데없이 코드를 길게 쓰는 일도 줄어들게 되죠.

일반적인 데이터베이스 어댑터의 구조, 기능

일반적으로 데이터베이스 어댑터는 다음과 같은 구조를 가지고 있습니다.

  • 필드 이름들
  • 데이터베이스 초기화에 필요한 SQL 문장들
  • 데이터베이스 정보 (테이블 이름, 데이터베이스 이름 등)
  • 헬퍼 클래스 (SQLiteOpenHelper; 데이터베이스 열기/닫기를 담당)
  • 헬퍼 클래스의 인스턴스
  • 데이터베이스 (SQLiteDatabase)의 인스턴스
  • 데이터베이스 작업에 필요한 메소드들


  • 필드 이름들

    지난 강좌 (2009/07/04 - [안드로이드 입문/데이터 이용하기] - 데이터베이스 이용하기 - (2) 안드로이드 데이터베이스 기초) 에서 봤던 것처럼, 데이터베이스는 테이블과 테이블 안에 필드(열)과 레코드(행)으로 이루어져있습니다. 이 중, 필드 이름은 앞으로 어댑터 내에서 심심하면 쓰게 됩니다. 데이터베이스를 추가할 때도, 수정할 때도, 테이블을 생성할 때에도 필요하니... 보통 자주 쓰이는 것이 아니죠. 보통, 이렇게 자주 쓰이는 것들은 데이터베이서 어댑터 내에 static final String 형태, 즉 "상수"처럼 저장해놓고 사용합니다.

    예) 
    	public static final String KEY_NAME = "name";
    	public static final String KEY_PHONE = "phone";
    	public static final String KEY_ROWID = "_id";
    



  • 데이터베이스 초기화에 필요한 SQL 문장들

    데이터베이스를 처음 생성할 때 필요한 SQL문장들입니다. 일반적으로 테이블을 생성하는 문장이 들어갑니다.

    예)
    	private static final String DATABASE_CREATE =
    		"create table data (_id integer primary key autoincrement,"+
    		"name text not null, phone text not null);";
    


  • 데이터베이스 정보 (테이블 이름, 데이터베이스 이름 등)

    테이블 이름, 데이터베이스 이름, 데이터베이스 버전입니다. 데이터베이스 버전은 나중에 데이터베이스를 업데이트할 때 업데이트 여부를 결정하는 기준이 됩니다.


    	private static final String DATABASE_NAME = "datum.db";
    	private static final String DATABASE_TABLE = "data";
    	private static final int DATABASE_VERSION = 1;
    

  • 헬퍼 클래스 (SQLiteOpenHelper; 데이터베이스 열기/닫기를 담당)

    데이터베이스 파일을 열고 닫는 작업을 수행해주는 클래스입니다. 필요하다면 데이터베이스를 생성하고, 업그레이드합니다. 데이터베이스를 생성하는 메소드인 onCreate()와 데이터베이스의 업그레이드를 담당하는 onUpgrade() 메소드를 필수로 구현해야 합니다. (두 메소드 모두 추상 메소드 형태로 선언되어있음)


    	private class DatabaseHelper extends SQLiteOpenHelper{
    
    		public DatabaseHelper(Context context) {
    			super(context, DATABASE_NAME, null, DATABASE_VERSION);
    			// TODO Auto-generated constructor stub
    		}
    		
    		public void onCreate(SQLiteDatabase db){
    			db.execSQL(DATABASE_CREATE);
    		}
    		
    		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
    			Log.w(TAG, "Upgrading db from version" + oldVersion + " to" +
    					newVersion + ", which will destroy all old data");
    			db.execSQL("DROP TABLE IF EXISTS data");
    			onCreate(db);
    		}
    		
    	}
    

  • 헬퍼 클래스의 인스턴스

    헬퍼 클래스는 데이터베이스를 열고 닫는 것을 도와주는 클래스라고 했었죠? 이런 역할을 하는 헬퍼 클래스를 이용하기 위해서는 헬퍼 클래스 형태를 가지는 인스턴스가 있어야겠지요?


    	private DatabaseHelper mDbHelper;
    

    이렇게 선언한 헬퍼 클래스의 인스턴스는 뒤에서 데이터베이스 어댑터 클래스 내의 데이터베이스를 여는 메소드, 닫는 메소드에서 사용되게 됩니다.

  • 데이터베이스(SQLiteDatabase)의 인스턴스

    실제 데이터베이스에 접근할 때 사용하는 인스턴스입니다. 이 인스턴스에서 insert(), update(), query(), delete()등의 메소드를 호출하여 원하는 작업을 수행하게 됩니다.

    	private SQLiteDatabase mDb;
    

  • 데이터베이스 작업에 필요한 메소드들

    실제로 데이터베이스를 가지고 수행할 작업들에 대한 메소드입니다. 일반적으로 데이터 추가, 수정, 삭제, 질의(Query) 작업을 수행하게 되므로, 이에 적합한 작업을 수행하는 메소드를 작성합니다. 아래는 예제입니다. 

    	public long createBook(String name, String phone){ // 레코드 생성
    		ContentValues initialValues = new ContentValues();
    		initialValues.put(KEY_NAME, name);
    		initialValues.put(KEY_PHONE, phone);
    		
    		return mDb.insert(DATABASE_TABLE, null, initialValues);
    	}
    
    	public boolean deleteBook(long rowID){ // 레코드 삭제
    		return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowID, null) > 0;
    	}
    	
    	public Cursor fetchAllBooks(){ // 모든 레코드 반환
    		return mDb.query(DATABASE_TABLE, new String[]{KEY_ROWID, KEY_NAME, KEY_PHONE}, null, null, null, null, null);
    		
    	}
    	
    	public Cursor fetchBook(long rowID) throws SQLException{ // 특정 레코드 반환
    		Cursor mCursor =
    			mDb.query(true, DATABASE_TABLE, new String[]{KEY_ROWID, KEY_NAME, KEY_PHONE}, KEY_ROWID + "=" + rowID, null, null, null, null, null);
    		if(mCursor != null)
    			mCursor.moveToFirst();
    		return mCursor;
    	}
    	
    	public boolean updateBook(long rowID, String name, String phone){ // 레코드 수정
    		ContentValues args = new ContentValues();
    		args.put(KEY_NAME, name);
    		args.put(KEY_PHONE, phone);
    		
    		return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowID, null) > 0;
    	}
    


데이터베이스 어댑터 예제


아래는 데이터베이스 어댑터의 예제입니다. 저장하는 데이터는 String형 데이터 2개입니다.

package com.androidhuman.dbExample;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DbAdapter {
	
	public static final String KEY_NAME = "name";
	public static final String KEY_PHONE = "phone";
	public static final String KEY_ROWID = "_id";
	
	public static final int FIND_BY_NAME = 0;
	public static final int FIND_BY_PHONE = 1;
	
	private static final String TAG = "DbAdapter";
	private DatabaseHelper mDbHelper;
	private SQLiteDatabase mDb; // 데이터베이스를 저장
	
	private static final String DATABASE_CREATE =
		"create table data (_id integer primary key autoincrement,"+
		"name text not null, phone text not null);";
	
	private static final String DATABASE_NAME = "datum.db";
	private static final String DATABASE_TABLE = "data";
	private static final int DATABASE_VERSION = 1;
	
	private final Context mCtx;
	
	private class DatabaseHelper extends SQLiteOpenHelper{

		public DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
			// TODO Auto-generated constructor stub
		}
		
		public void onCreate(SQLiteDatabase db){
			db.execSQL(DATABASE_CREATE);
		}
		
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
			Log.w(TAG, "Upgrading db from version" + oldVersion + " to" +
					newVersion + ", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS data");
			onCreate(db);
		}
		
	}
	
	public DbAdapter(Context ctx){
		this.mCtx = ctx;
	}
	
	public DbAdapter open() throws SQLException{
		mDbHelper = new DatabaseHelper(mCtx);
		mDb = mDbHelper.getWritableDatabase();
		return this;
	}
	
	public void close(){
		mDbHelper.close();
	}
	
	public long createBook(String name, String phone){
		ContentValues initialValues = new ContentValues();
		initialValues.put(KEY_NAME, name);
		initialValues.put(KEY_PHONE, phone);
		
		return mDb.insert(DATABASE_TABLE, null, initialValues);
	}

	public boolean deleteBook(long rowID){
		return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowID, null) > 0;
	}
	
	public Cursor fetchAllBooks(){
		return mDb.query(DATABASE_TABLE, new String[]{KEY_ROWID, KEY_NAME, KEY_PHONE}, null, null, null, null, null);
		
	}
	
	public Cursor fetchBook(long rowID) throws SQLException{
		Cursor mCursor =
			mDb.query(true, DATABASE_TABLE, new String[]{KEY_ROWID, KEY_NAME, KEY_PHONE}, KEY_ROWID + "=" + rowID, null, null, null, null, null);
		if(mCursor != null)
			mCursor.moveToFirst();
		return mCursor;
	}
		
	public boolean updateBook(long rowID, String name, String phone){
		ContentValues args = new ContentValues();
		args.put(KEY_NAME, name);
		args.put(KEY_PHONE, phone);
		
		return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowID, null) > 0;
	}

	
}