[안드로이드] SQLite 데이터베이스 이용하기

2010.04.22 10:19

   

   

<목표>  [안드로이드] SQLite 데이터베이스 이용하기

 

   

 오늘은 안드로이드 개발에 있어서 없어서는 안될 데이터베이스의 사용법에 대해 알아보겠습니다.안드로이드는 모바일 환경에 알맞은 SQLite 데이터베이스를 채택하고 있습니다. 기본의 다른 데이터베이스와의 큰 차이는 없습니다.다른 점이라면, 일반적은 데이터베이스는 테이블 생성시 각 속성에 대한 타입을 지정합니다. 하지만 SQLite는 타입을 지정하는 것이 없습니다. 즉, int, string, text 등의 타입을 지정할 수가 없다는 말이죠. 그러나 메모리와 속도면에서 소규모의 데이터베이스를 운영하는 데 있어서는 이점이 있습니다.

 데이터베이스의 사용법은 기존의 데이터베이스를 한번이라도 다뤄보신적 있으신 분은 별 어려움 없이 사용하실 수 있을 것입니다. 처음 접하는 사람들 역시, 기존에 있는 샘플코드를 이용하여 조금만 수정해서 사용하시면, 별 어려움 없이 프로그램에 적용시키실 수 있을 겁니다.

 샘플 코드는 인터넷에 널리 퍼져 있는 코드를 정리한 것입니다. 간단한 노트 기능으로 타이틀과 바디를 가지는 테이블이 있고, 이를 이용하여 데이터를 추가, 삭제, 업데이트 등을 수행할 수 있습니다.

   

   

  STEP 1 자바 소스 코드 

 

 자바 코드는 크게 두가지로 나뉘어집니다. 데이터베이스를 컨트롤 하는 객체와 이 객체를 사용하여 데이터베이스를 접근하는 엑티비티입니다.

 아래의 예제는 데이터베이스를 컨트롤 하는객체(NotesDbAdapter) 입니다. 이 클래스 내부에 DatabaseHelper객체가 있어 데이터베이스를 관리합니다. 이는 안드로이드에서 제공하는 SQLiteOpenHelper를 상속받아 간단히 만들 수 있습니다. DatabaseHelper 객체에는 크게 세 가지 함수가 존재합니다. 생성자, onCreate, onUpdate 가 있습니다. 말 그대로 onCreate는 데이터베이스를 생성하면서, 데이터베이스 이름과 버전을 설정할 수 있습니다. 이 부분에 쿼리문을 이용하여 데이터베이스의 테이블을 생성합니다. onUpdate는 말 그대로 업데이트가 필요할 시 수행이 됩니다. 현재의 데이터베이스 버전과 업데이트 하려는 데이터베이스의 버전을 비교하여, 낮은 버전일 경우 새롭게 테이블을 구성한다던가, 다른 조작 등을 취할 수 있습니다.

   

  [DBAdapter] 데이터베이스 관리 클래스

 

package com.pulsewings.exceltodatabase;

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 NotesDbAdapter {

	public static final String KEY_TITLE = "title";
	public static final String KEY_BODY = "body";
	public static final String KEY_ROWID = "_id";
	private static final String TAG = "NotesDbAdapter";

	private DatabaseHelper mDbHelper;
	private SQLiteDatabase mDb;

	/**
	 *
	 * Database creation sql statement
	 */

	private static final String DATABASE_CREATE = "create table notes (_id integer primary key autoincrement, "
			+ "title text not null, body text not null);";

	private static final String DATABASE_NAME = "data";
	private static final String DATABASE_TABLE = "notes";
	private static final int DATABASE_VERSION = 2;
	private final Context mCtx;

	private static class DatabaseHelper extends SQLiteOpenHelper {

		DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL(DATABASE_CREATE);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion
					+ ", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS notes");
			onCreate(db);
		}
	}

	public NotesDbAdapter(Context ctx) {
		this.mCtx = ctx;
	}

	public NotesDbAdapter open() throws SQLException {
		mDbHelper = new DatabaseHelper(mCtx);
		mDb = mDbHelper.getWritableDatabase();
		return this;
	}

	public void close() {
		mDbHelper.close();
	}

	public long createNote(String title, String body) {
		ContentValues initialValues = new ContentValues();
		initialValues.put(KEY_TITLE, title);
		initialValues.put(KEY_BODY, body);
		return mDb.insert(DATABASE_TABLE, null, initialValues);
	}

	public boolean deleteNote(long rowId) {
		Log.i("Delete called", "value__" + rowId);
		return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
	}

	public Cursor fetchAllNotes() {
		return mDb.query(DATABASE_TABLE, new String[] { KEY_ROWID, KEY_TITLE, KEY_BODY }, null, null, null, null, null);
	}

	public Cursor fetchNote(long rowId) throws SQLException {

		Cursor mCursor = mDb.query(true, DATABASE_TABLE, new String[] { KEY_ROWID, KEY_TITLE, KEY_BODY }, KEY_ROWID
				+ "=" + rowId, null, null, null, null, null);
		if (mCursor != null) {
			mCursor.moveToFirst();
		}
		return mCursor;
	}

	public boolean updateNote(long rowId, String title, String body) {
		ContentValues args = new ContentValues();
		args.put(KEY_TITLE, title);
		args.put(KEY_BODY, body);
		return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
	}

}

   

 그 밖에는 함수명에서도 볼 수 있듯이 데이터를 추가, 삭제, 업데이트하는 기능입니다. 기존의 데이터베이스와 다른 점은 쿼리문을 이용하지 않고 데이터를 조정이 가능한 것입니다. ContentValues 라는 타입을 이용하여 기존에 있는 테이블의 속성명과 조작하려는 인스턴스를 넣어 한꺼번에 데이터베이스로 요청할 수 있습니다. Insert, update 같이 데이터베이스 객체 내에 있는 함수를 이용하여 쿼리문 없이 데이터베이스 조작이 가능합니다. 이러한 함수는 내부에서 직접 쿼리문을 만들어 데이터베이스로 쿼리문을 날립니다.

 자신이 사용하고 싶은 테이블을 구성하신 뒤에, 이 코드를 자신에 맞는 테이블로 바꾸시면 큰 어려움 없이 데이터베이스를 이용하실 수 있으실 겁니다.

 

  [Bouns] Cursor 사용

 

