조회 수 23821 댓글 0
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print
?

단축키

Prev이전 문서

Next다음 문서

+ - Up Down Comment Print

출처 : http://arabiannight.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9CAndroid-DB-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EA%B4%80%EB%A6%AC-Cursor-Query


안드로이드 프로젝트를 진행하다 보면 DATA를 보관하고 사용하게 되는 경우가 자주 발생하게 됩니다. 보통 회원가입을 통해 회원정보를 저장할때 많이 사용하는데요. 그럴경우 DATA를 계속적으로 보관하고 사용해야 하기 때문에 DATABASE(=DB) 라는 저장공간에을 사용하게 되는 것 입니다.

안드로이드에서는 이러한 경우를 대비해 SQLiteDatabase라는 DATABASE 를 제공해 주는데요. 만약 DATA를 저장해서 사용하게 되는 경우가 생길 경우 
 SQLiteDatabase 와 DatabaseHelper(DB생성 및 관리를 도와준다.) 를 사용해서 좀더 편리하게 DATABASE를 관리 할 수 있습니다.

DB를 사용하기 위해서는 우선 어떤 작업을 제일 먼저 해야할까요? 그렇습니다.ㅎ 일단 DB를 사용하기 위해서는 DB를 생성해야 합니다. DB는 TABLE 구조로 DATA를 관리하고있으므로 우선대야 할 작업이 TABLE 구조를 만드는 일입니다. 자 그럼 TABLE 구조를 만드는 코드를 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// DataBase Table
public final class DataBases {
     
    public static final class CreateDB implements BaseColumns{
        public static final String NAME = "name";
        public static final String CONTACT = "contact";
        public static final String EMAIL = "email";
        public static final String _TABLENAME = "address";
        public static final String _CREATE =
            "create table "+_TABLENAME+"("
                    +_ID+" integer primary key autoincrement, "    
                    +NAME+" text not null , "
                    +CONTACT+" text not null , "
                    +EMAIL+" text not null );";
    }
}





여기서 주의해서 보실 부분중에 
 autoincrement 란 속성이 있습니다. autoincrement 란 속성은 DATA를 DB에 넣을때 자동으로 "+1" 을 증가 시켜주는 주어 DATA를 식별 할 수 있는 고유 번호를 부여하는 속성 입니다. 위의 예제에서는 "_id" 라는 필드에 사용 되었습니다.





실제 위의 코드로 생성한 DB 테이블(address) 구조 입니다.
RecNo은 DB Tool 자체의 Number코드이니 "_id" 필드 부터 보시면 됩니다. 또한 "_id" 필드의 값이 +1씩 자동 증가한 모습을 볼 수 있습니다.




자 그럼, DB TABLE 구조에 대해 알아보았습니다. 다음은 
SQLiteDatabase 와  DatabaseHelper 를 통한 DB 관리 예제 설명 하겠습니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class DbOpenHelper {
 
    private static final String DATABASE_NAME = "addressbook.db";
    private static final int DATABASE_VERSION = 1;
    public static SQLiteDatabase mDB;
    private DatabaseHelper mDBHelper;
    private Context mCtx;
 
    private class DatabaseHelper extends SQLiteOpenHelper{
 
        // 생성자
        public DatabaseHelper(Context context, String name,
                CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
 
        // 최초 DB를 만들때 한번만 호출된다.
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DataBases.CreateDB._CREATE);
 
        }
 
        // 버전이 업데이트 되었을 경우 DB를 다시 만들어 준다.
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS "+DataBases.CreateDB._TABLENAME);
            onCreate(db);
        }
    }
 
    public DbOpenHelper(Context context){
        this.mCtx = context;
    }
 
    public DbOpenHelper open() throws SQLException{
        mDBHelper = new DatabaseHelper(mCtx, DATABASE_NAME, null, DATABASE_VERSION);
        mDB = mDBHelper.getWritableDatabase();
        return this;
    }
 
    public void close(){
        mDB.close();
    }
 
}



위의 예제 소스에서 보시는 바와 같이,


 

onCreate(SQLiteDatabase db) : 최초 DB를 만들때 한번만 호출됩니다.

onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) : 버전이 업데이트 되었을 
경우 DB를 다시 만들어 줍니다. DATABASE_VERSION = 1, 2, 3 이런식으로 수정해 주시면 자동
으로 기존의 TABLE을 삭제하고 새로운 TABLE을 만들어 줍니다.

getWritableDatabase()  :  DB를 사용하기위해 생성하거나 열어 줍니다.

DB를 읽거나 쓸수 있는 권한을 부여 합니다.


 





다음은 DB를 사용하는 ACTIVITY 화면 입니다. 실제 사용자가 DB에 DATA를 저장, 삭제, 갱신, 조회 등의 작업을 수행하는 클래스 입니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
public class TestDataBaseActivity extends Activity {
     
    private static final String TAG = "TestDataBaseActivity";
    private DbOpenHelper mDbOpenHelper;
    private Cursor mCursor;
    private InfoClass mInfoClass;
    private ArrayList<infoclass> mInfoArray;
    private CustomAdapter mAdapter;
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
         
        setLayout();
         
