SQLite
A. SQLite의 사용
B. Query
C. 자료의 표현(CursorAdapter)
A. SQLite의 사용
SQLite
데이터베이스는 어플리케이션이 자료를 다룬다면 중요한 요소입니다. 임베디드에 내장되는 소프트웨어 역시 DB를 가지고 있습니다. SQLite는 이러한 DB를 관리하는 DB엔진으로 2000년 리처드 힙 박사에 의해 개발된 무료 엔진입니다.
안드로이드에서는 데이터를 저장하는 장소가 웹이 아닌 파일이 될 가능성이 큽니다. 그러므로 원하는대로 복사, 삭제, 이동할 수 있습니다.
SQLite 관련 클래스
SQLite는 따로 GUI툴을 제공하지 않습니다. 편하게 테이블을 만들거나 할 수 없고 SQL문을 사용해야 합니다. 그래서 안드로이드에서는 DB를 관리할 수 있는, 그리고 SQLite를 편하게 쓸 수 있는 클래스를 제공합니다.
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
인수 | 설명 |
context | DB를 생성하는 컨텍스트입니다. 보통 메인 액티비티를 전달하게 됩니다. |
name | DB 파일의 이름입니다. |
factory | 커스텀 커서를 사용할 때 지정하는데 null을 사용하게 되면 표준 커서를 사용합니다. |
version | DB의 버전입니다. |
* 커서는 후에 설명하겠습니다.
SQLiteOpenHelper의 주요 메서드
이 클래스에서는 DB를 생성하거나 읽을 때에 다음의 메서드들을 호출합니다.
메서드 | 호출시기 | 설명 |
onCreate | 처음 DB생성 시 | 테이블을 만들고 초기 설정과 레코드들을 삽입합니다. |
onUpgrade | DB를 업그레이드 시 | 기존의 테이블을 변형시키거나 새로 만들게 되는 작업을 수행합니다. |
onOpen | DB를 열 때 | 파일이 존재한다면 이 메서드가 호출됩니다. |
DB를 쓰거나 읽기 위해서는 다음의 메서드를 이용합니다.
메서드 | 설명 |
getReadableDatabase | 읽기 전용으로 DB를 이용합니다. |
getWritableDatabase | 읽고 쓰기를 위해 DB를 이용합니다. |
close | DB를 닫는 것으로 위 두 작업 이후 해주어야 합니다. |
만약 DB파일이 없다면 파일을 생성한 후에 자동으로 onCreate를 호출하게 될 것입니다.
버전이 바뀐 경우는 onUpgrade를 호출하겠지요. 이 메서드들을 이용하면 DB를 열고 객체를 리턴받을 수 있습니다.
B. Query
DB를 가져왔다면 이를 사용해야 합니다.
안드로이드에서는 메서드를 이용해서 DB를 다룰 수도 있지만 직접 쿼리문을 사용해서 DB를 다룰 수도 있습니다.
메서드를 이용하는 것보다 쿼리문을 그대로 사용하는 게 더욱 편합니다.
그렇다면 이런 DB의 사용에 관해서 알아봅시다.
DB 가져오기
먼저 DB에 기록하거나 DB를 읽기 위해서는 앞서 본 메서드로 DB를 가져와야 합니다.
SQLiteDatabase db객체 = SQLiteHelper클래스.getWritableDatabase();
SQLiteHelper클래스의 경우는 SQLiteHelper를 상속해서 DB를 관리하기 위한 클래스를 하나 만들어줘야 합니다.
SQLiteHelper는 추상클래스 이기 때문에 위의 onCreate, onUpgrade, onOpen을 구현해야 하기 때문입니다.
데이터 맵 준비
DB를 불러왔으니 기록할 데이터를 작성합니다.
데이터는 기본적으로 키 값과 내용물로 구성됩니다. 그리고 이 데이터를 저장하기 위해서는 맵(Map)이 필요합니다.
이 맵을 생성시키는 생성자는 다음과 같습니다.
생성자 | 설명 |
ContentValues() | 비어있는 디폴트 크기의 맵을 생성합니다. |
ContentValues(int size) | size 만큼의 크기를 가진 맵을 생성합니다. |
ContentValues(ContentValuse from) | 인수로 받은 ContentValues의 복사본을 생성합니다. |
맵에 데이터를 넣는 메서드는 put메서드를 사용합니다. 모든 타입에 오버로드 되어 있으므로 필드의 타입에 맞는 것을 사용하면 됩니다.
void put(String key, 데이터의 타입 value)
사용 예
ContentValues data = new ContentValues();
data.put(“key1”, “데이터1 입니다”);
data.put(“key2”, “데이터2 입니다”);
DB에 데이터 삽입
메서드를 이용한 삽입
long SQLiteDatabase.insert(String table, String nullColumnHack, ContentValues values)
인수 | 설명 |
table | DB Table의 이름을 말합니다. |
nullColumnHack | DB에서는 완전히 비어있는 행을 삽입하는 것을 허용하지 않습니다. 대신 비어있다는 것을 표현하기 위해서 NULL을 쓰게 됩니다. 그래서 만약NULL이 들어가야 할 곳에 들어가게 될 것을 말합니다. |
values | 생성해 놓은 데이터 맵을 말합니다. |
쿼리를 이용한 삽입
void execSQL(String sql)
sql 인수는 SQL문이 들어가게 됩니다. SQL문은 삽입의 경우는 다음과 같습니다.
execSQL(“INSERT INTO 테이블 명 VALUES (데이터);”);
DB의 데이터 삭제
메서드를 이용한 삭제
조건에 맞는 데이터만을 삭제했다면 0, 모든 데이터를 삭제했다면 1을 리턴합니다.
int delete(String table, String whereClause, String[] whereArgs)
인수 | 설명 |
table | DB Table의 이름을 말합니다. |
whereClause | 조건항으로 해당 조건에 맞는 항을 삭제하라는 뜻입니다. null을 넣게 되면 모든 행을 삭제합니다. |
whereArgs | 조건을 규정하는 argument입니다. |
쿼리를 이용한 삭제
void execSQL(String sql)
SQL문은 삭제의 경우는 다음과 같습니다.
execSQL(“DELETE FROM 테이블 명 WHERE 조건;”);
DB의 데이터 갱신
메서드를 이용한 갱신
int update(String table, ContentValues values, String whereClause, String[] whereArgs)
인수 | 설명 |
table | DB Table의 이름을 말합니다. |
values | 갱신 내용을 말합니다. |
whereClause | 조건항으로 해당 조건에 맞는 항을 삭제하라는 뜻입니다. null을 넣게 되면 모든 행을 삭제합니다. |
whereArgs | 조건을 규정하는 argument입니다. |
쿼리를 이용한 갱신
void execSQL(String sql)
SQL문은 삭제의 경우는 다음과 같습니다.
execSQL(“UPDATE 테이블 명 SET 갱신 내용 WHERE 조건;”);
DB의 데이터 검색
메서드를 이용한 검색
엄청나게 인수가 많지만 대부분 null이 될 가능성이 큽니다.
Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
인수 | 설명 |
distinct | 검색되는 행 중에서 중복되는 내용은 제거하고 출력합니다. null을 지정하면 모두 출력합니다. 이 행을 제외하고도 쓸 수 있습니다. |
table | DB Table의 이름을 말합니다. |
columns | 보여줄 열을 지정합니다. null을 지정하면 모든 열을 보여줍니다. |
selection | 어떤 행을 검색할지 정합니다. WHERE에 해당하는 내용으로 null을 지정하면 모든 행을 출력합니다. |
selectionArgs | |
groupBy | GROUP BY에 해당하는 내용으로 특정 속성을 기준으로 그룹을 지어 출력합니다. 설명이 힘들기 때문에 SQL문을 참조하시기 바랍니다. null을 지정하면 그룹을 만들지 않습니다. |
having | GROUP BY에서 조건을 정하는 부분입니다. 해당 속성 기준으로 조건에 만족하는 것만 출력합니다. |
orderBy | ORDER BY에 해당하는 내용으로 행을 어느 속성 기준으로 정렬시킬지 정합니다. null의 경우 정렬시키지 않습니다. |
limit | LIMIT에 해당하는 내용으로 출력 행의 수를 제한합니다. |
쿼리를 이용한 검색
void execSQL(String sql)
SQL문은 검색의 경우는 다음과 같습니다.
execSQL(“SELECT 속성 FROM 테이블 명 WHERE 조건
GROUP BY 속성 HAVING 조건 ORDER BY 속성 LIMIT 행의 수”);
SQLite 이용 예제
public class EnglishWord extends Activity {
WordDBHelper mHelper;
EditText mText;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.data_englishword);
mHelper = new WordDBHelper(this);
mText = (EditText)findViewById(R.id.edittext);
findViewById(R.id.insert).setOnClickListener(mClickListener);
findViewById(R.id.delete).setOnClickListener(mClickListener);
findViewById(R.id.update).setOnClickListener(mClickListener);
findViewById(R.id.select).setOnClickListener(mClickListener);
}
Button.OnClickListener mClickListener = new View.OnClickListener() {
public void onClick(View v) {
SQLiteDatabase db;
ContentValues row;
switch (v.getId()) {
case R.id.insert:
db = mHelper.getWritableDatabase();
// insert 메서드로 삽입
row = new ContentValues();
row.put("eng", "boy");
row.put("han", "머스마");
db.insert("dic", null, row);
// SQL 명령으로 삽입
db.execSQL("INSERT INTO dic VALUES (null, 'girl', '가시나');");
mHelper.close();
mText.setText("Insert Success");
break;
case R.id.delete:
db = mHelper.getWritableDatabase();
// delete 메서드로 삭제
db.delete("dic", null, null);
// SQL 명령으로 삭제
//db.execSQL("DELETE FROM dic;");
mHelper.close();
mText.setText("Delete Success");
break;
case R.id.update:
db = mHelper.getWritableDatabase();
// update 메서드로 갱신
row = new ContentValues();
row.put("han", "소년");
db.update("dic", row, "eng = 'boy'", null);
// SQL 명령으로 갱신
//db.execSQL("UPDATE dic SET han = '소년' WHERE eng = 'boy';");
mHelper.close();
mText.setText("Update Success");
break;
case R.id.select:
db = mHelper.getReadableDatabase();
Cursor cursor;
// query 메서드로 읽기
//cursor = db.query("dic", new String[] {"eng", "han"}, null,
// null, null, null, null);
// SQL 명령으로 읽기
cursor = db.rawQuery("SELECT eng, han FROM dic", null);
String Result = "";
while (cursor.moveToNext()) {
String eng = cursor.getString(0);
String han = cursor.getString(1);
Result += (eng + " = " + han + "\n");
}
if (Result.length() == 0) {
mText.setText("Empyt Set");
} else {
mText.setText(Result);
}
cursor.close();
mHelper.close();
break;
}
}
};
}
class WordDBHelper extends SQLiteOpenHelper {
public WordDBHelper(Context context) {
super(context, "EngWord.db", null, 1);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE dic ( _id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"eng TEXT, han TEXT);");
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS dic");
onCreate(db);
}
}
C. 자료의 표현(CursorAdapter)
커서(Cursor)
앞서 보면 종종 커서에 대한 얘기가 나옵니다. 일반적인 커서의 뜻은 위치를 가리키는 곳이라는 뜻입니다.
안드로이드에서는 결과 셋의 위치를 가리키는 포인터라고 이해하면 됩니다. 커서를 이용하면 대단히 많은 양이라고 할지라도 위치를 가리키는 특성 때문에 부하가 높지 않습니다.
커서를 이용해 쿼리 읽기
검색을 한 경우 리턴되는 커서는 가장 처음 레코드의 앞을 가리킵니다. 이후에 moveToNext를 이용하여 첫 레코드부터 차례로 다 읽을 수 있습니다. 보통 while문을 사용합니다.
while(cursor.moveToNext()){ … }
어댑터를 이용한 출력
커서를 어댑터에 바인딩해 놓으면 어댑텨 뷰로 출력할 수가 있습니다. 커서를 사용하는 어댑터는 아래와 같습니다.
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
인수 | 설명 |
context | 컨텍스트를 의미합니다. 보통 메인 액티비티가 됩니다. |
layout | 커서를 출력할 레이아웃을 말합니다. |
c | 검색한 데이터의 원본인 커서를 말합니다. |
from | 열 이름을 배열로 지정합니다. |
to | 각 열이 어느 위젯으로 출력될지 위젯의 ID를 지정합니다. |
커서 어댑터 사용 예제
public class ProductList extends Activity {
ProductDBHelper mHelper;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.data_productlist);
mHelper = new ProductDBHelper(this);
Cursor cursor;
SQLiteDatabase db = mHelper.getWritableDatabase();
cursor = db.rawQuery("SELECT * FROM product", null);
startManagingCursor(cursor);
SimpleCursorAdapter Adapter = null;
Adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_2,
cursor, new String[] { "name", "price" },
new int[] { android.R.id.text1, android.R.id.text2});
ListView list = (ListView)findViewById(R.id.list);
list.setAdapter(Adapter);
}
}
class ProductDBHelper extends SQLiteOpenHelper {
public ProductDBHelper(Context context) {
super(context, "Product.db", null, 1);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE product ( _id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"name TEXT, price INTEGER);");
db.execSQL("INSERT INTO product VALUES (null, '오징어 땅콩', 900);");
db.execSQL("INSERT INTO product VALUES (null, '농심 포테이토 칩', 2000);");
db.execSQL("INSERT INTO product VALUES (null, '로보트 태권 V', 1000);");
db.execSQL("INSERT INTO product VALUES (null, '꼬마 자동차 붕붕', 1500);");
db.execSQL("INSERT INTO product VALUES (null, '윈도우즈 API 정복', 32000);");
db.execSQL("INSERT INTO product VALUES (null, '롯데 인벤스 아파트', 190000000);");
db.execSQL("INSERT INTO product VALUES (null, '88 라이트', 1900);");
db.execSQL("INSERT INTO product VALUES (null, '프라이드 1.6 CVVT 골드', 8900000);");
db.execSQL("INSERT INTO product VALUES (null, '캐리비안 베이 입장권', 25000);");
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS product");
onCreate(db);
}
}
* 포스트의 내용은 김상형 저, "안드로이드 프로그래밍 정복"의 내용을 참고하였습니다.
본 블로그는 페이스북 댓글을 지원합니다.