인자내용
moveToFirst 커서가 쿼리(질의) 결과 레코드들 중에서 가장 처음에 위치한 레코드를 가리키도록 합니다.
moveToNext 다음 레코드로 커서를 이동합니다.
moveToPrevious 이전 레코드로 커서를 이동합니다.
getCount 질의 결과값(레코드)의 갯수를 반환합니다.
getColumnIndexOrThrow 특정 필드의 인덱스값을 반환하며, 필드가 존재하지 않을경우 예외를 발생시킵니다.
getColumnName 특정 인덱스값에 해당하는 필드 이름을 반환합니다.
getColumnNames 필드 이름들을 String 배열 형태로 반환합니다.
moveToPosition 커서를 특정 레코드로 이동시킵니다.
getPosition 커서가 현재 가리키고 있는 위치를 반환합니다.

 

 데이터베이스의 특성상 하나의 테이블의 레코드를 읽어 오기 위해서는 커서라는 것이 필요합니다. 조건에 맞는 레코드를 한꺼번에 모두 들고 올 수가 없기 때문에 커서를 이용해서 조작을 합니다. 말 그대로 커서는 현재 레코드를 가리키고 있는 곳을 말합니다. 하나씩 이 커서를 이동하면서 레코드 하나하나씩을 접근해서 가져옵니다. 이 커서 객체를 이용하여 get을 하게 되면 컬럼 번호에 맞게 데이터를 가져올 수 있습니다.

 

  [Activity] 데이터베이스 이용 엑티비티

 

package com.pulsewings.exceltodatabase;

import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class DatabaseTestActivity extends Activity {

	private NotesDbAdapter dbAdapter;
	private static final String TAG = "NotesDbAdapter";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		Log.d(TAG, "DatabaseTest :: onCreate()");
		dbAdapter = new NotesDbAdapter(this);
		dbAdapter.open();

		Button bt = (Button) findViewById(R.id.inputButton);
		bt.setOnClickListener(new View.OnClickListener() {

			public void onClick(View v) {
				dbAdapter.createNote("title", "body");
				TextView tv = (TextView) findViewById(R.id.textView1);
				tv.setText("데이터베이스에넣었습니다.");
				TextView tv1 = (TextView) findViewById(R.id.textView2);
				tv1.setText("Title과 Body를데이터베이스에저장하였습니다.");
				Log.d(TAG, "First Button Click");
			}
		});

		Button bt1 = (Button) findViewById(R.id.outputButton);
		bt1.setOnClickListener(new View.OnClickListener() {

			public void onClick(View v) {
				Cursor result = dbAdapter.fetchAllNotes();
				result.moveToFirst();
				while (!result.isAfterLast()) {
					String title = result.getString(1);
					String body = result.getString(2);
					TextView tv = (TextView) findViewById(R.id.textView1);
					tv.setText(title);
					TextView tv1 = (TextView) findViewById(R.id.textView2);
					tv1.setText(body);
					result.moveToNext();
				}

				Log.d(TAG, "Second Button Click");
				result.close();
			}

		});

	}

}

   

 위의 코드는 실제 엑티비티에서 데이터베이스를 이용하는 것을 나타내는 소스코드입니다. 위에서 살펴본 NotesDbAdapter 객체를 생성하여, open()을 한 뒤 사용하면 됩니다. 사용하는 법은 너무나도 직관적이라 생략하겠습니다. While문을 이용하여 커서를 이용하여 테이블에 있는 모든 레코드를 가져와서 화면에 뿌려주는 것을 수행하는 코드입니다.

   

  STEP 2 Xml 코드

   

 Xml코드에는 간단히 테스트할 수 있는 TextView객체 두 개만 생성하고 있습니다.

   

  STEP 3 AndroidManifest.xml 코드

 

 데이터베이스를 이용하기 위해서 AndroidManifest.xml 코드 수정은 필요하지 않습니다.

   

 

 

