SQLite storage data Demo

Let's take a look at DBHelper first:

 

package com.scott.db;  
  
import android.content.Context;  
import android.database.sqlite.SQLiteDatabase;  
import android.database.sqlite.SQLiteOpenHelper;  
  
public class DBHelper extends SQLiteOpenHelper {  
  
    private static final String DATABASE_NAME = "test.db";  
    private static final int DATABASE_VERSION = 1;  
      
    public DBHelper(Context context) {  
        //CursorFactory is set to null, using the default value  
        super(context, DATABASE_NAME, null, DATABASE_VERSION);  
    }  
  
    //onCreate will be called when the database is first created  
    @Override  
    public void onCreate(SQLiteDatabase db) {  
        db.execSQL("CREATE TABLE IF NOT EXISTS person" +  
                "(_id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR, age INTEGER, info TEXT)");  
    }  
  
    //If the DATABASE_VERSION value is changed to 2, the system finds that the existing database version is different and will call onUpgrade  
    @Override  
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
        db.execSQL("ALTER TABLE person ADD COLUMN other STRING");  
    }  
}  

 As mentioned above, the onCreate method will be called when the database is first created, and we can execute the statement to create the table. When the system finds the version change, the onUpgrade method will be called, and we can execute statements such as modifying the table structure.

 

 

In order to facilitate our object-oriented use of data, we build a Person class, corresponding to the fields in the person table, as follows:

 

package com.scott.db;  
  
public class Person {  
    public int _id;  
    public String name;  
    public int age;  
    public String info;  
      
    public Person() {  
    }  
      
    public Person(String name, int age, String info) {  
        this.name = name;  
        this.age = age;  
        this.info = info;  
    }  
}

 Then, we need a DBManager to encapsulate all our business methods, the code is as follows:

 

 

package com.scott.db;  
  
import java.util.ArrayList;  
import java.util.List;  
  
import android.content.ContentValues;  
import android.content.Context;  
import android.database.Cursor;  
import android.database.sqlite.SQLiteDatabase;  
  
public class DBManager {  
    private DBHelper helper;  
    private SQLiteDatabase db;  
      
    public DBManager(Context context) {  
        helper = new DBHelper(context);  
        //Because getWritableDatabase internally calls mContext.openOrCreateDatabase(mName, 0, mFactory);  
        //So to make sure the context has been initialized, we can put the step of instantiating the DBManager in the onCreate of the Activity  
        db = helper.getWritableDatabase();  
    }  
      
    /**
     * add persons
     * @param persons
     */  
    public void add(List<Person> persons) {  
        db.beginTransaction(); //Start transaction  
        try {  
            for (Person person : persons) {  
                db.execSQL("INSERT INTO person VALUES(null, ?, ?, ?)", new Object[]{person.name, person.age, person.info});  
            }  
            db.setTransactionSuccessful(); //Set the transaction to complete successfully  
        } finally {  
            db.endTransaction(); //end transaction  
        }  
    }  
      
    /**
     * update person's age
     * @param person
     */  
    public void updateAge(Person person) {  
        ContentValues cv = new ContentValues();  
        cv.put("age", person.age);  
        db.update("person", cv, "name = ?", new String[]{person.name});  
    }  
      
    /**
     * delete old person
     * @param person
     */  
    public void deleteOldPerson(Person person) {  
        db.delete("person", "age >= ?", new String[]{String.valueOf(person.age)});  
    }  
      
    /**
     * query all persons, return list
     * @return List<Person>
     */  
    public List<Person> query() {  
        ArrayList<Person> persons = new ArrayList<Person>();  
        Cursor c = queryTheCursor();  
        while (c.moveToNext()) {  
            Person person = new Person();  
            person._id = c.getInt(c.getColumnIndex("_id"));  
            person.name = c.getString(c.getColumnIndex("name"));  
            person.age = c.getInt(c.getColumnIndex("age"));  
            person.info = c.getString(c.getColumnIndex("info"));  
            persons.add(person);  
        }  
        c.close();  
        return persons;  
    }  
      
    /**
     * query all persons, return cursor
     * @return  Cursor
     */  
    public Cursor queryTheCursor() {  
        Cursor c = db.rawQuery("SELECT * FROM person", null);  
        return c;  
    }  
      
