1page.title=Saving Data in SQL Databases
2page.tags=data storage
3helpoutsWidget=true
4
5trainingnavtop=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,
42such as contact information. This class assumes that you are
43familiar with SQL databases in general and helps you get started with
44SQLite databases on Android. The APIs you'll need to use a database
45on 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
51declaration of how the database is organized. The schema is reflected in the SQL
52statements that you use to create your database.  You may find it helpful to
53create a companion class, known as a <em>contract</em> class, which explicitly specifies
54the 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,
57tables, and columns. The contract class allows you to use the same constants
58across all the other classes in the same package. This lets you change a column
59name 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
62global to your whole database in the root level of the class. Then create an inner
63class for each table that enumerates its columns.</p>
64
65<p class="note"><strong>Note:</strong> By implementing the {@link
66android.provider.BaseColumns} interface, your inner class can inherit a primary
67key field called {@code _ID} that some Android classes such as cursor adaptors
68will expect it to have.  It's not required, but this can help your database
69work harmoniously with the Android framework.</p>
70
71<p>For example, this snippet defines the table name and column names for a
72single table:</p>
73
74
75<pre>
76public 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
97that create and maintain the database and tables.  Here are some typical
98statements that create and delete a table:</P>
99
100<pre>
101private static final String TEXT_TYPE = &quot; TEXT&quot;;
102private static final String COMMA_SEP = &quot;,&quot;;
103private 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
111private 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
116href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal
117storage</a>, Android stores your database in private disk space that's associated
118application. Your data is secure, because by default this area is not
119accessible to other applications.</p>
120
121<p>A useful set of APIs is available in the {@link
122android.database.sqlite.SQLiteOpenHelper} class.
123When you use this class to obtain references to your database, the system
124performs the potentially
125long-running operations of creating and updating the database only when
126needed 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,
131be sure that you call {@link
132android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} or {@link
133android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} in a background thread,
134such 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
137overrides the {@link
138android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link
139android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} and {@link
140android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} callback methods. You may also
141want to implement {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()},
142but it's not required.</p>
143
144<p>For example, here's an implementation of {@link
145android.database.sqlite.SQLiteOpenHelper} that uses some of the commands shown above:</p>
146
147<pre>
148public 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
172android.database.sqlite.SQLiteOpenHelper}:</p>
173
174<pre>
175FeedReaderDbHelper 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}
184object to the {@link android.database.sqlite.SQLiteDatabase#insert insert()} method:</p>
185
186<pre>
187// Gets the data repository in write mode
188SQLiteDatabase db = mDbHelper.getWritableDatabase();
189
190// Create a new map of values, where column names are the keys
191ContentValues values = new ContentValues();
192values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
193values.put(FeedEntry.COLUMN_NAME_TITLE, title);
194values.put(FeedEntry.COLUMN_NAME_CONTENT, content);
195
196// Insert the new row, returning the primary key value of the new row
197long newRowId;
198newRowId = 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()}
205is simply the table name. The second argument provides
206the 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"},
208then 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()}
216method, passing it your selection criteria and desired columns.
217The method combines elements of {@link android.database.sqlite.SQLiteDatabase#insert insert()}
218and {@link android.database.sqlite.SQLiteDatabase#update update()}, except the column list
219defines the data you want to fetch, rather than the data to insert. The results of the query
220are returned to you in a {@link android.database.Cursor} object.</p>
221
222<pre>
223SQLiteDatabase 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.
227String[] 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
235String sortOrder =
236    FeedEntry.COLUMN_NAME_UPDATED + " DESC";
237
238Cursor 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
250methods, which you must always call before you begin reading values. Generally, you should start
251by calling {@link android.database.Cursor#moveToFirst}, which places the "read position" on the
252first 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
254getString()} or {@link android.database.Cursor#getLong getLong()}. For each of the get methods,
255you 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()}.
258For example:</p>
259
260<pre>
261cursor.moveToFirst();
262long 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
273identify the rows. The database API provides a mechanism for creating selection
274criteria that protects against SQL injection. The mechanism divides the
275selection specification into a selection clause and selection arguments. The
276clause defines the columns to look at, and also allows you to combine column
277tests. The arguments are values to test against that are bound into the clause.
278Because the result isn't handled the same as a regular SQL statement, it is
279immune to SQL injection.</p>
280
281<pre>
282// Define 'where' part of query.
283String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + &quot; LIKE ?&quot;;
284// Specify arguments in placeholder order.
285String[] selectionArgs = { String.valueOf(rowId) };
286// Issue SQL statement.
287db.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
295android.database.sqlite.SQLiteDatabase#update update()} method.</p>
296
297<p>Updating the table combines the content values syntax of {@link
298android.database.sqlite.SQLiteDatabase#insert insert()}  with the {@code where} syntax
299of {@link android.database.sqlite.SQLiteDatabase#delete delete()}.</p>
300
301<pre>
302SQLiteDatabase db = mDbHelper.getReadableDatabase();
303
304// New value for one column
305ContentValues values = new ContentValues();
306values.put(FeedEntry.COLUMN_NAME_TITLE, title);
307
308// Which row to update, based on the ID
309String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + &quot; LIKE ?&quot;;
310String[] selectionArgs = { String.valueOf(rowId) };
311
312int count = db.update(
313    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
314    values,
315    selection,
316    selectionArgs);
317</pre>
318
319