David Friedman | c921ee1 | 2015-06-29 22:14:16 -0700 | [diff] [blame] | 1 | page.title=SQL 데이터베이스에 데이터 저장하기 |
| 2 | page.tags=data storage |
| 3 | helpoutsWidget=true |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | |
| 7 | @jd:body |
| 8 | |
| 9 | |
| 10 | <div id="tb-wrapper"> |
| 11 | <div id="tb"> |
| 12 | |
| 13 | <h2>이 과정에서 다루는 내용</h2> |
| 14 | <ol> |
| 15 | <li><a href="#DefineContract">스키마 및 계약 정의하기</a></li> |
| 16 | <li><a href="#DbHelper">SQL Helper를 사용하여 데이터베이스 생성하기</a></li> |
| 17 | <li><a href="#WriteDbRow">데이터베이스에 정보 삽입하기</a></li> |
| 18 | <li><a href="#ReadDbRow">데이터베이스에서 정보 읽어오기</a></li> |
| 19 | <li><a href="#DeleteDbRow">데이터베이스에서 정보 삭제하기</a></li> |
| 20 | <li><a href="#UpdateDbRow">데이터베이스 업데이트하기</a></li> |
| 21 | </ol> |
| 22 | |
| 23 | <h2>필독 항목</h2> |
| 24 | <ul> |
| 25 | <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">데이터베이스 사용하기</a></li> |
| 26 | </ul> |
| 27 | |
| 28 | <!-- |
| 29 | <h2>Try it out</h2> |
| 30 | |
| 31 | <div class="download-box"> |
| 32 | <a href="{@docRoot}shareables/training/Sample.zip" class="button">Download the sample</a> |
| 33 | <p class="filename">Sample.zip</p> |
| 34 | </div> |
| 35 | --> |
| 36 | |
| 37 | </div> |
| 38 | </div> |
| 39 | |
| 40 | |
| 41 | <p>데이터베이스에 데이터를 저장하는 작업은 |
| 42 | 연락처 정보와 같이 반복적이거나 구조적인 데이터에 이상적입니다. 이 클래스에서는 사용자가 SQL 데이터베이스에 대한 기본 사항에 |
| 43 | 익숙하다는 전제하에 |
| 44 | Android에서 SQLite 데이터베이스를 시작하는 데 도움이 되는 유용한 정보를 제공합니다. Android에서 |
| 45 | 데이터베이스를 사용할 때 필요한 API는 {@link android.database.sqlite} 패키지에 있습니다.</p> |
| 46 | |
| 47 | |
| 48 | <h2 id="DefineContract">스키마 및 계약 정의하기</h2> |
| 49 | |
| 50 | <p>SQL 데이터베이스의 기본 원칙 중 하나는 스키마입니다. 스키마는 데이터베이스의 구성 체계에 대한 공식적인 |
| 51 | 선언입니다. 스키마는 개발자가 데이터베이스를 생성할 때 사용하는 SQL |
| 52 | 문에 반영됩니다. <em>계약</em> 클래스라고 하는 |
| 53 | 도우미 클래스를 생성하면 도움이 될 수 있습니다. 계약 클래스는 체계적이고 |
| 54 | 자기 문서화 방식으로 스키마의 레이아웃을 명시적으로 지정합니다.</p> |
| 55 | |
| 56 | <p>계약 클래스는 URI, 테이블 및 |
| 57 | 컬럼의 이름을 정의하는 상수를 유지하는 컨테이너입니다. 계약 클래스를 사용하면 동일한 패키지 내 |
| 58 | 모든 클래스에 동일한 상수를 사용할 수 있습니다. 즉, 어느 한 장소에서 컬럼 |
| 59 | 이름을 변경하면 코드 전체에 변경 사항이 반영됩니다.</p> |
| 60 | |
| 61 | <p>계약 클래스를 구성하는 좋은 방법은 클래스의 루트 레벨에 전체 데이터베이스에 |
| 62 | 전역적인 정의를 추가하는 것입니다. 그런 다음 컬럼을 열거하는 각 테이블에 대해 내부 |
| 63 | 클래스를 생성합니다.</p> |
| 64 | |
| 65 | <p class="note"><strong>참고:</strong> {@link |
| 66 | android.provider.BaseColumns} 인터페이스를 구현함으로써, 내부 클래스는 {@code _ID}라고 하는 기본 |
| 67 | 키 필드를 상속할 수 있습니다. 커서 어댑터와 같은 일부 Android 클래스의 경우 |
| 68 | 내부 클래스가 이러한 기본 키 필드를 가지고 있을 것이라 예상합니다. 내부 클래스는 반드시 필요한 것은 아니지만, 데이터베이스가 |
| 69 | Android 프레임워크와 조화롭게 작업하는 데 도움이 될 수 있습니다.</p> |
| 70 | |
| 71 | <p>예를 들어, 다음 스니펫은 테이블 이름과 |
| 72 | 단일 테이블의 컬럼 이름을 정의합니다.</p> |
| 73 | |
| 74 | |
| 75 | <pre> |
| 76 | public final class FeedReaderContract { |
| 77 | // To prevent someone from accidentally instantiating the contract class, |
| 78 | // give it an empty constructor. |
| 79 | public FeedReaderContract() {} |
| 80 | |
| 81 | /* Inner class that defines the table contents */ |
| 82 | public static abstract class FeedEntry implements BaseColumns { |
| 83 | public static final String TABLE_NAME = "entry"; |
| 84 | public static final String COLUMN_NAME_ENTRY_ID = "entryid"; |
| 85 | public static final String COLUMN_NAME_TITLE = "title"; |
| 86 | public static final String COLUMN_NAME_SUBTITLE = "subtitle"; |
| 87 | ... |
| 88 | } |
| 89 | } |
| 90 | </pre> |
| 91 | |
| 92 | |
| 93 | |
| 94 | <h2 id="DbHelper">SQL Helper를 사용하여 데이터베이스 생성하기</h2> |
| 95 | |
| 96 | <p>데이터베이스의 모양을 정의한 후에는 데이터베이스 및 테이블을 생성 및 유지하는 |
| 97 | 메서드를 구현해야 합니다. 다음은 테이블을 생성하고 삭제하는 몇 가지 일반적인 |
| 98 | 명령문입니다.</P> |
| 99 | |
| 100 | <pre> |
| 101 | private static final String TEXT_TYPE = " TEXT"; |
| 102 | private static final String COMMA_SEP = ","; |
| 103 | private static final String SQL_CREATE_ENTRIES = |
| 104 | "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + |
| 105 | FeedEntry._ID + " INTEGER PRIMARY KEY," + |
| 106 | FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + |
| 107 | FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + |
| 108 | ... // Any other options for the CREATE command |
| 109 | " )"; |
| 110 | |
| 111 | private static final String SQL_DELETE_ENTRIES = |
| 112 | "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME; |
| 113 | </pre> |
| 114 | |
| 115 | <p>기기의 <a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">내부 |
| 116 | 저장소</a>에 저장하는 파일과 같이, Android는 데이터베이스를 |
| 117 | 애플리케이션과 관련된 개인 디스크 공간에 저장합니다. 기본적으로 이 공간은 다른 애플리케이션이 |
| 118 | 액세스할 수 없기 때문에 저장된 데이터는 안전하게 유지됩니다.</p> |
| 119 | |
| 120 | <p>유용한 API 집합이 {@link |
Mark Lu | c4a0139 | 2016-07-18 10:42:11 -0700 | [diff] [blame^] | 121 | android.database.sqlite.SQLiteOpenHelper} 클래스에서 제공됩니다. |
David Friedman | c921ee1 | 2015-06-29 22:14:16 -0700 | [diff] [blame] | 122 | 데이터베이스에 대한 참조를 가져오기 위해 이 클래스를 사용하는 경우, 시스템은 |
Mark Lu | c4a0139 | 2016-07-18 10:42:11 -0700 | [diff] [blame^] | 123 | 필요한 경우에 한해서만 데이터베이스 생성 및 업데이트와 같이 |
David Friedman | c921ee1 | 2015-06-29 22:14:16 -0700 | [diff] [blame] | 124 | 장시간 실행될 수 있는 작업을 |
Mark Lu | c4a0139 | 2016-07-18 10:42:11 -0700 | [diff] [blame^] | 125 | 수행하며, <em>앱이 시작되는 동안에는 이러한 작업을 수행하지 않습니다</em>. |
| 126 | {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 또는 |
David Friedman | c921ee1 | 2015-06-29 22:14:16 -0700 | [diff] [blame] | 127 | {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}를 호출하기만 하면 됩니다.</p> |
| 128 | |
Mark Lu | c4a0139 | 2016-07-18 10:42:11 -0700 | [diff] [blame^] | 129 | <p class="note"><strong>참고:</strong> 이러한 작업은 장시간 실행될 수도 있기 때문에 |
David Friedman | c921ee1 | 2015-06-29 22:14:16 -0700 | [diff] [blame] | 130 | {@link android.os.AsyncTask} 또는 {@link android.app.IntentService}와 같이 백그라운드 스레드에서 {@link |
| 131 | android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 또는 {@link |
| 132 | android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}를 |
| 133 | 호출해야 합니다.</p> |
| 134 | |
| 135 | <p>{@link android.database.sqlite.SQLiteOpenHelper}를 사용하려면 {@link |
| 136 | android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link |
| 137 | android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} 및 {@link |
| 138 | android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} 콜백 메서드를 |
| 139 | 재정의하는 하위 클래스를 생성합니다. 반드시 필요한 것은 아니지만 |
| 140 | {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}도 구현해야 하는 |
| 141 | 경우가 있을 수 있습니다.</p> |
| 142 | |
| 143 | <p>다음은 위에 표시된 명령 중 일부를 사용하여 구현한 {@link |
| 144 | android.database.sqlite.SQLiteOpenHelper}의 예입니다.</p> |
| 145 | |
| 146 | <pre> |
| 147 | public class FeedReaderDbHelper extends SQLiteOpenHelper { |
| 148 | // If you change the database schema, you must increment the database version. |
| 149 | public static final int DATABASE_VERSION = 1; |
| 150 | public static final String DATABASE_NAME = "FeedReader.db"; |
| 151 | |
| 152 | public FeedReaderDbHelper(Context context) { |
| 153 | super(context, DATABASE_NAME, null, DATABASE_VERSION); |
| 154 | } |
| 155 | public void onCreate(SQLiteDatabase db) { |
| 156 | db.execSQL(SQL_CREATE_ENTRIES); |
| 157 | } |
| 158 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
| 159 | // This database is only a cache for online data, so its upgrade policy is |
| 160 | // to simply to discard the data and start over |
| 161 | db.execSQL(SQL_DELETE_ENTRIES); |
| 162 | onCreate(db); |
| 163 | } |
| 164 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { |
| 165 | onUpgrade(db, oldVersion, newVersion); |
| 166 | } |
| 167 | } |
| 168 | </pre> |
| 169 | |
| 170 | <p>데이터베이스에 액세스하려면 {@link |
| 171 | android.database.sqlite.SQLiteOpenHelper}의 하위 클래스를 인스턴스화합니다.</p> |
| 172 | |
| 173 | <pre> |
| 174 | FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); |
| 175 | </pre> |
| 176 | |
| 177 | |
| 178 | |
| 179 | |
| 180 | <h2 id="WriteDbRow">데이터베이스에 정보 삽입하기</h2> |
| 181 | |
| 182 | <p>{@link android.content.ContentValues} |
| 183 | 개체를 {@link android.database.sqlite.SQLiteDatabase#insert insert()} 메서드에 전달하여 데이터를 데이터베이스에 삽입합니다.</p> |
| 184 | |
| 185 | <pre> |
| 186 | // Gets the data repository in write mode |
| 187 | SQLiteDatabase db = mDbHelper.getWritableDatabase(); |
| 188 | |
| 189 | // Create a new map of values, where column names are the keys |
| 190 | ContentValues values = new ContentValues(); |
| 191 | values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); |
| 192 | values.put(FeedEntry.COLUMN_NAME_TITLE, title); |
| 193 | values.put(FeedEntry.COLUMN_NAME_CONTENT, content); |
| 194 | |
| 195 | // Insert the new row, returning the primary key value of the new row |
| 196 | long newRowId; |
| 197 | newRowId = db.insert( |
| 198 | FeedEntry.TABLE_NAME, |
| 199 | FeedEntry.COLUMN_NAME_NULLABLE, |
| 200 | values); |
| 201 | </pre> |
| 202 | |
| 203 | <p>{@link android.database.sqlite.SQLiteDatabase#insert insert()}의 |
| 204 | 첫 번째 인수는 테이블 이름입니다. 두 번째 인수는 |
| 205 | 컬럼 이름을 제공합니다. 프레임워크는 {@link android.content.ContentValues}가 비어있을 경우 |
| 206 | 여기에 NULL을 삽입할 수 있습니다. 만약 이를 {@code "null"}로 설정할 경우, |
| 207 | 값이 없으면 프레임워크가 행을 삽입하지 않습니다.</p> |
| 208 | |
| 209 | |
| 210 | |
| 211 | |
| 212 | <h2 id="ReadDbRow">데이터베이스에서 정보 읽어오기</h2> |
| 213 | |
| 214 | <p>데이터베이스로부터 정보를 읽어오려면 {@link android.database.sqlite.SQLiteDatabase#query query()} |
| 215 | 메서드를 사용하고, 선택 기준과 원하는 컬럼을 전달합니다. |
| 216 | 이 메서드는 {@link android.database.sqlite.SQLiteDatabase#insert insert()} |
| 217 | 및 {@link android.database.sqlite.SQLiteDatabase#update update()} 요소를 결합합니다. 단, 삽입하려는 데이터보다는 가져오려는 데이터를 |
| 218 | 정의하는 컬럼 목록은 예외입니다. 쿼리 결과는 |
| 219 | {@link android.database.Cursor} 개체로 반환됩니다.</p> |
| 220 | |
| 221 | <pre> |
| 222 | SQLiteDatabase db = mDbHelper.getReadableDatabase(); |
| 223 | |
| 224 | // Define a <em>projection</em> that specifies which columns from the database |
| 225 | // you will actually use after this query. |
| 226 | String[] projection = { |
| 227 | FeedEntry._ID, |
| 228 | FeedEntry.COLUMN_NAME_TITLE, |
| 229 | FeedEntry.COLUMN_NAME_UPDATED, |
| 230 | ... |
| 231 | }; |
| 232 | |
| 233 | // How you want the results sorted in the resulting Cursor |
| 234 | String sortOrder = |
| 235 | FeedEntry.COLUMN_NAME_UPDATED + " DESC"; |
| 236 | |
| 237 | Cursor c = db.query( |
| 238 | FeedEntry.TABLE_NAME, // The table to query |
| 239 | projection, // The columns to return |
| 240 | selection, // The columns for the WHERE clause |
| 241 | selectionArgs, // The values for the WHERE clause |
| 242 | null, // don't group the rows |
| 243 | null, // don't filter by row groups |
| 244 | sortOrder // The sort order |
| 245 | ); |
| 246 | </pre> |
| 247 | |
| 248 | <p>커서 안의 행을 보려면 {@link android.database.Cursor} 이동 |
| 249 | 메서드 중 하나를 사용합니다. 이 메서드는 값을 읽어오기 전에 항상 먼저 호출해야 합니다. 일반적으로 결과의 |
| 250 | 처음 항목에 "읽기 위치"를 배치하는 {@link android.database.Cursor#moveToFirst}를 먼저 |
| 251 | 호출해야 합니다. {@link android.database.Cursor#getString |
| 252 | getString()} 또는 {@link android.database.Cursor#getLong getLong()}과 같은 {@link android.database.Cursor} 가져오기 메서드 |
| 253 | 중 하나를 호출하여 각 행에 대한 컬럼 값을 읽어올 수 있습니다. 가져오기 메서드 각각에 대해 |
| 254 | 원하는 컬럼의 인덱스 위치를 전달해야 하며, 이는 |
| 255 | {@link android.database.Cursor#getColumnIndex getColumnIndex()} 또는 |
| 256 | {@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}를 호출하여 가져올 수 있습니다. |
| 257 | 예를 들면 다음과 같습니다.</p> |
| 258 | |
| 259 | <pre> |
| 260 | cursor.moveToFirst(); |
| 261 | long itemId = cursor.getLong( |
| 262 | cursor.getColumnIndexOrThrow(FeedEntry._ID) |
| 263 | ); |
| 264 | </pre> |
| 265 | |
| 266 | |
| 267 | |
| 268 | |
| 269 | <h2 id="DeleteDbRow">데이터베이스에서 정보 삭제하기</h2> |
| 270 | |
| 271 | <p>테이블에서 행을 삭제하려면 행을 |
| 272 | 식별하는 선택 기준을 제공해야 합니다. 데이터베이스 API는 SQL 삽입을 방지하는 선택 |
| 273 | 기준을 생성하는 메커니즘을 제공합니다. 이 메커니즘은 |
| 274 | 선택 사양을 선택 절과 선택 인수로 나눕니다. 절은 |
| 275 | 보려는 컬럼을 정의하고, 이를 통해 컬럼 |
| 276 | 테스트를 결합할 수 있습니다. 인수는 절 안에 묶여 테스트되는 값입니다. |
| 277 | 이 결과는 일반 SQL 문과 같이 처리되지 않기 때문에 SQL 삽입의 |
| 278 | 영향을 받지 않습니다.</p> |
| 279 | |
| 280 | <pre> |
| 281 | // Define 'where' part of query. |
| 282 | String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; |
| 283 | // Specify arguments in placeholder order. |
| 284 | String[] selectionArgs = { String.valueOf(rowId) }; |
| 285 | // Issue SQL statement. |
| 286 | db.delete(table_name, selection, selectionArgs); |
| 287 | </pre> |
| 288 | |
| 289 | |
| 290 | |
| 291 | <h2 id="UpdateDbRow">데이터베이스 업데이트하기</h2> |
| 292 | |
| 293 | <p>데이터베이스 값의 하위 세트를 수정해야 하는 경우, {@link |
| 294 | android.database.sqlite.SQLiteDatabase#update update()} 메서드를 사용합니다.</p> |
| 295 | |
| 296 | <p>테이블을 업데이트하면 {@link |
| 297 | android.database.sqlite.SQLiteDatabase#insert insert()}의 콘텐츠 값 구문과 |
| 298 | {@link android.database.sqlite.SQLiteDatabase#delete delete()}의 {@code where} 구문이 결합됩니다.</p> |
| 299 | |
| 300 | <pre> |
| 301 | SQLiteDatabase db = mDbHelper.getReadableDatabase(); |
| 302 | |
| 303 | // New value for one column |
| 304 | ContentValues values = new ContentValues(); |
| 305 | values.put(FeedEntry.COLUMN_NAME_TITLE, title); |
| 306 | |
| 307 | // Which row to update, based on the ID |
| 308 | String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; |
| 309 | String[] selectionArgs = { String.valueOf(rowId) }; |
| 310 | |
| 311 | int count = db.update( |
| 312 | FeedReaderDbHelper.FeedEntry.TABLE_NAME, |
| 313 | values, |
| 314 | selection, |
| 315 | selectionArgs); |
| 316 | </pre> |
| 317 | |