    /**
     * close database
     */  
    public void closeDB() {  
        db.close();  
    }  
}  

We instantiate DBHelper in the DBManager construction method and obtain a SQLiteDatabase object as the database instance of the entire application; when adding multiple Person information, we use transaction processing to ensure data integrity; finally, we provide a closeDB method to release Database resources, this step is executed when our entire application is closed, this link is easy to be forgotten, so friends should pay attention.

 

 

We use the getWritableDatabase() method when we get the database instance. Maybe friends will have questions. In getWritableDatabase() and getReadableDatabase(), why did you choose the former as the database instance of the entire application? Here I would like to focus on analyzing this point with you.

Let's take a look at the getReadableDatabase() method in SQLiteOpenHelper:

 

public synchronized SQLiteDatabase getReadableDatabase() {  
    if (mDatabase != null && mDatabase.isOpen()) {  
        // If it is found that mDatabase is not empty and has been opened, return directly  
        return mDatabase;  
    }  
  
    if (mIsInitializing) {  
        // Throws an exception if initializing  
        throw new IllegalStateException("getReadableDatabase called recursively");  
    }  
  
    // Start instantiating the database mDatabase  
  
    try {  
        // Note that the getWritableDatabase() method is called here  
        return getWritableDatabase();  
    } catch (SQLiteException e) {  
        if (mName == null)  
            throw e; // Can't open a temp database read-only!  
        Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);  
    }  
  
    // Open the database as read-only if it cannot be opened in read-write mode  
  
    SQLiteDatabase db = null;  
    try {  
        mIsInitializing = true;  
        String path = mContext.getDatabasePath(mName).getPath();// Get the database path  
        // open the database as read-only  
        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);  
        if (db.getVersion() != mNewVersion) {  
            throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to "  
                    + mNewVersion + ": " + path);  
        }  
  
        onOpen(db);  
        Log.w(TAG, "Opened " + mName + " in read-only mode");  
        mDatabase = db;// Specify the newly opened database for mDatabase  
        return mDatabase;// return the open database  
    } finally {  
        mIsInitializing = false;  
        if (db != null && db != mDatabase)  
            db.close();  
    }  
}  

  In the getReadableDatabase() method, first determine whether a database instance already exists and is open, if so, return the instance directly, otherwise try to obtain a database instance in read-write mode, if the disk space is full, etc. If it fails, open the database in read-only mode, get the database instance and return it, and then assign mDatabase the latest opened database instance. Now that it's possible to call the getWritableDatabase() method, let's take a look:

 

 

public synchronized SQLiteDatabase getWritableDatabase() {  
    if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {  
        // Returns this instance if mDatabase is not empty, opened and not in read-only mode  
        return mDatabase;  
    }  
  
    if (mIsInitializing) {  
        throw new IllegalStateException("getWritableDatabase called recursively");  
    }  
  
    // If we have a read-only database open, someone could be using it  
    // (though they shouldn't), which would cause a lock to be held on  
    // the file, and our attempts to open the database read-write would  
    // fail waiting for the file lock. To prevent that, we acquire the  
    // lock on the read-only database, which shuts out other users.  
  
    boolean success = false;  
    SQLiteDatabase db = null;  
    // If mDatabase is not empty, lock to prevent other operations  
    if (mDatabase != null)  
        mDatabase.lock();  
    try {  
        mIsInitializing = true;  
        if (mName == null) {  
            db = SQLiteDatabase.create(null);  
        } else {  
            // open or create database  
            db = mContext.openOrCreateDatabase(mName, 0, mFactory);  
        }  
        // Get the database version (if the database was just created, the version is 0)  
        int version = db.getVersion();  
        // Compare versions (the version mNewVersion in our code is 1)  
        if (version != mNewVersion) {  
            db.beginTransaction();// start transaction  
            try {  
                if (version == 0) {  
                    // execute our onCreate method  
                    onCreate(db);  
                } else {  
                    // If our application upgrades mNewVersion to 2, and the original version is 1, execute the onUpgrade method  
                    onUpgrade(db, version, mNewVersion);  
                }  
                db.setVersion(mNewVersion);// Set the latest version  
                db.setTransactionSuccessful();// Set transaction success  
            } finally {  
                db.endTransaction();// End the transaction  
            }  
        }  
  
        onOpen(db);  
        success = true;  
        return db;// return the database instance in read-write mode  
    } finally {  
        mIsInitializing = false;  
        if (success) {  
            // open successfully  
            if (mDatabase != null) {  
                // If mDatabase has a value, close it first  
                try {  
                    mDatabase.close();  
                } catch (Exception e) {  
                }  
                mDatabase.unlock();// Unlock  
            }  
            mDatabase = db;// Assign to mDatabase  
        } else {  
            // In case of failure to open: unlock, close  
            if (mDatabase != null)  
                mDatabase.unlock();  
            if (db != null)  
                db.close();  
        }  
    }  
}  

 As you can see, the key steps are to first determine if mDatabase is not empty and open and not in read-only mode, then return directly, otherwise if mDatabase is not empty, lock it, and then start to open or create a database, compare versions, according to The version number is used to call the corresponding method, set a new version number for the database, and finally release the old non-empty mDatabase and unlock it, assign the newly opened database instance to mDatabase, and return the latest instance.

 

 

