文档章节

SQLite数据库相关(三) SQLiteOpenHelper类

k
 kim366
发布于 2016/05/13 19:36
字数 1815
阅读 6
收藏 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 开发中使用 SQLite 数据库

SQLite 介绍 SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能。此外它还是开源的,任何人都可以使用它。许多开源项目((Mozilla, PHP, Python)都...

红薯
2010/08/22
67.1K
32
Kotlin入门(26)数据库ManagedSQLiteOpenHelper

共享参数毕竟只能存储简单的键值对数据,如果需要存取更复杂的关系型数据,就要用到数据库SQLite了。尽管SQLite只是手机上的轻量级数据库,但它麻雀虽小、五脏俱全,与Oracle一样存在数据库的...

aqi00
10/19
0
0
Android 数据存储与读取:SQLite

在Android平台上,集成了一个嵌入式关系型数据库—SQLite。如果你想要开发 Android 应用程序,一定需要在 Android 上存储数据,使用SQLite 数据库是一种非常好的选择。 下面介绍的基本使用:...

长平狐
2012/10/08
362
0
详解Android数据存储技术

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

达叔小生
08/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【Flutter教程】从零构建电商应用(一)

在这个系列中,我们将学习如何使用google的移动开发框架flutter创建一个电商应用。本文是flutter框架系列教程的第一部分,将学习如何安装Flutter开发环境并创建第一个Flutter应用,并学习Flu...

笔阁
23分钟前
5
0
什么是以太坊DAO?(三)

Decentralized Autonomous Organization,简称DAO,以太坊中重要的概念。一般翻译为去中心化的自治组织。 投票支付合约的所有费用和行动需要时间,并要求用户始终保持活跃,知情和专注。另一...

geek12345
25分钟前
2
0
一个本科学生对Linux的认知

一个本科学生对Linux的认知 我是一名大三的普通一本大学的软件工程的一名学生,学校开设了一些关于系统开发的课程,纸上得来终觉浅,学校的课程课时较短,想要在56个课时之内学会一些公司需要...

linuxCool
今天
3
0
CentOS 安装Tomcat

Tomcat 介绍 Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 Java 程序写的网站用tomcat+jdk来运...

野雪球
今天
1
0
OSChina 周四乱弹 —— 每天都迟到是种什么样的体验

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @开源中国首席机器人 :《Too Good At Goodbyes (Acoustic) - Sam Smith - 单曲》 《Too Good At Goodbyes (Acoustic) - Sam Smith - 单曲》 ...

小小编辑
今天
982
14

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部