文档章节

SQLite数据库相关(三) SQLiteOpenHelper类

k
 kim366
发布于 2016/05/13 19:36
字数 1815
阅读 4
收藏 0

        简介

        前面两篇简单介绍SQLite数据库,包括SQLiteDatabase,实际项目中应该很少使用SQLiteDatabase的方法来打开数据库,而是继承SQLiteOpenHelper开发子类,并通过该子类的getReadableDatabase(),getWritableDatabase()方法打开数据库。

        SQLite是Android提供的一个管理数据库的工具类(工具类这个概念很重要),可用于管理数据库的创建和版本更新。一般扩展他的onCreate(SQLiteDatabase db)和onUpgrate(SQLiteDatabase db, int oldVersion, int newVersion)。这是两个抽象方法。


        构造函数和常用方法

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

SQLiteOpenHelper( Context context,  String name,  SQLiteDatabase.CursorFactory factory, int version,  DatabaseErrorHandler errorHandler)

synchronized void close()  关闭当前SQLiteOpenHelper对象下的所有数据库对象
String getDatabaseName() 获取构造函数中指定的数据库名对应的数据库对象 
SQLiteDatabase getReadableDatabase() 打开或创建一个只读的数据库对象
SQLiteDatabase getWritableDatabase() 打开一个可读可写的数据库对象
abstract void onCreate( SQLiteDatabase db) 当数据库第一次创建时调用
void onDowngrade( SQLiteDatabase db, int oldVersion, int newVersion) 当数据库降级时调用
void onOpen( SQLiteDatabase db) 数据库打开时调用
abstract void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion) 当数据库升级时调用

       使用SQLiteOpenHelper时,需要重写oncreate和onUpgrade两个方法

       onCreate(SQLiteDatabae db); 用于初次使用软件时创建数据库表,调用getReadableDatabase()或者getWritableDatabase()生成数据库时,如果数据库不存在,则系统会自动生成一个数据库,接着调用onCreate()方法,onCreate()方法在初次生成数据库时才会被调用,重写onCreate()时可以生成数据库表结构并添加一些应用使用到的初始化数据。

       onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); 用于更新软件时更新数据库表结构,方法在数据库的版本发生变化时会被调用,oldVersion和newVersion分别表示旧版本和新版本,当程序创建SQLiteOpenHelper对象时,必须指定一个version参数,该参数就决定了所使用的数据库的版本,也就是说,数据库的版本由程序员控制,只要某次创建SQLiteOpenHelper对象时指定的数据库版本号高于之前的版本号,系统会自动触发onUpgrade(SQLiteDatabae db, int oldVersion, int newVersion)方法,程序就可以根据原版本号和目标版本号进行判断,既可根据版本号进行必要的表结构更新。

        实际上,应用程序在升级表结构的时候完全可能因为现有的数据造成升级失败。后面会专门写一篇分析数据库的升级问题。 

 

       SQLiteOpenHelper源码学习

       先贴上简化后的源码,可以通过原注释和后面的总结一起理解

public abstract class SQLiteOpenHelper {
    /**
     * 创建一个SQLite数据库的帮助类对象用于打开和管理一个数据库
     * 这个方法通常返回非常快速,这个数据库对象并不会在这里直接创建,
     * 知道调用了getWritableDatabase或者getReadableDatabase方法
     */
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
        this(context, name, factory, version, new DefaultDatabaseErrorHandler());
    }

    /**
     * 和上面的构造函数相同,不过这里多传了一个DatabaseErrorHandler对象,
     * 这个对象用于处理数据库的异常
     */
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
            DatabaseErrorHandler errorHandler) {
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
        if (errorHandler == null) {
            throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
        }

        mContext = context;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
        mErrorHandler = errorHandler;
    }

 
    public String getDatabaseName() {
        return mName;
    }

    /**
     * 1. 创建或打开一个可读可写的数据库,如果是第一次调用,那么数据库会被打开,oncreate,onUpgrade,open都可能调用。
     * 2. 如果打开成功,那么数据库会被缓存,所以你可以在任何时候对其进行读写。
     * 3. 如果不需要再使用这个数据库,那么确保调用close方法将其关闭。
     * 4. 如果发生错误,比如说磁盘已满,或者权限不允许,则正常调用可能会失败,修复问题后可以再次调用。
     * 5. 数据库升级可能会耗费较长时间,所以不应在应用的主线程中调用这个方法,包括ContentProvider。
     */
    public synchronized SQLiteDatabase getWritableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling mDatabase.close()
                mDatabase = null;
            } else if (!mDatabase.isReadOnly()) {
                return mDatabase;  // The database is already open for business
            }
        }

        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 != null) mDatabase.lock();
        try {
            mIsInitializing = true;
            if (mName == null) {
                db = SQLiteDatabase.create(null);
            } else {
                db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
            }

            int version = db.getVersion();
            if (version != mNewVersion) {
                db.beginTransaction();
                try {
                    if (version == 0) {
                        onCreate(db);
                    } else {
                        if (version > mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            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) {
                    try { mDatabase.close(); } catch (Exception e) { }
                    mDatabase.unlock();
                }
                mDatabase = db;
            } else {
                if (mDatabase != null) mDatabase.unlock();
                if (db != null) db.close();
            }
        }
    }

    /**
     * 创建或者打开一个数据库,这个数据库对象和调用getWritableDatabase打开的数据库是同一个,除非发生某些意外情况,
     * 在这种情况下,一个只读的数据库对象会被返回,如果问题修复,那么下次调用getWritableDatabase将会成功
     * 这时只读的数据库会被关闭,可读可写的数据库会被返回。
     * 其他的和getWritableDatabase()方法一样。 
     */
    public synchronized SQLiteDatabase getReadableDatabase() {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // darn! the user closed the database by calling mDatabase.close()
                mDatabase = null;
            } else {
                return mDatabase;  // The database is already open for business
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getReadableDatabase called recursively");
        }

        try {
            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);
        }

        SQLiteDatabase db = null;
        try {
            mIsInitializing = true;
            String path = mContext.getDatabasePath(mName).getPath();
            db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY,
                    mErrorHandler);
            if (db.getVersion() != mNewVersion) {
                throw new SQLiteException("Can't upgrade read-only database from version " +
                        db.getVersion() + " to " + mNewVersion + ": " + path); /** * database. 调用已经打开的数据库,实现类在更新表之前应该检查数据库是否是只读的。 */
            }

            onOpen(db);
            Log.w(TAG, "Opened " + mName + " in read-only mode");
            mDatabase = db;
            return mDatabase;
        } finally {
            mIsInitializing = false;
            if (db != null && db != mDatabase) db.close();
        }
    }


    public synchronized void close() {
        if (mIsInitializing) throw new IllegalStateException("Closed during initialization");

        if (mDatabase != null && mDatabase.isOpen()) {
            mDatabase.close();
            mDatabase = null;
        }
    }


    public abstract void onCreate(SQLiteDatabase db);


    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);


    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version " +
                oldVersion + " to " + newVersion);
    }


    public void onOpen(SQLiteDatabase db) {}
}

        从上面的源代码可知。

       1.  从始至中,SQLiteOpenHelper中只持有一个数据库对象,并且一般情况下,通过getWritableDatabase()和getReadableDatabase()创建或打开时获取到的是同一个SQLiteDatabase对象。

       2.  如果当前数据库是只读的,那么再次调用getWritableDatabase()获取可读可写数据库对象时会重新打开现有对象。

       3.  如果当前不存在数据库对象,并且初始化时没有指定数据库名称,也会创建一个默认数据库。

       4.  如果数据库对象不存在,但是指定了数据库的名称,那么系统会先查看是否有指定数据库,如果有则直接打开,没有则先创建再打开数据库。

       5.  数据库或降级升级时,会根据版本号进行判断,然后把升级和降级的过程作为一个事务进行操作,确保数据安全。

       6.  创建数据库对象的默认版本号为0。

