Android

[Android] SQLite3 데이터 활용: CRUD 작업, SQLiteDatabase, do, while

eyoo 2022. 7. 14. 09:48

연락처를 저장하고 수정하고 보여줄수있는 앱을 만들어 보자.

 

먼저 패키지와 클래스를 따로만들어서 SQL에 쓰일 DB의 이름, 버전, 테이블 이름과 그 테이블에 있는 키값들을 상수로 지정해준다.

 

public class Util {
    // 데이터베이스 관련된 상수
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "contact_db";
    public static final String TABLE_NAME = "contact";

    // 테이블의 컬럼 관련된 상수
    public static final String KEY_ID = "id";
    public static final String KEY_NAME = "name";
    public static final String KEY_PHONE = "phone";
}

# final로 수정되지 않도록 하여 상수처리 한다.

# static으로 패키지 내에서 사용 가능하게 하고 public으로 패키지 밖에서도 사용 가능하도록 한다.

 

 

그 후 다시 패키지와 클래스를 새로 만들어 연락처에 쓰일 데이터의 변수들을 만든다.

 

public class Contact {
    public int id;
    public String name;
    public String phone;

    public Contact() {

    }

    public Contact(int id, String name, String phone) {
        this.id = id;
        this.name = name;
        this.phone = phone;
    }

    public Contact(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }
}

# 생성자를 만들어 쉽게 등록 가능하도록 하자.

 

 

다시 패키지와 클래스를 새로 만들고 SQLiteOpenHelper를 상속받는다.

# 여기서 SQL 관련 작업들이 가능하도록 만들어준다.

 

