Android - SQLite

一 、数据库的创建

事实上,android的SQLite技术主要就是两个东西
一个是 用create table语句创建出来的那张(些)表 ,里边存着我们需要的数据(先不管它到底是怎样存储数据的),另一个就是 封装了对这张(些)表进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)(这些操作简称为CRUD)等操作的方法的SQLiteDatabase类  因为创建表的语句也是由SQLiteDatabase类的实例来执行的,所以我们先来关注SQLiteDatabase类及其实例的创建.
 
Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。
首先看一下SQLiteDatabase类的概要:
[java]  view plain  copy
  1. /** 
  2.  * Exposes methods to manage a SQLite database. 
  3.  * 
  4.  * SQLiteDatabase has methods to create, delete, execute SQL commands, and 
  5.  * perform other common database management tasks. 
  6.  *  
  7.  * See the Notepad sample application in the SDK for an example of creating 
  8.  * and managing a database. 
  9.  *  
  10.  * Database names must be unique within an application, not across all applications. 
  11.  *  
  12.  */  
  13. //提供管理一个SQLite database的方法,包括创建、删除、执行SQL语句和其他的任务  
  14. public final class SQLiteDatabase extends SQLiteClosable { }  
在SQLiteDatabase.java中,我们可以看到构造函数是私有的:
[java]  view plain  copy
  1. private SQLiteDatabase(String path, int openFlags, CursorFactory cursorFactory,  
  2.         DatabaseErrorHandler errorHandler) {  
  3.     mCursorFactory = cursorFactory;  
  4.     mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();  
  5.     mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);  
  6. }  
这意味着我们不能直接使用new的方式来创建SQLiteDatabase对象,那么在SQLiteDatabase类中就应该有一个供我们获取它的对象的方法,该方法就是
public static SQLiteDatabase openDatabase( String pat h, ... ...)
该方法的注释如下:
[java]  view plain  copy
  1. /** 
  2.  * Open the database according to the flags {@link #OPEN_READWRITE} 
  3.  * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}. 
  4.  */  
  5. //按照接收的flags参数打开一个数据库,这些参数包括OPEN_READWRITE、OPEN_READONLY等  
  6. public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,  
  7.         DatabaseErrorHandler errorHandler) {  
  8.     SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);  
  9.     db.open();  
  10.     return db;  
  11. }  
在该方法中通过new SQLiteDatabase获得一个SQLiteDatabase对象并返回,并且在SQLiteDatabase.java中我们可以看到,
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory)
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,DatabaseErrorHandler errorHandler)
public static SQLiteDatabase create(CursorFactory factory)
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags)
这些方法最终都是在调用
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,DatabaseErrorHandler errorHandler)方法来创建SQLiteDatabase实例.

