1 page.title=Saving Data in SQL Databases 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>This lesson teaches you to</h2> 14 <ol> 15 <li><a href="#DefineContract">Define a Schema and Contract</a></li> 16 <li><a href="#DbHelper">Create a Database Using a SQL Helper</a></li> 17 <li><a href="#WriteDbRow">Put Information into a Database</a></li> 18 <li><a href="#ReadDbRow">Read Information from a Database</a></li> 19 <li><a href="#DeleteDbRow">Delete Information from a Database</a></li> 20 <li><a href="#UpdateDbRow">Update a Database</a></li> 21 </ol> 22 23 <h2>You should also read</h2> 24 <ul> 25 <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">Using Databases</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>Saving data to a database is ideal for repeating or structured data, 42 such as contact information. This class assumes that you are 43 familiar with SQL databases in general and helps you get started with 44 SQLite databases on Android. The APIs you'll need to use a database 45 on Android are available in the {@link android.database.sqlite} package.</p> 46 47 48 <h2 id="DefineContract">Define a Schema and Contract</h2> 49 50 <p>One of the main principles of SQL databases is the schema: a formal 51 declaration of how the database is organized. The schema is reflected in the SQL 52 statements that you use to create your database. You may find it helpful to 53 create a companion class, known as a <em>contract</em> class, which explicitly specifies 54 the layout of your schema in a systematic and self-documenting way.</p> 55 56 <p>A contract class is a container for constants that define names for URIs, 57 tables, and columns. The contract class allows you to use the same constants 58 across all the other classes in the same package. This lets you change a column 59 name in one place and have it propagate throughout your code.</p> 60 61 <p>A good way to organize a contract class is to put definitions that are 62 global to your whole database in the root level of the class. Then create an inner 63 class for each table that enumerates its columns.</p> 64 65 <p class="note"><strong>Note:</strong> By implementing the {@link 66 android.provider.BaseColumns} interface, your inner class can inherit a primary 67 key field called {@code _ID} that some Android classes such as cursor adaptors 68 will expect it to have. It's not required, but this can help your database 69 work harmoniously with the Android framework.</p> 70 71 <p>For example, this snippet defines the table name and column names for a 72 single table:</p> 73 74 75 <pre> 76 public final class FeedReaderContract { 77 // To prevent someone from accidentally instantiating the contract class, 78 // make the constructor private. 79 private FeedReaderContract() {} 80 81 /* Inner class that defines the table contents */ 82 public static class FeedEntry implements BaseColumns { 83 public static final String TABLE_NAME = "entry"; 84 public static final String COLUMN_NAME_TITLE = "title"; 85 public static final String COLUMN_NAME_SUBTITLE = "subtitle"; 86 } 87 } 88 </pre> 89 90 91 92 <h2 id="DbHelper">Create a Database Using a SQL Helper</h2> 93 94 <p>Once you have defined how your database looks, you should implement methods 95 that create and maintain the database and tables. Here are some typical 96 statements that create and delete a table:</P> 97 98 <pre> 99 private static final String TEXT_TYPE = " TEXT"; 100 private static final String COMMA_SEP = ","; 101 private static final String SQL_CREATE_ENTRIES = 102 "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" + 103 FeedEntry._ID + " INTEGER PRIMARY KEY," + 104 FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + 105 FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE + " )"; 106 107 private static final String SQL_DELETE_ENTRIES = 108 "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME; 109 </pre> 110 111 <p>Just like files that you save on the device's <a 112 href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal 113 storage</a>, Android stores your database in private disk space that's associated 114 application. Your data is secure, because by default this area is not 115 accessible to other applications.</p> 116 117 <p>A useful set of APIs is available in the {@link 118 android.database.sqlite.SQLiteOpenHelper} class. 119 When you use this class to obtain references to your database, the system 120 performs the potentially 121 long-running operations of creating and updating the database only when 122 needed and <em>not during app startup</em>. All you need to do is call 123 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or 124 {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.</p> 125 126 <p class="note"><strong>Note:</strong> Because they can be long-running, 127 be sure that you call {@link 128 android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or {@link 129 android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} in a background thread, 130 such as with {@link android.os.AsyncTask} or {@link android.app.IntentService}.</p> 131 132 <p>To use {@link android.database.sqlite.SQLiteOpenHelper}, create a subclass that 133 overrides the {@link 134 android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link 135 android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} and {@link 136 android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} callback methods. You may also 137 want to implement {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, 138 but it's not required.</p> 139 140 <p>For example, here's an implementation of {@link 141 android.database.sqlite.SQLiteOpenHelper} that uses some of the commands shown above:</p> 142 143 <pre> 144 public class FeedReaderDbHelper extends SQLiteOpenHelper { 145 // If you change the database schema, you must increment the database version. 146 public static final int DATABASE_VERSION = 1; 147 public static final String DATABASE_NAME = "FeedReader.db"; 148 149 public FeedReaderDbHelper(Context context) { 150 super(context, DATABASE_NAME, null, DATABASE_VERSION); 151 } 152 public void onCreate(SQLiteDatabase db) { 153 db.execSQL(SQL_CREATE_ENTRIES); 154 } 155 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 156 // This database is only a cache for online data, so its upgrade policy is 157 // to simply to discard the data and start over 158 db.execSQL(SQL_DELETE_ENTRIES); 159 onCreate(db); 160 } 161 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 162 onUpgrade(db, oldVersion, newVersion); 163 } 164 } 165 </pre> 166 167 <p>To access your database, instantiate your subclass of {@link 168 android.database.sqlite.SQLiteOpenHelper}:</p> 169 170 <pre> 171 FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); 172 </pre> 173 174 175 176 177 <h2 id="WriteDbRow">Put Information into a Database</h2> 178 179 <p>Insert data into the database by passing a {@link android.content.ContentValues} 180 object to the {@link android.database.sqlite.SQLiteDatabase#insert insert()} method:</p> 181 182 <pre> 183 // Gets the data repository in write mode 184 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 185 186 // Create a new map of values, where column names are the keys 187 ContentValues values = new ContentValues(); 188 values.put(FeedEntry.COLUMN_NAME_TITLE, title); 189 values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle); 190 191 // Insert the new row, returning the primary key value of the new row 192 long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values); 193 </pre> 194 195 <p>The first argument for {@link android.database.sqlite.SQLiteDatabase#insert insert()} 196 is simply the table name. </p> 197 198 <p>The second argument tells the framework what to do in the event that the 199 {@link android.content.ContentValues} is empty (i.e., you did not 200 {@link android.content.ContentValues#put put} any values). 201 If you specify the name of a column, the framework inserts a row and sets 202 the value of that column to null. If you specify <code>null</code>, like in this 203 code sample, the framework does not insert a row when there are no values.</p> 204 205 206 207 208 <h2 id="ReadDbRow">Read Information from a Database</h2> 209 210 <p>To read from a database, use the {@link android.database.sqlite.SQLiteDatabase#query query()} 211 method, passing it your selection criteria and desired columns. 212 The method combines elements of {@link android.database.sqlite.SQLiteDatabase#insert insert()} 213 and {@link android.database.sqlite.SQLiteDatabase#update update()}, except the column list 214 defines the data you want to fetch, rather than the data to insert. The results of the query 215 are returned to you in a {@link android.database.Cursor} object.</p> 216 217 <pre> 218 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 219 220 // Define a <em>projection</em> that specifies which columns from the database 221 // you will actually use after this query. 222 String[] projection = { 223 FeedEntry._ID, 224 FeedEntry.COLUMN_NAME_TITLE, 225 FeedEntry.COLUMN_NAME_SUBTITLE 226 }; 227 228 // Filter results WHERE "title" = 'My Title' 229 String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?"; 230 String[] selectionArgs = { "My Title" }; 231 232 // How you want the results sorted in the resulting Cursor 233 String sortOrder = 234 FeedEntry.COLUMN_NAME_SUBTITLE + " DESC"; 235 236 Cursor c = db.query( 237 FeedEntry.TABLE_NAME, // The table to query 238 projection, // The columns to return 239 selection, // The columns for the WHERE clause 240 selectionArgs, // The values for the WHERE clause 241 null, // don't group the rows 242 null, // don't filter by row groups 243 sortOrder // The sort order 244 ); 245 </pre> 246 247 <p>To look at a row in the cursor, use one of the {@link android.database.Cursor} move 248 methods, which you must always call before you begin reading values. Generally, you should start 249 by calling {@link android.database.Cursor#moveToFirst}, which places the "read position" on the 250 first entry in the results. For each row, you can read a column's value by calling one of the 251 {@link android.database.Cursor} get methods, such as {@link android.database.Cursor#getString 252 getString()} or {@link android.database.Cursor#getLong getLong()}. For each of the get methods, 253 you must pass the index position of the column you desire, which you can get by calling 254 {@link android.database.Cursor#getColumnIndex getColumnIndex()} or 255 {@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. 256 For example:</p> 257 258 <pre> 259 cursor.moveToFirst(); 260 long itemId = cursor.getLong( 261 cursor.getColumnIndexOrThrow(FeedEntry._ID) 262 ); 263 </pre> 264 265 266 267 268 <h2 id="DeleteDbRow">Delete Information from a Database</h2> 269 270 <p>To delete rows from a table, you need to provide selection criteria that 271 identify the rows. The database API provides a mechanism for creating selection 272 criteria that protects against SQL injection. The mechanism divides the 273 selection specification into a selection clause and selection arguments. The 274 clause defines the columns to look at, and also allows you to combine column 275 tests. The arguments are values to test against that are bound into the clause. 276 Because the result isn't handled the same as a regular SQL statement, it is 277 immune to SQL injection.</p> 278 279 <pre> 280 // Define 'where' part of query. 281 String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?"; 282 // Specify arguments in placeholder order. 283 String[] selectionArgs = { "MyTitle" }; 284 // Issue SQL statement. 285 db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs); 286 </pre> 287 288 289 290 <h2 id="UpdateDbRow">Update a Database</h2> 291 292 <p>When you need to modify a subset of your database values, use the {@link 293 android.database.sqlite.SQLiteDatabase#update update()} method.</p> 294 295 <p>Updating the table combines the content values syntax of {@link 296 android.database.sqlite.SQLiteDatabase#insert insert()} with the {@code where} syntax 297 of {@link android.database.sqlite.SQLiteDatabase#delete delete()}.</p> 298 299 <pre> 300 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 301 302 // New value for one column 303 ContentValues values = new ContentValues(); 304 values.put(FeedEntry.COLUMN_NAME_TITLE, title); 305 306 // Which row to update, based on the title 307 String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?"; 308 String[] selectionArgs = { "MyTitle" }; 309 310 int count = db.update( 311 FeedReaderDbHelper.FeedEntry.TABLE_NAME, 312 values, 313 selection, 314 selectionArgs); 315 </pre> 316 317