After reading the above process, you may know a lot. If it is not in the case of full disk space, getReadableDatabase() will generally return the same database instance as getWritableDatabase(), so we use getWritableDatabase in the DBManager construction method () It is possible to get the database instance used by the entire application. Of course, if you are really worried about this happening, then you can use getWritableDatabase() to get the data instance first, and if you encounter an exception, try to get the instance with getReadableDatabase(), of course, the instance you get at this time can only be read but not written .

Finally, let's see how to use these data manipulation methods to display data, the following is the layout file and code of MainActivity.Java :

 

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent">  
    <Button  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="add"  
        android:onClick="add"/>  
    <Button  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="update"  
        android:onClick="update"/>  
    <Button  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="delete"  
        android:onClick="delete"/>  
    <Button  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="query"  
        android:onClick="query"/>  
    <Button  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="queryTheCursor"  
        android:onClick="queryTheCursor"/>  
    <ListView  
        android:id="@+id/listView"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"/>  
</LinearLayout>  

 

package com.scott.db;  
  
import java.util.ArrayList;  
import java.util.HashMap;  
import java.util.List;  
import java.util.Map;  
  
import android.app.Activity;  
import android.database.Cursor;  
import android.database.CursorWrapper;  
import android.os.Bundle;  
import android.view.View;  
import android.widget.ListView;  
import android.widget.SimpleAdapter;  
import android.widget.SimpleCursorAdapter;  
  
  
public class MainActivity extends Activity {  
     
