안드로이드 데이터베이스(DB) 프로그래밍 3. [SQLiteDatabase 사용 예제] (Android Database 3)

2017. 4. 14. 17:52


1. SQLiteDatabase.

안드로이드에서 기본적으로 제공하는 데이터베이스는 SQLite입니다. SQLite는 관계형 데이터베이스이며, 관계형 데이터베이스에 대한 개념과 용어에 대해서는 [안드로이드 데이터베이스(DB) 프로그래밍 1 - 관계형 데이터베이스]에서 간단히 설명하였습니다.


안드로이드에서 SQLite 데이터베이스를 사용할 때 가장 중요한 핵심 요소는 SQLiteDatabase 클래스입니다. SQLite 데이터베이스를 다루는 기능들이 SQLiteDatabase 클래스에 정의되어 있죠.


SQLite 데이터베이스를 열고 SQLiteDatabase 클래스의 객체를 획득하는 과정은 [안드로이드 데이터베이스(DB) 프로그래밍 2 - SQLiteDatabase]에서 설명하였습니다. 그리고 획득한 SQLiteDatabase 객체를 사용하여 데이터를 추가하거나, 삭제, 조회하는 방법에 대해서도 설명하였죠.


그럼, 지금부터는 실질적인 앱 작성을 통해 SQLite 데이터베이스에 데이터를 저장하고, 읽어들이는 방법에 대해 알아보도록 하겠습니다. 하지만 이 글에서 작성하는 예제는 SQLiteDatabase의 사용 방법을 소개하는데 초점이 맞춰져있기 때문에, 실제 앱 구현에 적용하기에는 다소 부족한 것이 사실입니다.


그러므로 본문의 예제는 SQLiteDatabase에 대한 이해 용도로만 살펴봐주시기 바라며, 좀 더 다듬어진 예제는 따로 정리하여 설명하겠습니다.

2. SQLiteDatabase 사용 예제

이제 앱 작성을 통해 SQLiteDatabase를 사용하여 데이터를 저장하고, 조회하는 방법에 대해 살펴보도록 하겠습니다.


예제로 작성되는 앱의 실행 화면은 아래 그림과 같이 구성됩니다.

예제 MainActivity 레이아웃


데이터베이스는 아래와 같은 구조를 가집니다.

데이터베이스 테이블 구조


참고로, 보통 데이터베이스를 사용할 때는 동일한 컬럼(Column)에 따라 식별된 많은 양의 로우(Row)를 저장하는데, 본문의 예제는 하나의 로우(Row)만 저장합니다. 이는 앱의 설정 정보 등을 저장할 때 사용하는 방법인데, 설정 정보는 최종적으로 저장된 단 하나의 값만 있으면 되기 때문입니다.

2.1 MainActivity의 레이아웃 작성.

앱 작성 시, 첫 번째로 할 일은 MainActivity의 레이아웃을 작성하는 것입니다. 앞서 설계한 예제 화면 구성도에 따라, 아래와 같이 MainActivity에 표시될 레아아웃 XML 코드를 작성합니다.

[STEP-1] "activity_main.xml" - MainActivity의 레이아웃 XML 코드 작성.
    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:stretchColumns="1">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="No" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextNo"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Name" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextName"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Phone" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextPhone"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Over20" />

            <CheckBox
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:id="@+id/checkBoxOver20"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="2">

                <Button
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="24dp"
                    android:text="Save"
                    android:id="@+id/buttonSave" />

                <Button
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:textSize="24dp"
                    android:text="CLEAR"
                    android:id="@+id/buttonClear" />
            </LinearLayout>

        </TableRow>
    </TableLayout>

2.2 SQLiteDatabase 클래스 변수 선언 및 데이터베이스 열기.

다음 단계로, SQLite 데이터베이스를 사용하는데 있어 가장 중요한 요소인, SQLiteDatabase 클래스의 객체를 초기화하는 코드를 작성합니다. SQLiteDatabase 클래스 객체는 MainActivity의 전역에서 사용되므로, 클래스 변수로 선언합니다. 참고로 SQLiteDatabase 초기화에 관한 내용은 [안드로이드 데이터베이스(DB) 프로그래밍 2 - SQLiteDatabase]에서 확인할 수 있습니다.


그리고 최초 앱 실행 시, SQLite 데이터베이스 파일이 존재하지 않으면 새로운 파일을 만들고 데이터베이스를 열도록 SQLiteDatabase.openOrCreateDatabase() 함수를 사용합니다. 이러한 과정은 init_database() 라는 함수에 작성하고, MainActivity의 onCreate() 함수에서 호출하도록 만듭니다.

