Android byDreamy postedMar 03, 2015

안드로이드에서 Database를 다뤄보자

?

단축키

Prev이전 문서

Next다음 문서

ESC닫기

+ - Up Down Comment Print

출처 : http://egloos.zum.com/androcat/v/1776001


SQLite 1

-> SQLite는 다른 프로그램에 임베팅하기 좋으면서도 깔끔한 SQL 인터페이스를 제공
-> 메모리도 적게 사용하면서 속도도 빠르다.
-> 실행파일과 소스 코드가 무료이고 공개되어 있기 때문에 많이 사용된다.
-> 안드로이드는 SQLite를 내장하고 있으며, 모든 안드로이드 애플리케이션은 간단하게 SQLite 데이터베이스를 생성해 활용할수 있다.
-> SQLite는 표준 SQL 인터페이스를 사용한다.
-> SQLite가 JDBC를 기본 API로 제공하지 않고, 휴대폰과 같은 환경에서 JDBC와 같은 규모 있는 프레임웍은 여러모로 무리가 된다.
-> 액티비티는 일반적으로 컨텐트 프로바이더나 서비스 등을 통해 데이터베이스에 접근한다.


SQLite 2

-> SQL 문법에 맞는 명령을 통해 데이터를 가져오거나(SELECT) 데이터를 변경하고(INSERT 등) 데이터 구조를 정의하는(CREATE TABLE 등) 작업을 처리한다.
-> 실제 데이터를 추가할 때는 컬럼마다 데이터 타입에 상관없이 어떤 데이터라도 마음대로 넣을수 있다. 예를 들어 INTEGER로 정의된 컬럼에 문자열 값도 아무런 문제없이 넣을수 있다.
-> 위와 같은 기능을 매니페스트 타입이라고 표현한다.
-> 매니페스트 타입 입장에서 보면 데이터 타입은 컬럼 자체가 아닌 개별값에 연결되는 속성이다. 따라서 SQLite는 애초에 해당 컬럼에 지정된 데이터 타입과 상관없이 어떤 데이터 타입의 어떤 값이라도 아무 컬럼에나 집어넣을수 있다.


-> 표준 SQL 구문에 정의된 기능 가운데 지원하지 않는 기능 : FOREIGN KEY, 중첩 트랜잭션, RIGHT OUTER JOIN, FULL OUTER JOIN, ALTER TABLE 등

 
기초

-> 데이터베이스를 생성하고 오픈하려면 SQLiteOpenHelper 객체를 사용한다.
-> SQLiteOpenHelper 클래스는 애플리케이션에서 요구하는 내용에 따라 데이터베이스를 생성하거나 업그레이드하는 기능을 제공한다.
-> SQLiteOpenHelper 클래스를 상속받아 구현하려면 다음과 같은 세가지 기능을 준비해야한다
-> 1. 생성 메소드 : 상위 클래스의 생성 메소드를 호출, Activity 등의 Context 인스턴스와 데이터베이스의 이름, 커서 팩토리(보통 Null 지정) 등을 지정하고, 데이터베이스 스키마 버전을 알려주는 숫자값을 넘겨 준다.
-> 2. onCreate() 메소드 : SQLiteDatabase를 넘겨 받으며, 데이블을 생성하고 초기 데이터를 추가하기에 적당한 위치이다.
-> 3. onUpgrade() 메소드 : SQLiteDatabase 인스턴스를 넘겨 받으며, 현재 스키마 버전과 최신 스키마 버전 번호도 받는다.

-> SQLiteOpenHelper를 상속받은 클래스를 사용하려면 먼저 인스턴스를 하나 생성한 다음, 하려는 작업이 읽기 전용인지 여부에 따라 getReadableDatabase()나 getWritealbleDatabese() 메소드를 호출 한다.

 

db = (new DatabaseHelper(getContext())).getWritableDatabase();
return (db == null) ? false : ture;


-> 결과적으로 db 변수에 SQLiteDatabase 인스턴스를 받아오게 되는데, SQLiteDatabase 인스턴스를 사용해 데이터를 호출하거나 내용을 변경 할수 있다.
-> 액티비티가 종료되는 등 데이터베이스를 모두 사용하고 나면 SQLiteDatabase인스턴스의 close() 메소드를 호출해 연결을 해제한다.


테이블 준비

-> 테이블과 색인 등을 생성하려면 원하는 DDL 구문을 준비해서 SQLiteDatabase 인스턴스의 execSQL() 메소드를 호출해야 한다.
 

