1 page.title=Saving Data in SQL Databases 2 3 trainingnavtop=true 4 previous.title=Saving Data in Files 5 previous.link=files.html 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 // 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">Create a Database Using a SQL Helper</h2> 95 96 <p>Once you have defined how your database looks, you should implement methods 97 that create and maintain the database and tables. Here are some typical 98 statements that create and delete a table:</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>Just like files that you save on the device's <a 116 href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal 117 storage</a>, Android stores your database in private disk space that's associated 118 application. Your data is secure, because by default this area is not 119 accessible to other applications.</p> 120 121 <p>A useful set of APIs is available in the {@link 122 android.database.sqlite.SQLiteOpenHelper} class. 123 When you use this class to obtain references to your database, the system 124 performs the potentially 125 long-running operations of creating and updating the database only when 126 needed and <em>not during app startup</em>. All you need to do is call 127 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or 128 {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.</p> 129 130 <p class="note"><strong>Note:</strong> Because they can be long-running, 131 be sure that you call {@link 132 android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or {@link 133 android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} in a background thread, 134 such as with {@link android.os.AsyncTask} or {@link android.app.IntentService}.</p> 135 136 <p>To use {@link android.database.sqlite.SQLiteOpenHelper}, create a subclass that 137 overrides the {@link 138 android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link 139 android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} and {@link 140 android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} callback methods. You may also 141 want to implement {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, 142 but it's not required.</p> 143 144 <p>For example, here's an implementation of {@link 145 android.database.sqlite.SQLiteOpenHelper} that uses some of the commands shown above:</p> 146 147 <pre> 148 public class FeedReaderDbHelper extends SQLiteOpenHelper { 149 // If you change the database schema, you must increment the database version. 150 public static final int DATABASE_VERSION = 1; 151 public static final String DATABASE_NAME = "FeedReader.db"; 152 153 public FeedReaderDbHelper(Context context) { 154 super(context, DATABASE_NAME, null, DATABASE_VERSION); 155 } 156 public void onCreate(SQLiteDatabase db) { 157 db.execSQL(SQL_CREATE_ENTRIES); 158 } 159 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 160 // This database is only a cache for online data, so its upgrade policy is 161 // to simply to discard the data and start over 162 db.execSQL(SQL_DELETE_ENTRIES); 163 onCreate(db); 164 } 165 public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 166 onUpgrade(db, oldVersion, newVersion); 167 } 168 } 169 </pre> 170 171 <p>To access your database, instantiate your subclass of {@link 172 android.database.sqlite.SQLiteOpenHelper}:</p> 173 174 <pre> 175 FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); 176 </pre> 177 178 179 180 181 <h2 id="WriteDbRow">Put Information into a Database</h2> 182 183 <p>Insert data into the database by passing a {@link android.content.ContentValues} 184 object to the {@link android.database.sqlite.SQLiteDatabase#insert insert()} method:</p> 185 186 <pre> 187 // Gets the data repository in write mode 188 SQLiteDatabase db = mDbHelper.getWritableDatabase(); 189 190 // Create a new map of values, where column names are the keys 191 ContentValues values = new ContentValues(); 192 values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id); 193 values.put(FeedEntry.COLUMN_NAME_TITLE, title); 194 values.put(FeedEntry.COLUMN_NAME_CONTENT, content); 195 196 // Insert the new row, returning the primary key value of the new row 197 long newRowId; 198 newRowId = db.insert( 199 FeedEntry.TABLE_NAME, 200 FeedEntry.COLUMN_NAME_NULLABLE, 201 values); 202 </pre> 203 204 <p>The first argument for {@link android.database.sqlite.SQLiteDatabase#insert insert()} 205 is simply the table name. The second argument provides 206 the name of a column in which the framework can insert NULL in the event that the 207 {@link android.content.ContentValues} is empty (if you instead set this to {@code "null"}, 208 then the framework will not insert a row when there are no values).</p> 209 210 211 212 213 <h2 id="ReadDbRow">Read Information from a Database</h2> 214 215 <p>To read from a database, use the {@link android.database.sqlite.SQLiteDatabase#query query()} 216 method, passing it your selection criteria and desired columns. 217 The method combines elements of {@link android.database.sqlite.SQLiteDatabase#insert insert()} 218 and {@link android.database.sqlite.SQLiteDatabase#update update()}, except the column list 219 defines the data you want to fetch, rather than the data to insert. The results of the query 220 are returned to you in a {@link android.database.Cursor} object.</p> 221 222 <pre> 223 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 224 225 // Define a <em>projection</em> that specifies which columns from the database 226 // you will actually use after this query. 227 String[] projection = { 228 FeedEntry._ID, 229 FeedEntry.COLUMN_NAME_TITLE, 230 FeedEntry.COLUMN_NAME_UPDATED, 231 ... 232 }; 233 234 // How you want the results sorted in the resulting Cursor 235 String sortOrder = 236 FeedEntry.COLUMN_NAME_UPDATED + " DESC"; 237 238 Cursor c = db.query( 239 FeedEntry.TABLE_NAME, // The table to query 240 projection, // The columns to return 241 selection, // The columns for the WHERE clause 242 selectionArgs, // The values for the WHERE clause 243 null, // don't group the rows 244 null, // don't filter by row groups 245 sortOrder // The sort order 246 ); 247 </pre> 248 249 <p>To look at a row in the cursor, use one of the {@link android.database.Cursor} move 250 methods, which you must always call before you begin reading values. Generally, you should start 251 by calling {@link android.database.Cursor#moveToFirst}, which places the "read position" on the 252 first entry in the results. For each row, you can read a column's value by calling one of the 253 {@link android.database.Cursor} get methods, such as {@link android.database.Cursor#getString 254 getString()} or {@link android.database.Cursor#getLong getLong()}. For each of the get methods, 255 you must pass the index position of the column you desire, which you can get by calling 256 {@link android.database.Cursor#getColumnIndex getColumnIndex()} or 257 {@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. 258 For example:</p> 259 260 <pre> 261 cursor.moveToFirst(); 262 long itemId = cursor.getLong( 263 cursor.getColumnIndexOrThrow(FeedEntry._ID) 264 ); 265 </pre> 266 267 268 269 270 <h2 id="DeleteDbRow">Delete Information from a Database</h2> 271 272 <p>To delete rows from a table, you need to provide selection criteria that 273 identify the rows. The database API provides a mechanism for creating selection 274 criteria that protects against SQL injection. The mechanism divides the 275 selection specification into a selection clause and selection arguments. The 276 clause defines the columns to look at, and also allows you to combine column 277 tests. The arguments are values to test against that are bound into the clause. 278 Because the result isn't handled the same as a regular SQL statement, it is 279 immune to SQL injection.</p> 280 281 <pre> 282 // Define 'where' part of query. 283 String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; 284 // Specify arguments in placeholder order. 285 String[] selectionArgs = { String.valueOf(rowId) }; 286 // Issue SQL statement. 287 db.delete(table_name, selection, selectionArgs); 288 </pre> 289 290 291 292 <h2 id="UpdateDbRow">Update a Database</h2> 293 294 <p>When you need to modify a subset of your database values, use the {@link 295 android.database.sqlite.SQLiteDatabase#update update()} method.</p> 296 297 <p>Updating the table combines the content values syntax of {@link 298 android.database.sqlite.SQLiteDatabase#insert insert()} with the {@code where} syntax 299 of {@link android.database.sqlite.SQLiteDatabase#delete delete()}.</p> 300 301 <pre> 302 SQLiteDatabase db = mDbHelper.getReadableDatabase(); 303 304 // New value for one column 305 ContentValues values = new ContentValues(); 306 values.put(FeedEntry.COLUMN_NAME_TITLE, title); 307 308 // Which row to update, based on the ID 309 String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?"; 310 String[] selectionArgs = { String.valueOf(rowId) }; 311 312 int count = db.update( 313 FeedReaderDbHelper.FeedEntry.TABLE_NAME, 314 values, 315 selection, 316 selectionArgs); 317 </pre> 318 319