SQLite数据库基本操作与分库(一)

2020-01-01  本文已影响0人  有没有口罩给我一个

SQliteDataBase相信大家都不陌生,目前有很多第三方框架,比如:GreenDao、DbFlow和Room等等,相信也都了解或者用过,但是我们今天不讲这些框架,目前我们项目中也用到数据库,但是根据目前框架有些需求并不能很好的符合我们的项目,所以就有了这系列文章,可能会有2篇关于数据库的文章,分别是:数据库基本操作与分库和数据库的升级,这些文章不会讲数据库的基本使用,我们将的是封装。

数据库设计三大范式:

数据库作用

如何设计数据库框架?

类图

base.png
base_sub.png

开始设计我们的数据库框架

public class BaseDao<T> implements IBaseDao<T> {
//持有数据库操作的引用
private SQLiteDatabase mSqLiteDatabase;
//数据表名
private String mTableName;
//操作数据所对应的类型,
private Class<T> mEntityClass;


//标识是否已经初始化
private boolean mIsInit = false;

//定义一个缓存Map key是数据库字段名 value是Class成员变量
//为什么需要做这个缓存,第一是为了性能优化,
// 第二是后面再查询,通过反射把Cursor设置给entity比较方便,
// 第三是数据库字段和entity进行映射
private HashMap<String, Field> mCacheMap_DbRecord_EntityField;


//数据库初始阿虎,需自动创建表
public boolean init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
    this.mSqLiteDatabase = sqLiteDatabase;
    this.mEntityClass = entityClass;
    if (!mIsInit) {
        //根据传入的class 进行数据表的创建
        DbTable dbTable = entityClass.getAnnotation(DbTable.class);
        if (dbTable != null && !TextUtils.isEmpty(dbTable.value())) {
            mTableName = dbTable.value().toLowerCase();
        } else {
            mTableName = entityClass.getSimpleName().toLowerCase();
        }

        //处理关键字 order
        if (mTableName.equalsIgnoreCase("order")) {
            mTableName = mTableName + "_info";
        }
        //数据库没有打开
        if (!sqLiteDatabase.isOpen()) {
            return mIsInit = false;
        }
        //得到创建数据表的sql 串
        String createTableSql = getCreateTableSql();
        Log.e("tag", createTableSql);
        sqLiteDatabase.execSQL(createTableSql);
        mCacheMap_DbRecord_EntityField = new HashMap<>();
        initCacheMap();
        return mIsInit = true;
    }
    return mIsInit = false;
}

}

BaseDao是我们把数据库的基本操作全部抽取出来,而一般Dao是对应数据库中的的一张表,首先我们先看看init方法,init方法是根据传入的EntityClass 进行数据表的创建,这个比较简单,在看看initCacheMap方法:

 private void initCacheMap() {
    //取得所有的列名
    String sql = "select * from " + mTableName + " limit 1,0";
    Cursor cursor = mSqLiteDatabase.rawQuery(sql, null);
    cursor.close();
    String[] columnNames = cursor.getColumnNames();

    //取得所有的成员变量
    Field[] fields = mEntityClass.getDeclaredFields();
    for (Field field : fields) field.setAccessible(true);

    for (String columnName : columnNames) {
        Field fieldColumn = null;
        for (Field field : fields) {
            String fieldName;
            DbFiled dbFiled = field.getAnnotation(DbFiled.class);
            if (dbFiled != null && !TextUtils.isEmpty(dbFiled.value())) {
                fieldName = dbFiled.value();
            } else {
                fieldName = field.getName();
            }

            if (columnName.equals(fieldName)) {
                fieldColumn = field;
                break;
            }
        }
        if (fieldColumn != null) {
            mCacheMap_DbRecord_EntityField.put(columnName, fieldColumn);
        }
    }
}

定义一个缓存Map key是数据库字段名 value是EntiytClass成员变量为什么需要做这个缓存,第一是为了性能优化,第二是后面操作时通过反射把Cursor设置给entityClass比较方便,第三是数据库字段和entity进行映射。

上面BaseDao定义好之后,我们需要在定义一个管理数据库的单例类来管理数据库的所有表的Dao:

/**
 * @Describe: 管理者该数据库的所有表, 而且公共数据只有一个
 */
public class BaseDaoFactory {
//防止同一个数据库同一张表存在多个BaseDao,key是entityClass,value是dao
//因为公共数据库只存在一个,那么我们之缓存dao,即:dao的缓存池
private WeakHashMap<Class<?>, BaseDao> mDaoWeakHashMap;
private SQLiteDatabase mSqLiteDatabase;
private static String sDbName;
protected static String sDbRootPath;

//你可以认为一个设备上只能存在一个数据库
protected BaseDaoFactory() {
    if (TextUtils.isEmpty(sDbName)) throw new RuntimeException("must call init method");
    this.mSqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sDbName, null);
    this.mDaoWeakHashMap = new WeakHashMap<>();
}

private static class BaseDaoFactoryHolder {
    private static final BaseDaoFactory INSTANCE = new BaseDaoFactory();
}

public static BaseDaoFactory getInstance() {
    return BaseDaoFactoryHolder.INSTANCE;
}

/**
 * 使用前必须初始化
 *
 * @param dbName data/data/xx.xx/my.db
 */
public static void init(Context context, String dbName) {
    File file = new File("data/data/" + context.getPackageName());
    if (!file.exists()) {
        file.mkdirs();
    }
    BaseDaoFactory.sDbRootPath = file.getAbsolutePath();
    BaseDaoFactory.sDbName = sDbRootPath + "/" + dbName;
}