db.execSQL("CREATE TABLE constants (_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, value REAL);");


-> constants라는 테이블이 생성되고, constants 테이블 기본키로 _id 컬럼이 사용되며 _id 컬럼은 정수형의 숫자값이 자동으로 증가되는 컬럼이다.
-> 실제 데이터는 title이라는 문자열 컬럼과 value라는 실수형 컬럼에 들어간다.
-> 키본키에 해당하는 컬럼에 대해서는 자동으로 색인을 생성한다.
-> 다른 컬럼에도 색인이 필요하다면 CREATE INDEX 구문으로 색인을 건다.
-> 테이블이나 색인을 제거해야 하는 상황이라면 DROP INDEX나 DROP TABLE 구문을 execSQL()로 실행하면 된다.



데이터 추가

-> 데이터를 추가하는 방법은 2가지가 있다.
-> 1. execSQL() 메소드는 값을 가져오는 문장이 아닌 INSERT, UPDATE, DELETE등의 모든 SQL 구문을 처리할 수 있다.
 

db.execSQL("INSERT INTO widgets (name, inventory)" + "VALUES ('Sprocket', 5)");


-> 2. SQLiteDatabase 클래스에서 제공하는 insert(), update(), delete() 등의 메소드를 사용하는 방법이 있다. 이와같은 개별 메소드는 인자로 입력받은 값을 조합해 최종적으로 SQL 문장을 동일하게 실핼하도록 만들어져 있다.
-> 개별 메소드는 Map과 비슷한 구조로 만들어져 있으면서 SQLite의 데이터 타입에 맞춰 동작하도록 구성된 ContentValues 객체를 사용해 동작한다.
-> 지정한 키에 해당하는 값을 찾아올때 단순하게 get() 메소드를 사용하는 대신, getAsInteger(), getAsString() 등의 메소드를 호출한다.
-> insert() 메소드는 대상이 되는 테이블 이름, null 처리 컬럼명, ContentValues 객체에 컬럼별 값을 넣어 인자로 넘겨 준다.
-> SQLite는 값이 하나도 없는 행은 허용하지 않는다. 따라서 ContentValues 인스턴스 값이 하나도 없는 경우 행이 생성되지 않기 때문에 이런경우 null 처리 컬럼 이름으로 지정된 컬럼값으로 NULL을 지정해 행이 생성되게 한다.

 
contentValues cv = new ContentValues();
cv.put(ContentValues.TITLE, "Gravity, Death Star I");
cv.put(ContentValues.VALUE, SensorManager.GRAVITY_DEATH_STAR_I);
db.insert("constants", getNullColumnHack(), cv);


-> update() 메소드는 대상 테이블 이름과 변경할 값이 들어 있는 ContentValues 객체를 넘겨준다.
-> 값을 변경할 대상을 한정지으려면 WHERE 구문과 함께 WHERE 조건에 해당하는 값 역시 넘겨줌
-> WHERE 구문에 물음표로 지정된 위치에 각자의 값이 배치돼 처리된다.
-> update() 메소드는 다른 정보를 사용해 계산된 값이 아닌 고정된 값을 갖는 컬럼만 변경할 수 있으므로, 필요한 경우에는 execSQL() 메소드를 사용해야 할 수도 있다.
-> WHERE 구문에 표시된 물음표와 각 조건값을 지정하는 방법은 다른 SQL API에서 많이 사용하던 방법과 별반 다르지 않다.

 

// replacements는 ContentValues 인스턴스
String[] parms = new String[] {"snicklefritz"};
db.update("widgets", replacements, "name=?", parms); 


-> delete() 메소드 역시 테이블 이름과 WHERE 구문을 사용한다.
-> 변경할 값을 지정하지 않는다는 점만 제외하면 update()와 동일한 방법으로 동작한다.
 

데이터 불러오기

-> INSERT, UPDATE, DELETE와 비슷하게 SELECT 구문으로 데이터를 가져올때도 2가지 방법을 사용할 수 있다.
-> 1. rawQuery() 메소드를 사용해 SELECT 구문을 직접 실행
-> 2. query() 메소드를 인자로 각 부분의 값을 넘겨 실행
-> SQLiteQueryBuilder 클래스와 관련된 부분과 커서와 커서 팩토리에 관한 부분이 가장 복잡하다.

 

