[안드로이드] DBMS을 이용한 영속화

hee09·2021년 10월 17일
0

안드로이드

목록 보기
5/20
post-thumbnail

이 글은 깡샘의 안드로이드 프로그래밍을 보며 글을 작성하였습니다

SQLite을 이용한 영속화

SQLite는 관계형 데이터베이스로 복잡하고 구조화된 애플리케이션 데이터를 저장하고 관리합니다. 프로세스가 아닌 라이브러리를 이용하므로 데이터베이스는 애플리케이션의 일부로 통합됩니다.
SQLite를 이용한 데이터는 파일에 저장되며 다음과 같은 경로에 저장됩니다.

  • data/data/[pacakge_name]/databases

SQLite는 서버에서 받은 데이터 일정 정도를 스마트폰 내부에 저장해서 네트워크 연결이 끊기는 상황에 대응할 수 있습니다.


SQLiteOpenHelper 클래스

상속 관계
public abstract class SQLiteOpenHelper
extends Object implements AutoCloseable

java.lang.Object
↳ android.database.sqlite.SQLiteOpenHelper

간단하게 SQLiteDatabaseCursor 클래스만 사용해도 모든 SQL문을 수행할 수 있지만, SQLiteOpenHelper 클래스를 이용하면 편리합니다. 이 클래스는 데이터베이스 관리만을 목적으로 하는 클래스입니다. 데이터 저장이나 획득 등의 코드는 SQLiteDatabase 클래스를 이용하여 insert, select 작업을 하고, 테이블 생성이나 스키마 변경 등의 작업은 SQLiteOpenHelper 클래스로 일원화 하는 구조입니다.

우선, SQLiteOpenHelper를 상속받는 하위 클래스를 작성합니다

const val DATABASE_VERSION: Int = 1

// SQLiteOpenHelper를 상속받는 서브 클래스
// 생성자로 context, 데이터베이스 파일명, Cursor Factory, 데이터베이스 버전정보
class DBHelper(context: Context): SQLiteOpenHelper(context, "memodb", null, DATABASE_VERSION) {
    // 앱이 설치된 후 최초로 이용되는 순간 호출
    override fun onCreate(db: SQLiteDatabase?) {
        
    }

    // 데이터베이스 버전이 변경될 때마다 호출
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        
    }
}

클래스를 상속받아 오버라이딩 해야하는 메소드는 다음과 같습니다.

  • onCreate() : 앱이 설치되어 SQLiteOpenHelper 클래스가 최초로 사용되는 순간 호출됩니다. 전체 앱 내에서 가장 처음 한 번만 수행되는 코드를 작성하고자 사용하며, 대부분 테이블을 생성하는 코드를 작성합니다
  • onUpgrade() : SQLiteOpenHelper 클래스의 생성자에 전달되는 데이터베이스 버전 정보가 변경될 떄마다 반복해서 호출됩니다. 결국, 테이블의 스키마 부분을 변경하기 위한 용도로 사용됩니다.

다음과 같이 클래스를 정의하고 실제 SQL문 수행을 위해 SQLiteDatabase 객체를 획득합니다. 이때는 SQLiteOpenHelper 클래스의 getReaderableDatabase() 함수와 getWritableDatabase() 함수를 이용합니다.

// SQLiteOpenHelper를 상속받는 클래스 객체 생성
val helper = DBHelper(this)
// SQLteDatabase 객체 획득
val db = helper.writableDatabase
  • getWritableDatabase() : 읽거나 쓰는데 사용되는 데이터베이스를 생성하거나 오픈합니다.
  • getReadableDatabase() : 데이터베으스를 생성하거나 오픈합니다.

insert(), query(), update(), delete() 함수 이용

  • insert(String table, String nullColumnHack, ContentValues values) : Insert문
  • update(String table, ContentValues values, String whereClause, String[] whereArgs) : Update문
  • delete(String table, String whereClause, String[] whereArgs) : Delete문
  • query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) : Select문
  • execSQL(String sql) : insert, update 등 select문이 아닌 나머지 SQL문
  • rawQuery(String sql, String[] selectionArgs, Object[] bindArgs) : select SQL문

rawQuery(), execSql() 함수는 직접 SQL문을 매개변수로 주어 실행하고, 나머지 insert(), update(), delete(), query() 함수들은 SQL문을 만들기 위한 정보만 매개변수로 주면 자동으로 SQL문을 만들어 실행해줍니다. 이 중 insert()와 update() 함수는 ContentValues 클래스를 매개변수로 넘기는데, 이 클래스는 key-value 형식으로 되어있고 데이터를 표현하기 위한 집합 객체입니다.