[STEP-2] "MainActivity.java" - SQLDatabase 클래스 변수 선언 및 초기화.
public class MainActivity extends AppCompatActivity {

    SQLiteDatabase sqliteDB ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속
        sqliteDB = init_database() ;

        // 코드 계속  ...
    }

    // ... 코드 계속
    private SQLiteDatabase init_database() {

        SQLiteDatabase db = null ;
        // File file = getDatabasePath("contact.db") ;
        File file = new File(getFilesDir(), "contact.db") ;

        System.out.println("PATH : " + file.toString()) ;
        try {
            db = SQLiteDatabase.openOrCreateDatabase(file, null) ;
        } catch (SQLiteException e) {
            e.printStackTrace() ;
        }

        if (db == null) {
            System.out.println("DB creation failed. " + file.getAbsolutePath()) ;
        }

        return db ;
    }

    // 코드 계속  ...
}

2.3 SQLite 데이터베이스를 새로 만들 때, 테이블 생성. (CREATE TABLE)

SQLite 데이터베이스를 열어 SQLiteDatabase에 대한 참조를 확보했다면, 이제 데이터베이스 테이블에 데이터를 추가하거나 조회할 수 있습니다. 그런데 만약 데이터베이스 파일을 새로 생성한 경우라면 테이블은 존재하지 않기 때문에, CREATE TABLE 문을 사용하여 테이블을 생성해야 합니다. 물론 테이블이 존재하는 경우도 가정해야겠죠. 이를 위해 CREATE TABLE 문에 IF NOT EXISTS를 더하여 테이블을 생성합니다.

    CONTACT_T 테이블 생성을 위한 SQL 문.
CREATE TABLE IF NOT EXISTS CONTACT_T (NO INTEGER NOT NULL, NAME TEXT, PHONE TEXT, OVER20 INTEGER)

아래와 같이 init_table() 이라는 함수를 새로 추가하고, onCreate()에서 init_database() 함수 다음에 호출하도록 작성합니다.

[STEP-3] "MainActivity.java" - SQLDatabase 클래스 변수 선언 및 초기화.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        sqliteDB = init_database() ;

        init_tables() ;

        // 코드 계속  ...
    }

    // ... 코드 계속
    private void init_tables() {

        if (sqliteDB != null) {
            String sqlCreateTbl = "CREATE TABLE IF NOT EXISTS CONTACT_T (" +
                    "NO "           + "INTEGER NOT NULL," +
                    "NAME "         + "TEXT," +
                    "PHONE "        + "TEXT," +
                    "OVER20 "       + "INTEGER" + ")" ;

            System.out.println(sqlCreateTbl) ;

            sqliteDB.execSQL(sqlCreateTbl) ;
        }
    }

    // 코드 계속  ...
}

2.4 앱 실행 시, 테이블 데이터 조회하여 표시. (SELECT)

최초 앱이 실행되면 데이터베이스에 저장된 데이터를 로딩하여 화면에 표시하는 코드를 작성합니다. [안드로이드 데이터베이스(DB) 프로그래밍 2 - SQLiteDatabase]에서 설명한대로, 데이터 조회를 위한 SELECT문 실행 결과 데이터를 Cursor 타입으로 리턴받은 다음, moveToNext() 함수를 통해 커서로 전달된 레코드 집합(Record Set)을 탐색합니다.

    CONTACT_T 테이블에서 모든 데이터를 조회하는 SQL 문.
SELECT * FROM CONTACT_T

데이터를 조회하는 함수는 load_values() 라는 함수에 작성하고, MainActivity의 onCreate() 함수에서 init_table() 함수 바로 다음에 호출합니다.

[STEP-4] "MainActivity.java" - SELECT 문을 통해 데이터 조회 및 표시.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        sqliteDB = init_database() ;
        init_tables() ;

        load_values() ;

        // 코드 계속  ...
    }

    // ... 코드 계속
    private void load_values() {

        if (sqliteDB != null) {
            String sqlQueryTbl = "SELECT * FROM CONTACT_T" ;
            Cursor cursor = null ;

            // 쿼리 실행
            cursor = sqliteDB.rawQuery(sqlQueryTbl, null) ;

            if (cursor.moveToNext()) { // 레코드가 존재한다면,
                // no (INTEGER) 값 가져오기. 
                int no = cursor.getInt(0) ;
                EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
                editTextNo.setText(Integer.toString(no)) ;

                // name (TEXT) 값 가져오기
                String name = cursor.getString(1) ;
                EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
                editTextName.setText(name) ;

                // phone (TEXT) 값 가져오기
                String phone = cursor.getString(2) ;
                EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
                editTextPhone.setText(phone) ;

                // over20 (INTEGER) 값 가져오기.
                int over20 = cursor.getInt(3) ;
                CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
                if (over20 == 0) {
                    checkBoxOver20.setChecked(false) ;
                } else {
                    checkBoxOver20.setChecked(true) ;
                }
            }
        }
    }

    // 코드 계속  ...
}