        // DB Create and Open
        mDbOpenHelper = new DbOpenHelper(this);
        mDbOpenHelper.open();
         
        mDbOpenHelper.insertColumn("김태희","01000001111" , "angel@google.com");
        mDbOpenHelper.insertColumn("송혜교","01333331111" , "asdffff@emdo.com");
        mDbOpenHelper.insertColumn("낸시랭","01234001111" , "yaya@hhh.com");
        mDbOpenHelper.insertColumn("제시카","01600001111" , "tree777@atat.com");
        mDbOpenHelper.insertColumn("성유리","01700001111" , "tiger@tttt.com");
        mDbOpenHelper.insertColumn("김태우","01800001111" , "gril@zzz.com");
         
//        startManagingCursor(mCursor);
         
         
        mInfoArray = new ArrayList<infoclass>();
         
        doWhileCursorToArray();
         
        for(InfoClass i : mInfoArray){
            DLog.d(TAG, "ID = " + i._id);
            DLog.d(TAG, "name = " + i.name);
            DLog.d(TAG, "contact = " + i.contact);
            DLog.d(TAG, "email = " + i.email);
        }
         
        mAdapter = new CustomAdapter(this, mInfoArray);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemLongClickListener(longClickListener);
         
    }
     
    @Override
    protected void onDestroy() {
        mDbOpenHelper.close();
        super.onDestroy();
    }
     
     
    /**
     * ListView의 Item을 롱클릭 할때 호출 ( 선택한 아이템의 DB 컬럼과 Data를 삭제 한다. )
     */
    private OnItemLongClickListener longClickListener = new OnItemLongClickListener() {
        @Override
        public boolean onItemLongClick(AdapterView<!--?--> arg0, View arg1,
                int position, long arg3) {
             
            DLog.e(TAG, "position = " + position);
             
            boolean result = mDbOpenHelper.deleteColumn(position + 1);
            DLog.e(TAG, "result = " + result);
             
            if(result){
                mInfoArray.remove(position);
                mAdapter.setArrayList(mInfoArray);
                mAdapter.notifyDataSetChanged();
            }else {
                Toast.makeText(getApplicationContext(), "INDEX를 확인해 주세요.",
                        Toast.LENGTH_LONG).show();
            }
             
            return false;
        }
    };
     
     
    /**
     * DB에서 받아온 값을 ArrayList에 Add
     */
    private void doWhileCursorToArray(){
         
        mCursor = null;
        mCursor = mDbOpenHelper.getAllColumns();
        DLog.e(TAG, "COUNT = " + mCursor.getCount());
         
        while (mCursor.moveToNext()) {
             
            mInfoClass = new InfoClass(
                    mCursor.getInt(mCursor.getColumnIndex("_id")),
                    mCursor.getString(mCursor.getColumnIndex("name")),
                    mCursor.getString(mCursor.getColumnIndex("contact")),
                    mCursor.getString(mCursor.getColumnIndex("email"))
                    );
             
            mInfoArray.add(mInfoClass);
        }
         
        mCursor.close();
    }
     
     
    /**
     * OnClick Button
     * @param v
     */
    public void onClick(View v){
        switch (v.getId()) {
        case R.id.btn_add:
            mDbOpenHelper.insertColumn
                    (
                    mEditTexts[Constants.NAME].getText().toString().trim(),
                    mEditTexts[Constants.CONTACT].getText().toString().trim(),
                    mEditTexts[Constants.EMAIL].getText().toString().trim()
                    );
             
            mInfoArray.clear();
             
            doWhileCursorToArray();
             
            mAdapter.setArrayList(mInfoArray);         
            mAdapter.notifyDataSetChanged();
             
            mCursor.close();
             
            break;
 
        default:
            break;
        }
    }
     
    /*
     * Layout
     */
    private EditText[] mEditTexts;
    private ListView mListView;
     
    private void setLayout(){
        mEditTexts = new EditText[]{
                (EditText)findViewById(R.id.et_name),
                (EditText)findViewById(R.id.et_contact),
                (EditText)findViewById(R.id.et_email)
        };
         
        mListView = (ListView) findViewById(R.id.lv_list);
    }
}
</infoclass></infoclass>


위의 코드는 사용자가 직접 입력한 값을 DB에 추가하고, ListView에 뿌려주고 있으며, 또한 사용자가 ListView의 Item을 롱클릭 하였을 경우, Item을 DB와 ListView에서 삭제 시켜주는 코드 입니다.






이런 형식으로 짜여진 코드 입니다. 그런데 테스트 도중 아주 중요한 문제점을 하나 발견 했습니다. 바로 ListView의 Item을 롱클릭해서 DB와 Adapter의 DATA를 지우는 도중에 자동증가된 "_id" 의 값이 ArrayList의 포지션값과 다르다는 것을... 읔..

쉽게말해 ArrayList는 해당 Position을 지우면, 1,2,3,4,5 중 4를 지우면 자동으로 1,2,3,4로 포지션이 재할당 됩니다. (5번 포지션이 4번으로 바뀌는 것이죠.) 그런데 "_id" 속성은 DATA를 DB에 삽입 할 때 마다 +1을 증가시키지만, 마찬가지로 1,2,3,4,5 번중에 4번을 지우면 1,2,3,5 의 index를 가지게 된다는 말입니다.