    private DBManager mgr;  
    private ListView listView;  
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate (savedInstanceState);  
        setContentView(R.layout.main);  
        listView = (ListView) findViewById(R.id.listView);  
        //Initialize DBManager  
        mgr = new DBManager(this);  
    }  
      
    @Override  
    protected void onDestroy() {  
        super.onDestroy ();  
        //DB should be released when the last Activity of the application is closed  
        mgr.closeDB();  
    }  
      
    public void add(View view) {  
        ArrayList<Person> persons = new ArrayList<Person>();  
          
        Person person1 = new Person("Ella", 22, "lively girl");  
        Person person2 = new Person("Jenny", 22, "beautiful girl");  
        Person person3 = new Person("Jessica", 23, "sexy girl");  
        Person person4 = new Person("Kelly", 23, "hot baby");  
        Person person5 = new Person("Jane", 25, "a pretty woman");  
          
        persons.add(person1);  
        persons.add(person2);  
        persons.add(person3);  
        persons.add(person4);  
        persons.add(person5);  
          
        mgr.add(persons);  
    }  
      
    public void update(View view) {  
        Person person = new Person();  
        person.name = "Jane";  
        person.age = 30;  
        mgr.updateAge(person);  
    }  
      
    public void delete(View view) {  
        Person person = new Person();  
        person.age = 30;  
        mgr.deleteOldPerson(person);  
    }  
      
    public void query(View view) {  
        List<Person> persons = mgr.query();  
        ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>();  
        for (Person person : persons) {  
            HashMap<String, String> map = new HashMap<String, String>();  
            map.put("name", person.name);  
            map.put("info", person.age + " years old, " + person.info);  
            list.add(map);  
        }  
        SimpleAdapter adapter = new SimpleAdapter(this, list, android.R.layout.simple_list_item_2,  
                    new String[]{"name", "info"}, new int[]{android.R.id.text1, android.R.id.text2});  
        listView.setAdapter(adapter);  
    }  
      
    public void queryTheCursor(View view) {  
        Cursor c = mgr.queryTheCursor();  
        startManagingCursor(c); //Entrust the activity to manage the life cycle of Cursor according to its own life cycle  
        CursorWrapper cursorWrapper = new CursorWrapper(c) {  
            @Override  
            public String getString(int columnIndex) {  
                // add age before profile  
                if (getColumnName(columnIndex).equals("info")) {  
                    int age = getInt(getColumnIndex("age"));  
                    return age + " years old, " + super.getString(columnIndex);  
                }  
                return super.getString(columnIndex);  
            }  
        };  
        // Make sure there is a "_id" column in the query result  
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2,   
                cursorWrapper, new String[]{"name", "info"}, new int[]{android.R.id.text1, android.R.id.text2});  
        ListView listView = (ListView) findViewById(R.id.listView);  
        listView.setAdapter(adapter);  
    }  
}  

 What needs to be paid attention to here is the application of SimpleCursorAdapter. When we use this adapter, we must first get a Cursor object. There are several problems: how to manage the life cycle of Cursor, and what needs to be paid attention to in Cursor result set if Cursor is packaged.

 

If you manually manage Cursor, it will be very troublesome, and there are certain risks. If it is not handled properly, an exception will occur during operation. Fortunately, Activity provides us with the startManagingCursor(Cursor cursor) method, which will manage the current according to the life cycle of Activity. Cursor object, the following is the description of the method:

/**
     * This method allows the activity to take care of managing the given
     * {@link Cursor}'s lifecycle for you based on the activity's lifecycle.
     * That is, when the activity is stopped it will automatically call 
     * {@link Cursor#deactivate} on the given Cursor, and when it is later restarted 
     * it will call {@link Cursor#requery} for you.  When the activity is 
     * destroyed, all managed Cursors will be closed automatically. 
     *  
     * @param c The Cursor to be managed. 
     *  
     * @see #managedQuery(android.net.Uri , String[], String, String[], String) 
     * @see #stopManagingCursor 
     */  

 文中提到,startManagingCursor方法会根据Activity的生命周期去管理当前的Cursor对象的生命周期,就是说当Activity停止时他会自动调用Cursor的deactivate方法,禁用游标,当Activity重新回到屏幕时它会调用Cursor的requery方法再次查询,当Activity摧毁时,被管理的Cursor都会自动关闭释放。

 

How to wrap Cursor: We will use the CursorWrapper object to wrap our Cursor object and implement the data conversion work we need. This CursorWrapper actually implements the Cursor interface. The Cursor obtained by our query is actually a reference of Cursor, and what the system actually returns to us must be an object instance of an implementation class of the Cursor interface. We wrap this instance with CursorWrapper, and then use SimpleCursorAdapter to display the result on the list.

What needs to be paid attention to in the Cursor result set: One of the most important things to pay attention to is that we must include a "_id" column in our result set, otherwise SimpleCursorAdapter will turn around and not recognize people. Why must this be the case? Because this is derived from the SQLite specification, the primary key is "_id" as the standard. There are three solutions: first, do it according to the specification when building the table; second, use an alias when querying, for example: SELECT id AS _id FROM person; third, make a fuss in CursorWrapper:

CursorWrapper cursorWrapper = new CursorWrapper(c) {  
    @Override  
    public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException {  
        if (columnName.equals("_id")) {  
            return super.getColumnIndex("id");  
        }  
        return super.getColumnIndexOrThrow(columnName);  
    }  
};  

 If we try to get the column index corresponding to "_id" from CursorWrapper, we just return the column index corresponding to "id" in the query result.

 

Finally, let's see how it turns out:

 

 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326323040&siteId=291194637