/**
 * @param daoClass    获取到指定Dao
 * @param entityClass 对应数据表的实体类,也就是表
 * @param <DAO>       获取到指定Dao
 * @param <ENTITY>    对应数据表的实体类
 * @return 具体的实体类
 * <p>
 * create table if not exists order(desc TEXT,id INTEGER)
 * create table if not exists tb_user(id INTEGER,name TEXT,password TEXT)
 */
@Nullable
public <DAO extends BaseDao<ENTITY>, ENTITY> DAO getBaseDao(@NonNull Class<DAO> daoClass, Class<ENTITY> entityClass) {
    // 每一张表对应一个dao
    BaseDao baseDao = mDaoWeakHashMap.get(entityClass);
    try {
        if (baseDao == null) {
            synchronized (BaseDaoFactory.class) {
                baseDao = mDaoWeakHashMap.get(entityClass);
                if (baseDao == null) {
                    baseDao = daoClass.newInstance();
                    // 每一张表对应一个dao,为了解决自动创建表,必须一个表对应一个dao
                    boolean isSuccess = baseDao.init(mSqLiteDatabase, entityClass);
                    if (isSuccess) {
                        mDaoWeakHashMap.put(entityClass, baseDao);
                    }
                }
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
    return (DAO) baseDao;
}

}

在BaseDaoFactory 中管理者该数据库的所有表, 并且对每一个Dao做了缓存,避免每次操作还要重新创建Dao造成不必要的性能开销,BaseDaoFactory 管理者该数据库的所有表, 而且公共数据只有一个,为什么这么说呢?因为目前我们的项目用到数据库分库,我们需要把公共数据库和用户的私有数据库分开来管理,而BaseDaoFactory就是管理者我们的公共数据库,那接下来看看如何分库?

public class BaseDaoSubFactory extends BaseDaoFactory {
private static final BaseDaoSubFactory instance = new BaseDaoSubFactory();

//数据库连接池,key:dbpth  value : WeakHashMap<Class<?>,BaseDao>
// WeakHashMap<Class<?>,BaseDao> :防止同一个数据库同一张表存在多个BaseDao,key是entityClass,value是dao
private WeakHashMap<String, WeakHashMap<Class<?>, BaseDao>> mDbWeakHashMap;

private BaseDaoSubFactory() {
    super();
    mDbWeakHashMap = new WeakHashMap<>();
}

public static BaseDaoSubFactory getInstance() {
    return instance;
}

/**
 * 定义一个用于分库的数据库对象
 */
private SQLiteDatabase subSqLiteDatabase;


@Override
@Nullable
public <DAO extends BaseDao<ENTITY>, ENTITY> DAO getBaseDao(@NonNull Class<DAO> daoClass, Class<ENTITY> entityClass) {
    String databaseValue = PrivateDbPathHelper.getValue(sDbRootPath);
    BaseDao baseDao = getDao(entityClass, databaseValue);
    try {
        //baseDao为null,有两种情况,1、首次创建数据库;2、首次使用dao
        if (baseDao == null) {
            synchronized (BaseDaoSubFactory.class) {
                baseDao = getDao(entityClass, databaseValue);
                if (baseDao == null) {
                    subSqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(databaseValue, null);
                    // 每一张表对应一个dao,为了解决自动创建表,必须一个表对应一个dao
                    baseDao = daoClass.newInstance();
                    boolean isSuccess = baseDao.init(subSqLiteDatabase, entityClass);
                    if (isSuccess) {
                        //从数据库缓存池中获取对应的dao
                        WeakHashMap<Class<?>, BaseDao> daoWeakHashMap = mDbWeakHashMap.get(databaseValue);
                        if (daoWeakHashMap != null) {
                            daoWeakHashMap.put(entityClass, baseDao);
                        }
                    }
                }
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
    return (DAO) baseDao;
}


/**
 * 私有数据库可能存在多个,所以我们需要缓存起来,并且要把统一数据中dao也缓存起来
 *
 * @param entityClass
 * @param databaseValue
 * @param <ENTITY>
 * @return
 */
@Nullable
private <ENTITY> BaseDao getDao(Class<ENTITY> entityClass, String databaseValue) {
    //从数据库databaseValue中拿到对应的dao的缓存池
    WeakHashMap<Class<?>, BaseDao> daoWeakHashMap = mDbWeakHashMap.get(databaseValue);
    if (daoWeakHashMap == null) {
        //管理以前的数据库,注意关闭的是私有数据库
        if (subSqLiteDatabase != null && subSqLiteDatabase.isOpen()) {
            subSqLiteDatabase.close();
        }
        //清空以前的dao缓存池
        mDbWeakHashMap.clear();
        // 如果dao的缓存池不存在,那么创建一个dao的缓存池,并加入该数据库缓存池中
        daoWeakHashMap = new WeakHashMap<>();
        mDbWeakHashMap.put(databaseValue, daoWeakHashMap);
    }
    //从获取dao缓存池中获取到指定的dao
    return daoWeakHashMap.get(entityClass);
}
}

可以看到BaseDaoSubFactory 继承BaseDaoFactory ,并且在BaseDaoSubFactory 中因为我们切换数据,我们需要把每一个数据库dao缓存,并且是根据数据库进行区分的。

多表查询:select * from person,dept where person.did = dept.did;

上一篇 下一篇

猜你喜欢

热点阅读