Android数据存储(七) SQLite使用注意和SQL语句

来源:互联网 发布:excel数据分析回归 编辑:程序博客网 时间:2024/06/14 23:02

1、数据类型的问题

SQLite内部只支持NULL、INTEGER、REAL(浮点数)、TEXT(文本)和BLOB(大二进制对象)这5种数据类型,但实际上SQLite完全可以接受varchar(n)、char(n)、decimal(p,s)等数据类型,只不过SQLite会在原酸或保存时将他们转换为上面5种数据类型中相应的类型。

除此之外,SQLite还有一个特点:它允许把各种类型的数据保存到任何类型字段中,开发者可以不用关心声明该字段所使用的数据类型。例如程序可以把字符串类型的值存入到INTEGER类型的字段中,也可以把数值数据类型的值存入到布尔类型的字段中。。。。但是有一中情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数,当向这种字段保存除整数意外的其他类型的数据时,SQLite会产生错误。

并且,可以向字符串字段中插入任意长度的字符串,即使声明varchar(20)加以限制。


2、数据库何时关闭问题

我们使用SQLiteOpenHelper获取一个SQLiteDatabase数据库对象,然后就可以去使用这个数据库了。那么这个数据库何时关闭呢。一般我们不需要操作一次数据库,就去关闭它,因为这样很耗资源。毕竟这不是像J2EE上面的数据库,J2EE服务器上的数据库会根据访问的用户生成许多数据库连接,所以需要关闭。但是在Android手机上,只有我们当前的应用程序再回有一个数据库的连接,当然不需要用一次关闭一次。

所以,我们只需要完成了所有的数据库操作,或者退出程序的时候,选择一个恰当的实际关闭。


3、SQLiteDatabase对象的获取问题。

我们获取数据库实例时使用了getWritableDatabase()方法,也许朋友们会有疑问,在getWritableDatabase()和getReadableDatabase()中,你为什么选择前者作为整个应用的数据库实例呢?在这里我想和大家着重分析一下这一点。

我们来看一下SQLiteOpenHelper中的getReadableDatabase()方法:

    public synchronized SQLiteDatabase getReadableDatabase() {          if (mDatabase != null && mDatabase.isOpen()) {              // 如果发现mDatabase不为空并且已经打开则直接返回              return mDatabase;          }                if (mIsInitializing) {              // 如果正在初始化则抛出异常              throw new IllegalStateException("getReadableDatabase called recursively");          }                // 开始实例化数据库mDatabase                try {              // 注意这里是调用了getWritableDatabase()方法              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);              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;// 为mDatabase指定新打开的数据库              return mDatabase;// 返回打开的数据库          } finally {              mIsInitializing = false;              if (db != null && db != mDatabase)                  db.close();          }      }  

在getReadableDatabase()方法中,首先判断是否已存在数据库实例并且是打开状态,如果是,则直接返回该实例,否则试图获取一个可读写模式的数据库实例,如果遇到磁盘空间已满等情况获取失败的话,再以只读模式打开数据库,获取数据库实例并返回,然后为mDatabase赋值为最新打开的数据库实例。既然有可能调用到getWritableDatabase()方法,我们就要看一下了:

    public synchronized SQLiteDatabase getWritableDatabase() {          if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {              // 如果mDatabase不为空已打开并且不是只读模式 则返回该实例              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()获取实例,当然这个时候你获取的实例只能读不能写了。

并且,同一个数据库只会返回一个SQLiteDatabase数据库实例。


4、在Android中对数据库操作语句和方法

SQLite中sql语句和MySql非常类似,完全可以去参照MySql的sql语句。

原创粉丝点击