而我们使用数据库的常见写法为:
[java]  view plain  copy
  1. public class MySQLiteHelper extends SQLiteOpenHelper{  
  2.     ... ...  
  3.     @Override  
  4.     public void onCreate(SQLiteDatabase db) {  
  5.     db.execSQL("create table... ...}  
  6.   
  7.     @Override  
  8.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  9.         ... ...   
  10.     }  
  11. }  
  12. ... ...  
  13. public class myActivity extends Activity {  
  14.     ... ...  
  15.     myHelper = new MySQLiteHelper(this"my.db"null1);  
  16.     SQLiteDatabase db = myHelper.getWritableDatabase();  
  17.     db.execSQL("insert ... ...  
  18. }  
于是问题就来了,既然我们最终会通过调用openDatabase()方法来获得一个SQLiteDatabase 实例,那么,
1、getWritableDatabase和getReadableDatabase方法最终是不是也会去调用openDatabase方法呢?
2、SQLiteOpenHelper类存在的目的是什么?

直接来看 SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法:
[java]  view plain  copy
  1. public SQLiteDatabase getWritableDatabase() {  
  2.     synchronized (this) {  
  3.         return getDatabaseLocked(true);  
  4.     }  
  5. }  
  6.   
  7. public SQLiteDatabase getReadableDatabase() {  
  8.     synchronized (this) {  
  9.         return getDatabaseLocked(false);  
  10.     }  
  11. }  
两个方法里边都调用了getDatabaseLocked方法,只是传入的参数不同,一个为true,一个为false,该方法大致逻辑为:
[java]  view plain  copy
  1. private SQLiteDatabase getDatabaseLocked(boolean writable) {  
  2.     //mDatabase是SQLiteOpenHelper的成员变量private SQLiteDatabase mDatabase  
  3.     if (mDatabase != null) {  
  4.         //在mDatabase != null的情况下,先判断mDatabase是否为open状态  
  5.         if (!mDatabase.isOpen()) {  
  6.             // Darn!  The user closed the database by calling mDatabase.close().  
  7.             //如果处于关闭状态,将mDatabase 置为null  
  8.             mDatabase = null;  
  9.         } else if (!writable || !mDatabase.isReadOnly()) {  
  10.             // The database is already open for business.  
  11.             //如果mDatabase为open状态并且是可读或者可写的,则将它返回,不会重新创建  
  12.             return mDatabase;  
  13.         }  
  14.     }  
  15.   
  16.     //在mDatabase != null但是mDatabase处于关闭状态 和 mDatabase == null这两种情况下,  
  17.     //执行以下逻辑:  
  18.     if (mIsInitializing) {  
  19.         //如果正在执行初始化操作,则抛出异常  
  20.         throw new IllegalStateException("getDatabase called recursively");  
  21.     }  
  22.       
  23.     //先将mDatabase赋给一个临时定义的SQLiteDatabase db  
  24.     SQLiteDatabase db = mDatabase;  
  25.     try {  
  26.         //更改db的初始化操作对应的标示mIsInitializing 的值为true  
  27.         mIsInitializing = true;  
  28.         if (db != null) {  
  29.             //如果db != null并且db处于关闭状态,那么  
  30.             if (writable && db.isReadOnly()) {  
  31.                  //重新以读写的方式打开db  
  32.                 db.reopenReadWrite();  
  33.             }  
  34.         } else if (mName == null) {  
  35.             //mName 是SQLiteOpenHelper的成员变量 private final String mName;  
  36.             //在我们执行myHelper = new MySQLiteHelper()时,在SQLiteOpenHelper的  
  37.             //构造函数中完成赋值,如果db == null并且mName == null 则  
  38.             //Create a memory backed SQLite database  
  39.             db = SQLiteDatabase.create(null);  
  40.         } else {  
  41.             //如果db == null但是mName != null 则执行下边的逻辑:  
  42.             try {  
  43.                 if (DEBUG_STRICT_READONLY && !writable) {  
  44.                     //调用getReadableDatabase方法将会执行到这里,接着调用openDatabase方法  
  45.                     //并最终执行SQLiteDatabase 中的openDatabase()方法  
  46.                     final String path = mContext.getDatabasePath(mName).getPath();  
  47.                     db = SQLiteDatabase.openDatabase(path, mFactory,  
  48.                             SQLiteDatabase.OPEN_READONLY, mErrorHandler);  
  49.                 } else {  
  50.                     //调用getWritableDatabase方法将会执行到这里,调用openOrCreateDatabase方法  
  51.                     //并最终执行SQLiteDatabase 中的openDatabase()方法  
  52.                     db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?  
  53.                             Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,  
  54.                             mFactory, mErrorHandler);  
  55.                 }  
  56.             } catch (SQLiteException ex) {  
  57.                 if (writable) {  
  58.                     throw ex;  
  59.                 }  
  60.                 Log.e(TAG, "Couldn't open " + mName  
  61.                         + " for writing (will try read-only):", ex);  
  62.                 final String path = mContext.getDatabasePath(mName).getPath();  
  63.                 db = SQLiteDatabase.openDatabase(path, mFactory,  
  64.                         SQLiteDatabase.OPEN_READONLY, mErrorHandler);  
  65.             }  
  66.         }  
  67.         //该方法在onCreate,onUpgrade,onDowngrade,onOpen等方法之前被调用,  
  68.         //主要是用来配置数据库的链接(翻译可能不准确,但不影响主要逻辑的理解)  
  69.         onConfigure(db);  
  70.           
  71.         //到此为止,SQLiteDatabase的实例db的创建已经完成,  
  72.         //下边是关于version(版本升级和降级)的操作,来看一下具体逻辑:  
  73.         //获得db的版本:version  
  74.         final int version = db.getVersion();  
  75.         if (version != mNewVersion) {  
  76.             //如果version != mNewVersion,则执行这里的逻辑  
  77.             //mNewVersion是SQLiteOpenHelper的成员变量private final int mNewVersion;在我们  
  78.             //执行myHelper = new MySQLiteHelper()时,在SQLiteOpenHelper的构造函数中完成赋值  
  79.             if (db.isReadOnly()) {  
  80.                 //因为升级和降级需要对数据库进行写操作,所以如果db是只读的,那么就抛出异常  
  81.                 throw new SQLiteException("Can't upgrade read-only database from version " +  
  82.                         db.getVersion() + " to " + mNewVersion + ": " + mName);  
  83.             }  
  84.   
  85.             db.beginTransaction();  
  86.             try {  
  87.                 if (version == 0) {  
  88.                     //第一次创建的SQLiteDatabase实例,其version的值应该是0  
  89.                     //(没有深入看db.getVersion()的逻辑)  
  90.                     //如果version == 0的话就执行onCreate(db)方法:  
  91.                     onCreate(db);  
  92.                     //于是我们自定义的MySQLiteHelper 类中复写的onCreate()方法就被调用了,  
  93.                     //接着用已经创建出来的SQLiteDatabase实例db去执行创建表的语句  
  94.                 } else {  
  95.                     //如果version != 0,则执行下边的逻辑:  
  96.                     if (version > mNewVersion) {  
  97.                         //如果version > mNewVersion(其值为new MySQLiteHelper()时传入),则:  
  98.                         onDowngrade(db, version, mNewVersion);  
  99.                     } else {  
  100.                         //如果version < mNewVersion(其值为new MySQLiteHelper()时传入),则:  
  101.                         onUpgrade(db, version, mNewVersion);  
  102.                     }  
  103.                 }  
  104.                 //执行完再将mNewVersion的值赋给db  
  105.                 db.setVersion(mNewVersion);  
  106.                 db.setTransactionSuccessful();  
  107.             } finally {  
  108.                 db.endTransaction();  
  109.             }  
  110.         }  
  111.         //执行onOpen()方法  
  112.         onOpen(db);  
  113.   
  114.         if (db.isReadOnly()) {  
  115.             Log.w(TAG, "Opened " + mName + " in read-only mode");  
  116.         }  
  117.         //然后将db赋值给SQLiteOpenHelper的成员变量private SQLiteDatabase mDatabase  
  118.         //以便下次调用getDatabaseLocked()方法时,通过判断mDatabase 是否为null以及是否isOpen等  
  119.         //来确定怎么创建一个SQLiteDatabase实例  
  120.         mDatabase = db;  
  121.         //将db返回  
  122.         return db;  
  123.     } finally {  
  124.         //最后将mIsInitializing 的值设为false,表示SQLiteDatabase实例的初始化已经完成  
  125.         mIsInitializing = false;  
  126.         if (db != null && db != mDatabase) {  
  127.             db.close();  
  128.         }  
  129.     }  
  130. }  
上边第 91 行代码onCreate()方法的定义及注释 :
[java]  view plain  copy
  1. /** 
  2.  * Called when the database is created for the first time. This is where the 
  3.  * creation of tables and the initial population of the tables should happen. 
  4.  * 
  5.  * @param db The database. 
  6.  */  
  7. //该方法是在SQLiteDatabase第一次被创建的时候调用,表的创建和初始化操作在这里定义  
  8. public abstract void onCreate(SQLiteDatabase db);  

在分析了SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法的大致逻辑之后.我们在上文中提出的两个问题也就好回答了.
1、SQLiteOpenHelper类中的getWritableDatabase和getReadableDatabase方法最终会调用SQLiteDatabase类中的openDatabase方法
2、SQLiteOpenHelper类存在的目的主要是以下两个方面:

A、对SQLiteDatabase类中的openDatabase方法进行封装,优化SQLiteDatabase的实例的创建

现在来看一下SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法的注释吧:
getWritableDatabase():
[java]  view plain  copy
  1. /** 
  2.  * Create and/or open a database that will be used for reading and writing. 
  3.  * The first time this is called, the database will be opened and 
  4.  * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be 
  5.  * called. 
  6.  * 
  7.  * <p>Once opened successfully, the database is cached, so you can 
  8.  * call this method every time you need to write to the database. 
  9.  * (Make sure to call {@link #close} when you no longer need the database.) 
  10.  * Errors such as bad permissions or a full disk may cause this method 
  11.  * to fail, but future attempts may succeed if the problem is fixed.</p> 
  12.  * 
  13.  * <p class="caution">Database upgrade may take a long time, you 
  14.  * should not call this method from the application main thread, including 
  15.  * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. 
  16.  * 
  17.  * @throws SQLiteException if the database cannot be opened for writing 
  18.  * @return a read/write database object valid until {@link #close} is called 
  19.  */  
  20. //创建或打开一个可读写的database,第一次调用这个方法,database 将被打开和创建  
  21. //onUpgrade和onOpen方法会被调用  
  22. //一旦创建成功,database将被缓存,当你不再需要它时,记得调用close方法  
  23. //错误的权限或者磁盘空间已满都会导致这个方法失败,但当这些问题被修复后该方法也可能执行成功  
  24. //如果database不能被打开会抛出SQLiteException   
  25. //返回的可读写的database对象将会一直有效直到close方法被调用  
  26. public SQLiteDatabase getWritableDatabase() {  
  27.     synchronized (this) {  
  28.         return getDatabaseLocked(true);  
  29.     }  
  30. }  
getReadableDatabase():
[java]  view plain  copy
  1. /** 
  2.  * Create and/or open a database.  This will be the same object returned by 
  3.  * {@link #getWritableDatabase} unless some problem, such as a full disk, 
  4.  * requires the database to be opened read-only.  In that case, a read-only 
  5.  * database object will be returned.  If the problem is fixed, a future call 
  6.  * to {@link #getWritableDatabase} may succeed, in which case the read-only 
  7.  * database object will be closed and the read/write object will be returned 
  8.  * in the future. 
  9.  * 
  10.  * <p class="caution">Like {@link #getWritableDatabase}, this method may 
  11.  * take a long time to return, so you should not call it from the 
  12.  * application main thread, including from 
  13.  * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. 
  14.  * 
  15.  * @throws SQLiteException if the database cannot be opened 
  16.  * @return a database object valid until {@link #getWritableDatabase} 
  17.  *     or {@link #close} is called. 
  18.  */  
  19. //创建或打开一个database,这将和调用getWritableDatabase方法返回的对象是一样的  
  20. //除非出现某些问题,比如磁盘空间已满  
  21. //需要将此数据库以只读的方式打开,在这种情况下,一个只读的数据库对象将会被返回。  
  22. //如果这些问题被解决了,继续调用getWritableDatabase方法可能成功,  
  23. //这时,只读的database对象将被关闭而返回一个可读写的database对象  
  24. //如果database不能被打开会抛出SQLiteException   
  25. //返回的可读写的database对象将会一直有效直到close方法被调用  
  26. public SQLiteDatabase getReadableDatabase() {  
  27.     synchronized (this) {  
  28.         return getDatabaseLocked(false);  
  29.     }  
  30. }  
结合它们的注释和上边对getDatabaseLocked方法逻辑的分析,将会更有利于我们理解SQLiteOpenHelper对SQLiteDatabase的实例创建的优化

B、方便对数据库进行升级和降级
在开发中,数据库升级和降级是比较常见的操作,如果没有SQLiteOpenHelper,我们应该也会采用和SQLiteOpenHelper类似的方法用一个标示来判断是否要对数据库进行升级或降级,而SQLiteOpenHelper已经为我们做了这个工作.(参考getDatabaseLocked方法中的分析),让我们能够更加容易的管理数据库的版本。

再来看一下SQLiteOpenHelper类的注释:
[java]  view plain  copy
  1. /** 
  2.  * A helper class to manage database creation and version management. 
  3.  * 
  4.  * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and 
  5.  * optionally {@link #onOpen}, and this class takes care of opening the database 
  6.  * if it exists, creating it if it does not, and upgrading it as necessary. 
  7.  * Transactions are used to make sure the database is always in a sensible state. 
  8.  */  
  9. //这是一个管理数据库的创建和版本的帮助类,创建一个子类实现onCreate()、onUpdate()和可选的onOpen()  
  10. //方法此类负责打开一个数据库(如果存在)或者创建一个数据库(如果不存在),并在需要时进行更新... ...  
  11. //事务被用来确保数据库处于一个正确的状态  
  12. public abstract class SQLiteOpenHelper { }  
到此为止,SQLiteOpenHelper类的注释中的 "A helper class to manage database creation and version management."这句话也就好理解了

还需要关注一下SQLiteOpenHelper的构造函数:
[java]  view plain  copy
  1. /** 
  2.  * Create a helper object to create, open, and/or manage a database. 
  3.  * This method always returns very quickly.  The database is not actually 
  4.  * created or opened until one of {@link #getWritableDatabase} or 
  5.  * {@link #getReadableDatabase} is called. 
  6.  * 
  7.  * @param context to use to open or create the database 
  8.  * @param name of the database file, or null for an in-memory database 
  9.  * @param factory to use for creating cursor objects, or null for the default 
  10.  * @param version number of the database (starting at 1); if the database is older, 
  11.  *     {@link #onUpgrade} will be used to upgrade the database; if the database is 
  12.  *     newer, {@link #onDowngrade} will be used to downgrade the database 
  13.  */  
  14. //创建一个helper来创建、打开和管理数据库,数据库并不会真正的被创建直到getWritableDatabase()和  
  15. //getReadableDatabase()方法被调用.接收的四个参数分别是:  
  16. //context  上下文  
  17. //name     数据库的名称,或者为null for an in-memory database  
  18. //factory  一个用于创建游标的工厂,默认是null  
  19. //version  数据库版本号,从1开始,这将是调用onUpgrade更新或是调用onDowngrade降级数据库的根据  
  20. public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {  
  21.     this(context, name, factory, version, null);  
  22. }  
这个构造函数内部调用了下边的构造函数:
[java]  view plain  copy
  1. /** 
  2.  * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database 
  3.  * corruption, or null to use the default error handler. 
  4.  */  
  5. //和上边的构造函数不同的是多了一个DatabaseErrorHandler errorHandler,当sqlite报告了错误,  
  6. //由errorHandler来处理,如果errorHandler为null,就由默认处理  
  7. public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,  
  8.         DatabaseErrorHandler errorHandler) {  
  9.     if (version < 1throw new IllegalArgumentException("Version must be >= 1, was " + version);  
  10.   
  11.     mContext = context;  
  12.     mName = name;  
  13.     mFactory = factory;  
  14.     mNewVersion = version;  
  15.     mErrorHandler = errorHandler;  
  16. }  
这两个构造函数的注释再结合上边对getDatabaseLocked方法逻辑的分析,我们就更容易理解在创建SQLiteOpenHelper的子类对象时传入的name和version参数了.

以上我们对  SQLiteOpenHelper在 SQLiteDatabase的实例创建上的优化  和 对数据库表的升级和降级功能的封装 两方面做了详细的分析,下边我们来看SQLiteDatabase类定义的方法的使用,即数据库的操作.


二、数据库的操作


关于数据库的使用,有不少好文章可以参考,暂不详细写了

ContentValues类的注释:
[java]  view plain  copy
  1. /** 
  2.  * This class is used to store a set of values that the {@link ContentResolver} 
  3.  * can process. 
  4.  */  
  5. //该类用来存储键值对值  
  6. public final class ContentValues implements Parcelable { }  
查看ContentValues.java文件,可以发现,该类就一个主要的成员变量:private HashMap<String, Object> mValues;在ContentValues的各个构造函数中,主要的操作就是根据不同的需求对mValues进行初始化,并且在该类中定义了诸如put(String key, String value)和getAsString(String key)等方法:
put类方法,以put(String key, String value)为例:
[java]  view plain  copy
  1. /** 
  2.  * Adds a value to the set. 
  3.  * 
  4.  * @param key the name of the value to put 
  5.  * @param value the data for the value to put 
  6.  */  
  7. public void put(String key, String value) {  
  8.     mValues.put(key, value);  
  9. }  
get类方法,以getAsString(String key)为例:
[java]  view plain  copy
  1. /** 
  2.  * Gets a value and converts it to a String. 
  3.  * 
  4.  * @param key the value to get 
  5.  * @return the String for the value 
  6.  */  
  7. public String getAsString(String key) {  
  8.     Object value = mValues.get(key);  
  9.     return value != null ? value.toString() : null;  
  10. }  
另外,还有public int size()、public boolean containsKey(String key)、public void clear()、public Set<String> keySet()等,可以认为ContentValues类就是对HashMap的一个封装。

三、数据库的升级

未完待续 ... ...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值