Home | History | Annotate | Download | only in data-storage
      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 = &quot;entry&quot;;
     84         public static final String COLUMN_NAME_ENTRY_ID = &quot;entryid&quot;;
     85         public static final String COLUMN_NAME_TITLE = &quot;title&quot;;
     86         public static final String COLUMN_NAME_SUBTITLE = &quot;subtitle&quot;;
     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 = &quot; TEXT&quot;;
    102 private static final String COMMA_SEP = &quot;,&quot;;
    103 private static final String SQL_CREATE_ENTRIES =
    104     &quot;CREATE TABLE &quot; + FeedEntry.TABLE_NAME + &quot; (&quot; +
    105     FeedEntry._ID + &quot; INTEGER PRIMARY KEY,&quot; +
    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     &quot; )&quot;;
    110 
    111 private static final String SQL_DELETE_ENTRIES =
    112     &quot;DROP TABLE IF EXISTS &quot; + 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 = &quot;FeedReader.db&quot;;
    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 + &quot; LIKE ?&quot;;
    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 + &quot; LIKE ?&quot;;
    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