SQLite storage data for Android data storage [2]

 Introduction to the SQLiteOpenHelper class:

SQLiteOpenHelper is a helper class for SQLiteDatabase that manages database creation and version updates. Generally, a class is created to inherit it and implement its onCreate and onUpgrade methods.

method name method description
SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version)

construction method, where

context The program context environment is: XXXActivity.this;

name : database name;

factory: cursor factory, the default is null, that is, the default factory is used;

version database version number

onCreate(SQLiteDatabase db) Called when the database is created
onUpgrade(SQLiteDatabase db,int oldVersion , int newVersion) Called when the version is updated
getReadableDatabase() Create or open a read-only database
getWritableDatabase() Create or open a read-write database

First create the database class

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class SqliteDBHelper extends SQLiteOpenHelper {

    // Step 1: Set constant parameters
    private static final String DATABASE_NAME = "diary_db";
    private static final int VERSION = 1;
    private static final String TABLE_NAME = "diary";

    // Step 2: Overload the constructor
    public SqliteDBHelper(Context context) {
        super(context, DATABASE_NAME, null, VERSION);
    }

    /*
     * Parameter introduction: context The program context environment is: XXXActivity.this
     * name database name
     * factory receives data, usually null
     * version database version number
     */
    public SqliteDBHelper(Context context, String name, CursorFactory factory,
            int version) {
        super(context, name, factory, version);
    }
    //When the database is created for the first time, onCreate() will be called
    @Override
    public void onCreate(SQLiteDatabase db) {
        // Step 3: Creation of database table
        String strSQL = "create table "
                + TABLE_NAME
                + "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)";
        //Step 4: Use the parameter db to create an object
        db.execSQL(strSQL);
    }
    //When the database version changes, onUpgrade() will be called
    @Override
    public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {

    }
}

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.

 We need a Dao to encapsulate all our business methods. The code is as follows:

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.chinasoft.dbhelper.SqliteDBHelper;

public class DiaryDao {

    private SqliteDBHelper sqliteDBHelper;
    private SQLiteDatabase db;

    // Override constructor
    public DiaryDao(Context context) {
        this.sqliteDBHelper = new SqliteDBHelper(context);
        db = sqliteDBHelper.getWritableDatabase();
    }

    // read operation
    public String execQuery(final String strSQL) {
        try {
            System.out.println("strSQL>" + strSQL);
            // Cursor is equivalent to ResultSet in JDBC
            Cursor cursor = db.rawQuery(strSQL, null);
            // Always let the cursor point to the first row of the database table
            cursor.moveToFirst();
            // Define a StringBuffer object for dynamically concatenating strings
            StringBuffer sb = new StringBuffer();
            // loop cursor, if not the last record
            while (!cursor.isAfterLast()) {
                sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"
                        + cursor.getString(2) + "/" + cursor.getString(3) + "/"
                        + cursor.getString(4)+"#");
                //cursor cursor movement
                cursor.moveToNext();
            }
            db.close();
            return sb.deleteCharAt(sb.length()-1).toString();
        } catch (RuntimeException e) {
            e.printStackTrace ();
            return null;
        }

    }

    // write operation
    public boolean execOther(final String strSQL) {
        db.beginTransaction(); //Start transaction
        try {
            System.out.println("strSQL" + strSQL);
            db.execSQL(strSQL);
            db.setTransactionSuccessful(); //Set the transaction to complete successfully
            db.close();
            return true;
        } catch (RuntimeException e) {
            e.printStackTrace ();
            return false;
        }finally {  
            db.endTransaction(); //end transaction  
        }  

    }
}

 We instantiate sqliteDBHelper in the Dao construction method and obtain a SQLiteDatabase object as the database instance of the entire application; when adding, deleting or modifying information, we use transaction processing to ensure data integrity; finally, we must pay attention to release database resources db.close() , 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 there is a database instance and it 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;  
    // 如果mDatabase不为空则加锁 阻止其他的操作  
    if (mDatabase != null)  
        mDatabase.lock();  
    try {  
        mIsInitializing = true;  
        if (mName == null) {  
            db = SQLiteDatabase.create(null);  
        } else {  
            // 打开或创建数据库  
            db = mContext.openOrCreateDatabase(mName, 0, mFactory);  
        }  
        // 获取数据库版本(如果刚创建的数据库,版本为0)  
        int version = db.getVersion();  
        // 比较版本(我们代码中的版本mNewVersion为1)  
        if (version != mNewVersion) {  
            db.beginTransaction();// 开始事务  
            try {  
                if (version == 0) {  
                    // 执行我们的onCreate方法  
                    onCreate(db);  
                } else {  
                    // 如果我们应用升级了mNewVersion为2,而原版本为1则执行onUpgrade方法  
                    onUpgrade(db, version, mNewVersion);  
                }  
                db.setVersion(mNewVersion);// 设置最新版本  
                db.setTransactionSuccessful();// 设置事务成功  
            } finally {  
                db.endTransaction();// 结束事务  
            }  
        }  
  
        onOpen(db);  
        success = true;  
        return db;// 返回可读写模式的数据库实例  
    } finally {  
        mIsInitializing = false;  
        if (success) {  
            // 打开成功  
            if (mDatabase != null) {  
                // 如果mDatabase有值则先关闭  
                try {  
                    mDatabase.close();  
                } catch (Exception e) {  
                }  
                mDatabase.unlock();// 解锁  
            }  
            mDatabase = db;// 赋值给mDatabase  
        } else {  
            // 打开失败的情况:解锁、关闭  
            if (mDatabase != null)  
                mDatabase.unlock();  
            if (db != null)  
                db.close();  
        }  
    }  
}