public class DatabaseHandler extends SQLiteOpenHelper {
    public DatabaseHandler(@Nullable Context context) {
        super(context, Util.DATABASE_NAME, null, Util.DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        // 테이블 생성
        String CREATE_CONTACT_TABLE = "create table " + Util.TABLE_NAME + "("+
                Util.KEY_ID + " integer primary key, "+
                Util.KEY_NAME + " text, "+
                Util.KEY_PHONE + " text )";
        Log.i("MyContact", "SQL 테이블 생성문 : "+CREATE_CONTACT_TABLE);

        // SQL 테이블 생성문을 넣어서 실행
        sqLiteDatabase.execSQL(CREATE_CONTACT_TABLE);

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

        // 테이블을 삭제하고 다시 만든다,
        String DROP_TABLE = "drop table " + Util.TABLE_NAME;
        sqLiteDatabase.execSQL(DROP_TABLE, new String[]{Util.DATABASE_NAME});

        onCreate(sqLiteDatabase);
    }

# SQLiteOpenHelper가 추상 클래스여서 extends로 상속받으면 오류메세지로 implement 하라고 나오는데 실행시킨다.

# 실행시키면 SQLiteDatabase의 onCreate와  onUpgrad가 자동으로 오버라이드 되며 여기에 필요한 코드를 작성한다.

# onCreate에 작성해둔 테이블 이름과 키를 이용하여 테이블을 만드는 SQL문을 작성하고 execSQL로 실행되게 한다. 

# onUpgrad에는 테이블을 삭제하는 SQL문이 실행되게 하고 위의 onCreate가 실행되게하여 지우고 다시 만드는식으로 수정되도록 한다.

 

 

이제 앱을 동작시키는데 필요한 SQL문이 적용된 함수들을 만든다.

 

먼저 insert 함수로 연락처를 추가하는 함수를 만들어 보자.

 

    public void addContact(Contact contact){
        // 데이터베이스를 가져온다.
        SQLiteDatabase db = this.getWritableDatabase();

        // 데이터 컬럼 이름과 해당 데이터를 매칭해서 넣어준다.
        ContentValues values  = new ContentValues();
        values.put(Util.KEY_NAME,contact.name);
        values.put(Util.KEY_PHONE,contact.phone);

        // 데이터베이스에 위의 데이터를 insert 한다.
        db.insert(Util.TABLE_NAME, null, values);

        // db를 닫는다.
        db.close();

    }

# getWritableDatabase로 수정 가능한 상테의 DB를 가져온다.

# ContentValues를 객체생성하고 put을 사용해서 데이터 컬럼 이름과 해당 데이터를 매칭하여 넣는다.

# 그것을 DB에 insert하고 DB를 닫아준다.

 

 

메인에서 연락처를 생성하는 함수를 테스트하자.

 

// 주소록 데이터를 저장하는 코드
DatabaseHandler db = new DatabaseHandler(MainActivity.this);
Contact contact = new Contact("홍길동", "010-1234-5678");
db.addContact(contact);

# Contact 클래스로 객체를 생성해서 DB에 저장된다.

 

 

이제 연락처를 가져오는 함수를 만들어보자.

 

rawQuery 함수를 사용해서 연락처 id를 받아서 해당 연락처 정보를 가져오는 함수를 만든다.

 

    public Contact getContact(int id){
    
        // 데이터베이스를 가져온다.
        SQLiteDatabase db = this.getReadableDatabase();
        
        // 쿼리문
        Cursor cursor = db.rawQuery("select * from contact where id =" + id,null);

        // 두번째 방법: 혹은 ? 에 매칭되는것을 두번째 파라미터에 써주면 된다.
        // Cursor cursor = db.rawQuery("select * from contact where id = ?" ,new String[]{""+id});
        
        // 커서가 null이 아닐때
        if(cursor != null){
            cursor.moveToFirst();
        }

        // DB에 저장된 데이터를 메모리에다 만들어야 cpu가 처리할수있다.
        Contact contact = new Contact(cursor.getInt(0), cursor.getString(1), cursor.getString(2));
        return contact;

    }

# getReadableDatabase로 읽기 상태의 DB를 가져온다.

# 그 후 커서를 DB의 rawQuery로 둔다.

# rawQuery의 첫번째 파라미터에는 쿼리문을, 그리고 두번째 파라미터에는 첫번째 파라미터에 ?를 사용한 경우 대체 되는 데이터를 어레이 리스트를 사용해서 넣는다.

# 위의 cursor가 null이 아닐때 moveToFirst를 실행하여 커서를 처음으로 이동시킨다.

# 컬럼의 인덱스를 getString에 넣어주며 데이터를 가져와 메모리에 만든다.

 

 

메인에서 id를 받아 해당 연락처를 가져오는 함수를 테스트하자

 

// 주소록 DB 에서 id가 1인 주소록 데이터로그
DatabaseHandler db = new DatabaseHandler(MainActivity.this);
Contact contact1 = db.getContact(1);
Log.i("MyContact","하나의 주소록 id : "+contact1.id + " , name : "+contact1.name+" , phone : "+contact1.phone);

# id가 1인 연락처를 가져왔다.

 

 

전체 연락처를 가져오는 함수도 만들어보자

 

    public ArrayList<Contact> getAllContact(){
        // DB 불러오기
        SQLiteDatabase db = this.getReadableDatabase();
        // 쿼리문
        Cursor cursor = db.rawQuery("select * from contact",null);

        ArrayList<Contact> contactList = new ArrayList<Contact>();

//        cursor.moveToFirst();
//        for(int i = 0; i < cursor.getCount(); i++){
//            Contact contact = new Contact(cursor.getInt(0),cursor.getString(1), cursor.getString(2));
//            contactList.add(contact);
//            cursor.moveToNext();
//        }

        // for문 말고도 do while 문을 사용하여 반복하여 넣을수있다.
        
        if(cursor.moveToFirst()){
            do{
                Contact contact = new Contact(cursor.getInt(0),cursor.getString(1), cursor.getString(2));
                contactList.add(contact);
            } while(cursor.moveToNext());
        }

        return contactList;
    }

# 전체 연락처를 불러오는 함수는 위의 함수와 비슷하지만 리스트의 형식으로 된 데이터 묶음을 하나씩 처리하여 보여주도록 한다.

# for문을 사용해도 되지만 do와 while문을 사용해서 처리하는것이 더 유용하다.

# while에는 이 코드를 실행하는 동안에 라는 조건을, 그리고 do에는 작업할 코드를 입력한다. 

 

 

전체 연락처를 가져오는 함수를 테스트하자.

 

@Override
protected void onResume() {
    super.onResume();
    printDBData();
}

// DB에 있는 모든 주소록 데이터를 디버깅용으로 로그 출력하는 함수
void printDBData(){
    DatabaseHandler db = new DatabaseHandler(MainActivity.this);
    ArrayList<Contact> contactList =  db.getAllContact();
    for(Contact data : contactList) {
        Log.i("MyContact", "id : " + data.id + " , name : " + data.name + " , phone : " + data.phone);
    }
}

# onResume에 전체 연락처를 로그에 찍는 함수를 만들어 확인한다.

# DB를 연결하고 리스트 어레이에 있는 데이터를 반복문으로 로그에 찍었다.

# for(Contact data : contactList)는 파이썬의  for data in contactList와 같은 역할을 한다.

 

 

이제 rawQuery를 사용하는것이 아닌 execSQL를 사용하여 데이터를 수정하고 삭제하는 함수를 만든디.

 

    // 주소록 수정하는 함수
    public void updateContact(Contact contact){
        SQLiteDatabase db = this.getWritableDatabase();
//        ContentValues values = new ContentValues();
//        values.put(Util.KEY_NAME, contact.name);
//        values.put(Util.KEY_PHONE, contact.phone);

//        db.update(Util.TABLE_NAME, values, Util.KEY_ID + "=?", new String[]{contact.id+""});

        db.execSQL("update contact set name = ?, phone = ? where id = ?",new String[]{contact.name, contact.phone, contact.id+""});

        db.close();
    }

	// 주소록 삭제하는 함수
    public void deleteContact (Contact contact){
        // delete from contact where id = 1
        SQLiteDatabase db = this.getWritableDatabase();
        db.execSQL("delete from contact where id = ?",new String[]{contact.id+""});
        db.close();

    }

# execSQL의 첫번째 파라미터는 쿼리문을 넣고 두번째 파라미터에는 ? 에 들어갈 데이터를 넣는다.

 

 

위 함수들을 테스트하면 잘 실행된다.

 

// 업데이트 함수 적용
DatabaseHandler db = new DatabaseHandler(MainActivity.this);
Contact contact = new Contact(1,"김길동","010-9999-9999");
db.updateContact(contact);

// delete 함수 적용
DatabaseHandler db = new DatabaseHandler(MainActivity.this);
Contact contact = new Contact(1,"김길동","010-9999-9999");
db.deleteContact(contact);