<마무리>  SQLite 데이터베이스 사용하기

 

 데이터베이스를 사용하기 위해서는 안드로이드에서 제공하는 SQLiteOpenHelper를 이용하여 간단히 데이터베이스를 조작할 수 있습니다. 이 것을 상속받아 객체를 만들고 이 객체를 자신의 데이터베이스에 맞게 조작하도록 클래스(NotesDbAdapter)를 만들고, 엑티비티에서는 이 클래스를 생성하여 데이터베이스를 직접 사용할 수 있게 합니다. 간단한 샘플 코드를 이용하여 자신의 데이터베이스 테이블에 맞게 수정하여 사용하면 간단히 데이터를 저장할 수 있는 환경이 됩니다.

   

 

 


Posted by 
맥박맥박의 개발 일지

 

  • 2013년 7월 25일 업데이트하였습니다.

 

 

맥박 안드로이드 , , ,

  1. Blog Icon
    오창근

    많은 도움이 되는 정보였습니다. 간단하게 데이터베이스로 접근할 수 있었네요. 근데 한가지 안되는 부분이 있어서 질문드립니다. 저는 데이터베이스의 모든 값을 시현하길 원하는데, 지금 올려놓으신 Activity를 똑같이 적용해본 결과 데이터베이스의 맨 마지막값 하나만 textView로 시현하게 되는데요. 어떻게 바꿔주면 좋을까요? main.xml은 다음과 같이 간단하게 짰습니다.

    <?xml version="1.0" encoding="utf-8"?>


    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
    android:id="@+id/TextView1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
    <TextView
    android:id="@+id/TextView2"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
    <Button
    android:id="@+id/outputButton"
    android:layout_height="wrap_content"
    android:text="load inventories"
    android:lines="2"
    android:layout_gravity="center_vertical"
    android:layout_width="fill_parent"
    />

    </LinearLayout>

  2. 아래의 코드는 버튼을 눌렸을 때, 처리되는 코드입니다. (위의 코드에서 가져왔습니다.)

    public void onClick(View v) {

    Cursor result = dbAdapter.fetchAllNotes();
    result.moveToFirst();
    while(!result.isAfterLast()){
    String title = result.getString(1);
    String body = result.getString(2);
    TextView tv = (TextView)findViewById(R.id.textView1);
    tv.setText(title);
    TextView tv1 = (TextView)findViewById(R.id.textView2);
    tv1.setText(body);
    result.moveToNext();
    }

    위의 보시면 아시겠지만, while문을 통해서 데이터베이스의 값을 계속 받아오고 있습니다. 즉, 안드로이드 엑티비티상에서 모든 값을 순서대로 불러오고 있고, while문이 계속 다음값으로 텍스트뷰를 세팅시키고 있는 구조입니다. 당연히, 사람이 보게되는 것은 데이터베이스의 마지막값이 텍스트뷰에 보여지게 되는 것이죠. 현재 이 소스에서 title과 body 값을 리스트에 저장을 하시거나, 반복될때마다, 원하시는 처리를 해주시면 간단히 해결될 것으로 보입니다~^^

  3. Blog Icon
    오창근

    말씀하신대로 list에 저장하는 방식으로 해결했습니다. textview로는 해결하기가 쉽지 않더군요. 감사합니다.

  4. Blog Icon
    자신감

    잘 배우고 있습니다.
    질문있는데요
    insert함수..creatnote함수가 왜 long형 인가요?

    int형보다 길어서인가요?
    그리고delete 함수는 왜 boolean인지요

    생각해도 잘 모르겠어서요

    그리고 보너스 스펠링이 바뀌었습니다.

  5. Blog Icon
    푸릉푸릉

    sqlite에 왜 int, string, text 속성이 없다는거죠? 자바에만 없나요?

  6. sqlite가 타입 체크를 하지 않는다는 것으로 알고 있습니다. 이 글의 도입 부분을 읽어보시면,
    타입을 지정하지만, 타입체크는 안 한다는 소리 같습니다.

  7. Blog Icon
    화이팅

    테이블을 여러개 사용하고 싶은데

    가능한가요..?

  8. Blog Icon
    서쳐

    sqllite관련 자료 찾다가 우연히 들렸습니다 좋은 도움되었습니다 감사합니다.:)

  9. Blog Icon
    초보로이드

    안녕하세요!

    제가 가지고 있는 txt 파일(단어들이 들어있음)을 .db 형태로 변경해서 프로그램에 넣고 싶은데요~~
    이미 내용은 완성된 txt 파일을 .db 파일로 변경하려면 어떻게 해야 하나요??
    (ps. 윈도우 유저입니다)

  10. Blog Icon
    웃는개발자

    오라클 SQL문 입니다.

    SELECT NVL ((SELECT max(cat_quest_no) FROM tctquest a ) + 1,
    '1000000000') cat_quest_no
    FROM DUAL

    안드로이드 SQLite를 사용해서 위와 같은 SQL문을 만들수 있나요????

  11. 감사합니다~ 담아갈게요~