在操作Orm数据库sqlite时, 当存在多线程/多进程并行写时报错。 支持同一时刻多个读, 但同一时刻只能有一个写(不能有读);  简单记: 读写、写写是互斥的, 读读是可以并发的。
      因为多线程、多进程可以通过数据库名称操作同一个数据库, 建议只在一个进程中实例单例访问同一个数据库, 从而实现互斥判断逻辑;

      什么是写?  增加insert、删除delete、更新update和数据库表结构变化(例如修改已存在表的字段)
      什么是读? 只有一个query

      数据库存放在手机的  /data/data/包名/databases 中, 例如贝壳在 /data/data/com.lianjia.beike/databases 。
     推荐使用try/catch/finally实现资源访问、回收;

     使用事务时必须保证beginTransaction和endTransaction函数成对执行!!!

要点:单进程、单例、读写锁、数据库名称全局唯一

SqliteDatabase的close方法使得引用计数减1, 当引用计数为0时释放资源, 可根据业务需要评估是否执行。 


推荐数据库写法:

class ***Helper extends SQLiteOpenHelper {
  private static final String DB_NAME = "***.db";  //数据名称, 一般由英文和下划线组成
  private static final int DB_VERSION = 1;
 
  //单例, 避免创建多个SQLiteOpenHelper实例
  private static volatile ***Helper sHelper;
  private final ReentrantReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();   //读写锁支持读读并发、读写/写写互斥
  private final Lock mReadLock = mReadWriteLock.readLock();   //读锁
  private final Lock mWriteLock = mReadWriteLock.writeLock(); //写锁
 
  public static ***Helper getInstance(@NonNull Context context) {
    if (mDbHelper == null) {
      synchronized (***Helper.class) {
        if (sDbHelper == null) {
          sDbHelper = new *Helper(context);
        }
      }
    }
    return mDbHelper;
  }
 
  private ***Helper(Context context) {
    if (sHelper != null) {
      throw new IllegalArgumentException("不支持反射实例化,整个进程只能有一个实例");
    }
  }
 
  /**
   * 使用写锁和getWritableDatabase, 也可以使用tryLock并设置超时时间, lock是一直等待
  **/
  public int delete/insert/update(***) {
    ...
    SQLiteDatabase database = null;
    try {
      mWriteLock.lock(); //获取写锁
      database = getWritableDatabase();
      count = database.delete(***);  //database.update , database.insert等
    } catch (Throwable e){
       
    }finally {
      try {
        mWriteLock.unlock(); //释放写锁
      } catch (Exception e) {
        e.printStackTrace();
      }
 
      if (database != null) {
        //实际上是引用计数减1, 当引用计数为0时才执行真正的释放逻辑
        database.close();
      }
    }
    ...
  }
 
  /**
   * 使用读锁和getReadableDatabase(), 也可以改造为tryLock并设置超时时间, lock会一直等待
   **/
  public List query(***) {
    Cursor cursor = null;
    SQLiteDatabase database = null;
    try {
      mReadLock.lock();
      database = getReadableDatabase();
      cursor = database.query(...);
      //遍历cursor填充数据
    } catch (Throwable e) {
   
    } finally {
      if (cursor != null) {
        cursor.close();
      }
      if (database != null) {
        database.close(); //非必要操作, 引用计数减1. 引用计数为0时释放资源。
      }
      mReadLock.unlock();
    }
    return 数据;
  }

  /**
beginTransaction和endTransaction必须成对执行
   * endTransaction函数注释:This method must be called exactly once for each call to {@link #beginTransaction}.
  */
 public void query(***) {
 Cursor cursor = null;
    SQLiteDatabase database = null;
    try {
      mReadLock.lock();
      database = getReadableDatabase();
      db.beginTransaction(); 

      cursor = database.query(...); //数据库查询操作

      db.setTransactionSuccessful();  //更新状态
    } catch (Throwable e) {
   
    } finally {
      if (cursor != null) {
        cursor.close();
      }
      if (database != null) {
        if (db.inTransaction()) { //当前有等待操作则进入函数体, 即如果执行beginTransaction函数成功则条件为真。 如果beginTransaction语句后面出现崩溃则条件为false
          db.endTransaction();   //如果执行了beginTransaction, 则必须保证执行endTransaction.
        }
        database.close(); //非必要操作, 引用计数减1. 引用计数为0时释放资源。
      }
      mReadLock.unlock();
    }
    return 数据;
}


 

更多推荐