大家可以看到,几个关键步骤是,首先判断mDatabase如果不为空已打开并不是只读模式则直接返回,否则如果mDatabase不为空则加锁,然后开始打开或创建数据库,比较版本,根据版本号来调用相应的方法,为数据库设置新版本号,最后释放旧的不为空的mDatabase并解锁,把新打开的数据库实例赋予mDatabase,并返回最新实例。

看完上面的过程之后,大家或许就清楚了许多,如果不是在遇到磁盘空间已满等情况,getReadableDatabase()一般都会返回和getWritableDatabase()一样的数据库实例,所以我们在DBManager构造方法中使用getWritableDatabase()获取整个应用所使用的数据库实例是可行的。当然如果你真的担心这种情况会发生,那么你可以先用getWritableDatabase()获取数据实例,如果遇到异常,再试图用getReadableDatabase()获取实例,当然这个时候你获取的实例只能读不能写了

最后,让我们看一下如何使用这些数据操作方法来显示数据,界面核心逻辑代码: 

public class SQLiteActivity extends Activity {

    public DiaryDao diaryDao;

    //因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0, mFactory);  
    //所以要确保context已初始化,我们可以把实例化Dao的步骤放在Activity的onCreate里
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        diaryDao = new DiaryDao(SQLiteActivity.this);
        initDatabase();
    }

    class ViewOcl implements View.OnClickListener {

        @Override
        public void onClick(View v) {

            String strSQL;
            boolean flag;
            String message;
            switch (v.getId()) {
            case R.id.btnAdd:
                String title = txtTitle.getText().toString().trim();
                String weather = txtWeather.getText().toString().trim();;
                String context = txtContext.getText().toString().trim();;
                String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
                        .format(new Date());
                // 动态组件SQL语句
                strSQL = "insert into diary values(null,'" + title + "','"
                        + weather + "','" + context + "','" + publish + "')";
                flag = diaryDao.execOther(strSQL);
                //返回信息
                message = flag?"添加成功":"添加失败";
                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                break;
            case R.id.btnDelete:
                strSQL = "delete from diary where tid = 1";
                flag = diaryDao.execOther(strSQL);
                //返回信息
                message = flag?"删除成功":"删除失败";
                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                break;
            case R.id.btnQuery:
                strSQL = "select * from diary order by publish desc";
                String data = diaryDao.execQuery(strSQL);
                Toast.makeText(getApplicationContext(), data, Toast.LENGTH_LONG).show();
                break;
            case R.id.btnUpdate:
                strSQL = "update diary set title = '测试标题1-1' where tid = 1";
                flag = diaryDao.execOther(strSQL);
                //返回信息
                message = flag?"更新成功":"更新失败";
                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                break;
            }
        }
    }

    private void initDatabase() {
        // 创建数据库对象
        SqliteDBHelper sqliteDBHelper = new SqliteDBHelper(SQLiteActivity.this);
        sqliteDBHelper.getWritableDatabase();
        System.out.println("数据库创建成功");
    }
}

 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326975784&siteId=291194637