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