예시 코드

메모장 기능을 하는 데이터베이스 프로그램을 작성하겠습니다. 사용자가 입력하는 글을 데이터베이스에 저장하고 저장된 데이터를 화면에 출력합니다.


SQLIteOpenHelper를 상속받는 소스코드(DBHelper.kt)

const val DATABASE_VERSION: Int = 1

// SQLiteOpenHelper를 상속받는 서브 클래스
// 생성자로 context, 데이터베이스 파일명, Cursor Factory, 데이터베이스 버전정보
class DBHelper(context: Context): SQLiteOpenHelper(context, "memodb", null, DATABASE_VERSION) {
    // 앱이 설치된 후 최초로 이용되는 순간 호출
    override fun onCreate(db: SQLiteDatabase?) {
        // Table을 생성하는 create문 SQL
        val memoSQL = "create table tb_memo " +
                "(_id integer primary key autoincrement," +
                "title," +
                "content)"

        // SQL문 실행
        db?.execSQL(memoSQL)
    }

    // 데이터베이스 버전이 변경될 때마다 호출
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        // DATABASE_VERSION의 값을 증가하면 onUpgrade() 함수가 자동으로 호출
        // 매개변수로 들어오는 버전이 DATABASE_VERSION과 같다면
        if(newVersion == DATABASE_VERSION) {
            // 테이블을 삭제하고 다시 만들게 작성
            db?.execSQL("drop table tb_memo")
            onCreate(db)
        }
    }
}

메모장 기능을 하는 MainActivity 소스코드

class MainActivity : AppCompatActivity() {
    lateinit var titleView: EditText
    lateinit var contentView: EditText
    lateinit var addBtn: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        titleView = findViewById(R.id.add_title)
        contentView = findViewById(R.id.add_content)
        addBtn = findViewById(R.id.add_btn)

        // Button SetOnClickListener(델리게이션 이벤트 모델 형식)
        addBtn.setOnClickListener {
            val title = titleView.text.toString()
            val content = contentView.text.toString()

            // SQLiteOpenHelper를 상속받는 클래스 객체 생성
            val helper: DBHelper = DBHelper(this)
            // SQLiteDatabase 객체 생성
            val db: SQLiteDatabase = helper.writableDatabase

            // Insert문 작성
            // 첫 번째 매개변수는 SQL문, 두 번째 매개변수는 SQL문의 ?안에 들어갈 데이터(배열로 전달)
            db.execSQL("insert into tb_memo (title, content) values (?, ?)", arrayOf(title, content))
            // SQLiteDatabase 닫기
            db.close()

            // 시스템에게 인텐트를 전달하여 해당 인텐트를 정보로 하는 Activity 시작하기
            val intent = Intent(this, ReadDBActivity::class.java)
            startActivity(intent)
        }
    }
}

데이터 베이스에 저장된 결과를 출력하는 ReadActivity 소스코드

class ReadDBActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_read_db)

        val titleView = findViewById<TextView>(R.id.read_title)
        val contentView = findViewById<TextView>(R.id.read_content)

        // SQLiteOpenHelper를 상속받는 클래스 객체 생성
        val helper:DBHelper = DBHelper(this)
        // SQLiteDatabase 객체 생성
        val db:SQLiteDatabase = helper.writableDatabase

        // Select문을 실행하여 결과로 Cursor 객체 획득하기
        // Cursor 객체는 SQL 질의문을 통해 반환되는 결과이다(title, content 컬럽 값의 데이터가 들어가있다)
        val cursor: Cursor = db.rawQuery("select title, content from tb_memo order by _id desc limit 1", null)

        // 다음 값이 있다면
        while(cursor.moveToNext()) {
            // index 0번에 해당하는 컬럼값 얻어오기(title)
            titleView.text = cursor.getString(0)
            // index 1번에 해당하는 컬럼값 얻어오기(content)
            contentView.text = cursor.getString(0)
        }
        
        // SQLiteDatabase 종료
        db.close()
    }
}

SQLiteDatabase와 SQLiteOpenHelper를 사용하는 방법도 있지만 Jetpack의 AAC중 하나인 Room을 사용하는 방법도 있습니다.
Room 글 업데이트 후 링크 걸기

profile
되새기기 위해 기록

0개의 댓글