SQL문 직접 작성
-> API 호출 방법만 놓고 보면 rawQuery() 메소드를 사용하는 방법이 가장 간단하다.
-> rawQuery() 메소드에 SELECT 구문을 넘겨 주기만 하면 된다.
-> SELECT 구문 역시 위치에 맞는 인자 배열을 함께 넘겨 줄수 있다.
 

Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='constants'", null);

 

-> 위의 코드는 SQLite의 기본 테이블 가운데 하나인 sqlite_master 테이블 내용을 가져오는데, 내용으로 보면 constants라는 이름의 테이블이 만들어져 있는지 확인하는 구문이다.
-> 결과로 받아오는 값은 Cursor 인스턴스인데, Cursor를 사용하면 여러 건의 결과를 하나씩 받아오면서 처리할수 있다.
-> SELECT 문장이 동적으로 변경되지 않고 아예 프로그램 내부에 고정시켜버릴 예정이라면 위 방법이 가장 간단하다.

-> SELECT 구문 가운데 이부분이 동적으로 변경되거나, 위치로 인자값을 지정하는 방법으로 한계가 있는 수준이라면 굉장히 복잡해진다.
-> 예를 들어 값을 가져와야 할 컬럼 개수가 개발 당시에 정해지지 않고 동적으로 변경된다면 처리하기 쉽지 않다. 그렇다고 해서 컬럼 이름을 필요할 때마다 쉼표로 연결해 사용하는건 물론 좋은 방법이 아니다. 이런 경우에는 query() 메소드를 사용하는게 훨씬 간편하다.
 

일정한 형식의 쿼리

-> query() 메소드는 SELECT 구문의 각 부분을 쪼개 각 인자로 넘겨받고, 최종적으로는 SELECT문을 생성해 실행한다.
-> query() 가 받아서 처리하는 인자의 순서
-> 1. 대상 테이블 이름
-> 2. 값을 가져올 컬럼 이름의 배열
-> 3. WHERE 구문. 물음표를 사용해 인자의 위치를 지정할 수 있다.
-> 4. WHERE 구문에 들어가는 인자값
-> 5. GROUP BY 구문
-> 6. ORDER BY 구문
-> 7. HAVING 구문
-> 테이블 이름을 제외한 각 값이 필요없는 경우라면 null을 지정한다.

 

String[] columns={"ID", "inventory"};
Steing[] parms={"snicklefritz"};
Cursor result=db.query("widgets", columns, "name=?", parms, nullnullnull);
 

쿼리 구문 생성

-> SQLiteQueryBuilder 클래스를 활용하면 훨씬 다양한 방법으로 UNION이나 하위 쿼리 등을 포함하는 복잡한 구문을 생성할 수 있다.
-> SQLiteQueryBuilder 클래스가 ContentProvider 인터페이스와 완벽하게 맞아 떨어진다.
-> 컨텐트 프로바이더의 query() 메소드를 구현하는 가장 일반적인 방법은 SQLiteQueryBuilder 인스턴스를 생성하고 기본값을 일부 채워넣은 다음 전체 쿼리를 생성하고 실행할수 있게 구성한다.
-> SQLiteQueryBuilder 클래스를 사용해 요청을 처리하는 컨텐츠 프로바이더의 코드

 

@Override
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sort){
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

qb.setTables(getTableName());
 

if (isCollectionUri(url)){
    qb.setProjectionMap(getDefaultProjection());
}else{
    qb.appendWhere(getIdColumnName()+"="+url.getPathSegments().get(1));
}


String orderBy;

 

if (TextUtils.isEmpty(sort)){
    orderBy=getDefaultSortOrder();
}else{
    orderBy=sort;
}


    Cursor c = qb.query(db, projection, selection, selectionArgs, nullnull, orderBy); 
    c.setNotificationUri(getContext().getContentResolver(), url);
    return c;
}

 

-> 1. SQLiteQueryBuilder 인스턴스를 생성
-> 2. 쿼리에 사용할 테이블 이름을 설정 (setTables(getTableName())).
-> 3. 값을 가져올 기본 컬럼 이름의 목록을 지정하거나 (setProjectionMap()), 또는 Uri값에 들어 있는 ID값으로 테이블 항목 가운데 특정한 값을 가져올 수 있도록 WHERE 구문을 추가했다(appendWhere()).
-> 4. 마지막으로 기본값과 요청이 들어온 값을 조합해 생성된 쿼리 구문을 실행한다.
(qb.query(db, projection, selection, selectionArgs, null, null, orderBy))

 

