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