本文转载自:http://blog.csdn.net/oyangyujun/article/details/40711227

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
不会点SQLite,都不好意思说自己是开发的

一、为什么要会点SQLite? SQLite作为一款轻量级的关系型数据库,占用的资源特别少,所以其应用场景也是特别的多。在移动开发中,我们经常会有将数据存储在本地的需求,此时SQLite将是我们最...

silencezwm
07/03
0
0
详解Android数据存储技术

前言 学习Android相关知识,数据存储是其中的重点之一,如果不了解数据,那么让你跟一款没有数据的应用玩,你能玩多久呢?答案是这和没有手机几乎是差不多的。我们聊QQ,聊微信,看新闻,刷朋...

达叔小生
08/04
0
0
ANDROID开发之SQLite详解

SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储、管理、维护等各方面都相当出色,功能也非常的强大。SQLite具备下列特点: 1.轻量级 使用 SQLite 只需要带一个动态库...

天下杰论
2013/03/08
0
0
Android开发之SQLite技术详解

  【IT168技术】Google为Andriod的较大的数据处理提供了SQLite,他在数据存储、管理、维护等各方面都相当出色,功能也非常的强大。SQLite具备下列特点:   1.轻量级   使用 SQLite 只需...

庸人谷
2012/11/28
0
0
Android创建和使用数据库详细指南(1)

数据库支持每个应用程序无论大小的生命线,除非你的应用程序只处理简单的数据,那么就需要一个数据库系统存储你的结构化数据,Android使用SQLite数据库,它是一个开源的、支持多操作系统的S...

冯京宝
2011/01/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

gson1.7.1线程并发导致空指针问题

java.lang.NullPointerExceptionat com.google.gson.FieldAttributes.getAnnotationFromArray(FieldAttributes.java:231)at com.google.gson.FieldAttributes.getAnnotation(FieldAttribut......

东风125
37分钟前
1
0
以太坊RPC接口使用

以太坊RPC接口文档: https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_clientversion 使用方式: 比如我要调用某个合约的balanceOf(address _owner)方法。 因为没有改变合约的状态,所以...

王坤charlie
今天
2
0
C#下的一个好用的日历库(sxtwl_cpp),支持农历转公历,和公历转农历等功能

sxtwl_cpp是寿星天文历的C++版本实现。支持多种语言的绑定 代码首页 懒人包 懒人包使用方法 1、右链工程中的引用-》添加引用-》浏览-》选中dotnet目录下的sxtwl.net.dll 2、生成解决方案-》找...

元谷
今天
1
0
C++基础知识

链接:https://zhuanlan.zhihu.com/p/38399566 本文主要提一下以下三个区别: 引用必须初始化,而指针可以不初始化。 我们在定义一个引用的时候必须为其指定一个初始值,但是指针却不需要。 ...

悲催的古灵武士
今天
1
0
Oracle备份脚本,保留10天数据

@echo off echo 删除10天前的备分文件和日志forfiles /p "D:\oracleback\backfile" /m *.dmp /d -10 /c "cmd /c del @path" forfiles /p "D:\oracleback\backfile" /m *.log /d -10......

lyle_luo
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部