-> SQLiteQueryBuilder에서 쿼리를 직접 실행하는 대신 buildQuery() 메소드를 호출해 최종 생성된 SELECT 구문만을 리턴하게 할 수도 있다. 그러면 넘겨 받은 SELECT문을 필요할때 실행할수 있다.

 

커서 활용

-> SELECT 쿼리를 어떻게 실행하건 간에 그 결과로는 Cursor 인스턴스를 받는다.
-> 커서 개념을 안드로이드와 SQLite에서 구현한 클래스가 바로 Cursor다.
-> getCount() 메소드 : 전체 결과 건수가 몇개인지 확인할 수 있다.
-> moveToFirst(), moveToNext(), isAfterLast() 등의 메소드 : 결과건을 모두 확인할수있다.
-> getColumnNames() 메소드 : 결과에 포함된 전체 컬럼 이름을 알수 있다.
-> requery() 메소드 : 쿼리를 재실행 할수 있다.
-> close() 메소드 : 커서가 확보한 자원을 모두 해제한다.
 

Cursor result = db.rawQuery("SELECT ID, namem inventory FROM widgets");
 

result.moveToFirst();
 

while (!result.isAfterLast()){
   int id = result.getInt(0);
   String name = result.getString(1);
   int inventory = result.getInt(2);
//실제 필요한 작업 처리
 

result.moveToNext();
}
result.close();
-> widgets 테이블에 있는 항목을 모두 가져와 모든 결과값을 뽑아내는 반복문 예제

 

커서 구현

-> 기본적으로 제공하는 Cursor 인스턴스 대신 Cursor를 상속받아 새로운 커서를 구현해야 할 피요가 있을 수도 있다.
-> queryWithFactory() 메소드나 rawQueryWithFactory() 메소드에 SQLiteDatabase.CursorFactory 인스턴스를 인자로 넘겨 사용한다.
-> CursorFactory 클래스는 newCursor() 메소드가 구현된 내용에 따라 새로운 Cursor를 생성한다.
-> 일반적인 안드로이드 애플리케이션을 개발하고 있다면 커서를 새로 구현해야할 일이 많지 않다.

 

데이터 직접 다루기

-> 에뮬레이터에는 sqlite3 프로그램이 포함되어 있고, adb shell 명령을 통해 실행해 사용할수있다.
-> 에뮬레이터의 셸에 접속한 다음 sqlite3명령을 실행하면서 데이터베이스 파일이 위치한 경로를 함게 지정해 주면 된다.
-> 데이터베이스 파일의 일반적인 위치 : /data/data/your.app.package/database/your-db-name
-> your.app.package 부분은 애플리케이션이 들어 있는 자바 패키지 명을 의미한다.
-> your-db-name 부분은 createDatabase() 명령을 실행할때 지정했던 데이터베이스 이름을 넣는다.
-> sqlite3 프로그램은 충분한 기능을 갖추고 있으며, 콘솔 화면에서 데이터베이스를 다루는게 익숙하다면 괜찮은 방법이다.
-> 콘솔 인터페이스보다 좀더 GUI를 갖춘 화면이 필요하다면, 위의 특정 경로에 보관되어 있는 데이터베이스 파일을 복사한 다음, 데스크탑에서 SQLite 데이터베이스 파일을 인식하는 다양한 프로그램을 활용해 데이터를 조회하고 다룰 수 있다.
-> 바깥으로 불러낸 복사본에 변경 작업을 진행했다면 변경된 데이터베이스 파일을 다시 기기에 업로드해야 반영된다.
-> 데이터베이스 파일을 기기에서 뽑아내려면 adb pull 메소드를 사용하여, 원본 경로와 대상 디렉토리 등을 적어주면 파일을 복사할수 있다.
-> 변경된 데이터베이스 파일을 기기에 업로드 하려면 adb push 명령을 사용한다.
-> adb push도 adb pull과 마찬가지로 원본 파일 경로와 대상 디렉토리 등을 알려줘야 한다.
-> 일반적으로 가장 많이 사용되는 SQLite 클라이언트 프로그램은 파이어폭스 브라우저의 확장 기능으로 구현돼있는 SQLite Manager이다. 파이어 폭스 확장 기능이기 때문에 운영체제 플랫폼에 상관없이 어디서든 사용할 수 있다.


나눔글꼴 설치 안내


이 PC에는 나눔글꼴이 설치되어 있지 않습니다.

이 사이트를 나눔글꼴로 보기 위해서는
나눔글꼴을 설치해야 합니다.

설치 취소

Designed by sketchbooks.co.kr / sketchbook5 board skin

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5