2.5 입력 데이터 저장하기. (INSERT INTO)

이제 "Add" 버튼을 누르면, 데이터를 저장하는 기능을 구현하겠습니다. 데이터를 추가할 때는 INSERT INTO문을 사용합니다. 그런데 예제에서는 테이블 내에 단 하나의 레코드만 저장하므로, 저장되어 있는 레코드를 지우고 나서 새로 추가하도록 만들겠습니다.

    CONTACT_T 테이블에서 데이터를 추가하는 SQL 문.
INSERT INTO CONTACT_T (NO, NAME, PHONE, OVER20) VALUES (1, 'ppotta', '010-8888-9999', 1)
    CONTACT_T 테이블의 모든 레코드를 삭제하는 SQL 문.
DELETE FROM CONTACT_T

데이터를 저장하는 코드는 save_values() 함수에 작성한 다음, buttonSave 버튼의 클릭 이벤트 핸들러에서 해당 함수에서 호출하도록 작성합니다.

[STEP-5] "MainActivity.java" - 데이터 저장하기.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button buttonSave = (Button) findViewById(R.id.buttonSave) ;
        buttonSave.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                save_values() ;
            }
        });

        // 코드 계속  ...
    }

    // ... 코드 계속
    private void save_values() {
        if (sqliteDB != null) {
            
            // delete
            sqliteDB.execSQL("DELETE FROM CONTACT_T") ;

            EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
            String noText = editTextNo.getText().toString() ;
            int no = 0 ;
            if (noText != null && !noText.isEmpty()) {
                no = Integer.parseInt(noText) ;
            }

            EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
            String name = editTextName.getText().toString() ;

            EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
            String phone = editTextPhone.getText().toString() ;

            CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
            boolean isOver20 = checkBoxOver20.isChecked() ;

            String sqlInsert = "INSERT INTO CONTACT_T " +
                    "(NO, NAME, PHONE, OVER20) VALUES (" +
                    Integer.toString(no) + "," +
                    "'" + name + "'," +
                    "'" + phone + "'," +
                    ((isOver20 == true) ? "1" : "0") + ")" ;

            System.out.println(sqlInsert) ;

            sqliteDB.execSQL(sqlInsert) ;
        }
    }

    // 코드 계속 ...
}

2.6 데이터 삭제하기. (DELETE)

마지막으로 "Clear" 버튼이 눌려지면 입력된 모든 내용을 지우고, 테이블의 데이터를 삭제합니다. 이를 위해 DELETE 문을 사용합니다.

    CONTACT_T 테이블의 모든 레코드를 삭제하는 SQL 문.
DELETE FROM CONTACT_T

delete_values() 함수에 데이터 삭제 코드를 작성하고, buttonClear 버튼이 눌려지면 호출하도록 만듭니다.

[STEP-6] "MainActivity.java" - 데이터 삭제하기.
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button buttonClear = (Button) findViewById(R.id.buttonClear) ;
        buttonClear.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View v) {
                delete_values() ;
            }
        });

        // 코드 계속  ...
    }

    // ... 코드 계속

    private void delete_values() {
        if (sqliteDB != null) {
            String sqlDelete = "DELETE FROM CONTACT_T" ;

            sqliteDB.execSQL(sqlDelete) ;

            EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
            editTextNo.setText("") ;

            EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
            editTextName.setText("") ;

            EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
            editTextPhone.setText("") ;

            CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
            checkBoxOver20.setChecked(false) ;
        }
    }
}

3. 예제 실행 화면

예제를 작성하고 실행하면, 아래와 같은 실행 화면이 나타납니다.

예제 실행 화면


각 필드에 값을 입력하고 "SAVE" 버튼을 누르면, 입력된 값들이 데이터베이스에 저장됩니다. 그리고 앱을 다시 실행하면, 데이터베이스에 저장된 값이 화면에 표시되는 것을 확인할 수 있습니다.

데이터베이스 저장 및 저장된 데이터 표시


"CLEAR" 버튼을 누르면 데이터베이스에 저장된 값이 지워지고, 화면에 표시된 내용이 초기화되는 것을 확인할 수 있습니다.

데이터베이스 레코드 삭제 및 표시 값 초기화


4. 참고.

.END.


ANDROID 프로그래밍/DB