Home | History | Annotate | Download | only in data-storage
      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 = &quot;entry&quot;;
     84         public static final String COLUMN_NAME_TITLE = &quot;title&quot;;
     85         public static final String COLUMN_NAME_SUBTITLE = &quot;subtitle&quot;;
     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 = &quot; TEXT&quot;;
    100 private static final String COMMA_SEP = &quot;,&quot;;
    101 private static final String SQL_CREATE_ENTRIES =
    102     &quot;CREATE TABLE &quot; + FeedEntry.TABLE_NAME + &quot; (&quot; +
    103     FeedEntry._ID + &quot; INTEGER PRIMARY KEY,&quot; +
    104     FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    105     FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE + &quot; )&quot;;
    106 
    107 private static final String SQL_DELETE_ENTRIES =
    108     &quot;DROP TABLE IF EXISTS &quot; + 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 = &quot;FeedReader.db&quot;;
    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 + &quot; = ?&quot;;
    230 String[] selectionArgs = { &quot;My Title&quot; };
    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 + &quot; LIKE ?&quot;;
    282 // Specify arguments in placeholder order.
    283 String[] selectionArgs = { &quot;MyTitle&quot; };
    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 + &quot; LIKE ?&quot;;
    308 String[] selectionArgs = { &quot;MyTitle&quot; };
    309 
    310 int count = db.update(
    311     FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    312     values,
    313     selection,
    314     selectionArgs);
    315 </pre>
    316 
    317