이렇듯 말이죠.

 

 
그러므로 위의 index값이 일치하지 않는 문제를 가지고 있는 코드 입니다. 헐. 그럼 DATA를 삭제 할 수 없는 것일까요? ㅎㅎ그렇지 않습니다. 주민등록 번호나 전화번호 처럼 고유한 값을 가지고 DATA 삭제 처리를 해주면 됩니다. 주민등록번호나 전화번호는 전세계 하나밖에 없는 고유한 번호이기 때문에 해당 번호를 지우게 되도 중복으로 지워지는 일을 방지 할 수 있습니다.


자 그럼, 코드에 대해 마자 분석을 해보겠습니다.




우선 DB에서 값을 받아오는 Cursor라는 녀석이 있습니다.
DB 테이블에 담긴 DATA를 받아오는 Cousor는 SQLiteDatabase 의

query(DataBases.CreateDB._TABLENAME, null, null, null, null, null, null);
매개인자 별로 테이블, 컬럼, 선택값, 선택값배열, 그룹, 조건절, 정렬등을 지정해 쿼리를 좀더 편리하게 쓸 수 있도록 하는 메서드 입니다.

rawQuery( "select * from address where name=" + "'" + name + "'" , null);
평소에 많이 보았던 전체 쿼리로 DATA를 다루는 메서드 입니다.

의 속성을 이용해서 DATA를 받아 올 수 있습니다.

커서를 이용해 해당 필드를 조회 하는 방법 입니다.

mCursor.getInt(mCursor.getColumnIndex("_id")),
mCursor.getString(mCursor.getColumnIndex("name")),
mCursor.getString(mCursor.getColumnIndex("contact")),
mCursor.getString(mCursor.getColumnIndex("email"))

해당 필드에 대한 값을 int, String 으로 얻어 올 수 있습니다.

또한 While 문며 ArrayList에 DaTa를 Add 시킬때,
mCursor.moveToNext() 란 속성을 이용했는데요. 이 속성은 커서가 다음 행에 커서를 이동 시키는 방법 입니다. 커서의 마지막 행까지 루프를 돌게 되는 방법 입니다.

startManagingCursor(mCursor) 란 속성은 Activity 딴에서 커서를 관리해 주는 메서드 입니다. 커서를 Close 하지 않고 이동하거나 하는 경우 메모리 문제와 오류가 발생 할 수 있기때문에 Activity 주기에 따라 커서를 Open , Close 해주는 아주 고마운 메서드인 셈 입니다.
(BUT, 그러나 Cursor객체는 DB에서 값을 받아온 후 바로 Close 해주는게 좋습니다. Cursor 객체가 할당하고 있는 메모리를 생각해서 말이죠.) 


mAdapter.notifyDataSetChanged() 이 메서드 역시 굉장히 중요한 메서드 입니다. ListView에 뿌려지는 Data들을 관리해주는 Adapter에게 "Data의 변동이 있으니 변경 사항을 적용해라." 라고 알려주는 메서드 입니다. 이 메서드를 호출 해야만 Data의 추가, 삭제가 ListView를 통해 보여지게 되니 정말로 중요한 메서드 입니다.


Dreamy의 코드 스크랩

내가 모으고 내가 보는

List of Articles
번호 분류 제목 날짜 조회 수 추천 수
311 Android [GIT 사용법] Git Tutorial 2011.12.26 96471 0
310 Android [GIT 사용법] Git Log 활용하기 2012.05.08 12889 0
309 Android [GIT 사용법] Dreamy의 요약 2 secret 2012.04.20 158 0
308 MFC [C] Unicode 사용에 대하여 2006.04.14 47087 0
307 HTML5 [CSS] 선택자(Selector)의 이해 2017.12.29 9829 0
306 MFC [Collection] CMap 템플릿 클래스 사용하기 2010.06.23 50473 0
305 MFC [Collection] CList 템플릿 클래스 사용하기 2010.06.24 46822 0
304 MFC [Collection] CArray Class 사용법 2017.01.25 15825 0
303 MFC [Collection] ArrayList 예제 2017.01.25 6518 0
302 C# [C#] (System.Collections.Generic) ArrayList 2012.05.23 15250 0
301 Pi [Arduino] 아두이노 나노 Arduino Nano Spec 1 file 2016.09.07 19939 0
300 Pi [Arduino] Timer 라이브러리 사용하기 2016.10.25 13942 0
299 Pi [Arduino] millis() 함수로 시간 재기 2016.10.25 9633 0
298 Android [apk분석] 개발자가 아니더라도 쉽게 apk를 분석해보자 2014.05.08 18839 0
297 Python zip() 함수 2014.04.30 22332 0
목록
Board Pagination ‹ Prev 1 ... 9 10 11 12 13 14 15 16 17 18 ... 34 Next ›
/ 34

나눔글꼴 설치 안내


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

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

설치 취소

Designed by sketchbooks.co.kr / sketchbook5 board skin

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5

Sketchbook5, 스케치북5