GreenDao

一、数据库升级(如增加字段)

1、增加phone

@Entity
public class UserModel {
    @Id(autoincrement = true)
    private Long id; //主键
    private String name; //姓名
    private String phone;//电话
}

2、修改版本号1->2


greendao {
    //数据库的版本
    schemaVersion 2
    //设置DaoMaster、DaoSession、Dao包名,即这些类存放的路径。
    daoPackage 'cn.lpf.demo.database'
    //设置DaoMaster、DaoSession、Dao目录
    targetGenDir 'src/main/java'
}

3、Build->Make Project->会自动更新DaoMaster、UserModel、UserModelDao文件,至此已完成升级,下面是数据库工具类,方便使用。

public class GreenDaoUtil {
    private static final String DB_NAME = "user.db";
    private static final GreenDaoUtil INSTANCE = new GreenDaoUtil();
    private DaoSession daoSession;
    private SQLiteDatabase database;

    private GreenDaoUtil() {}

    public static GreenDaoUtil getInstance() {
        return INSTANCE;
    }

    /**
     * 初始化数据库
     * 建议放在Application中执行
     */
    public void initDataBase(Context context) {
        
        //通过DaoMaster的内部类DevOpenHelper,可得到一个SQLiteOpenHelper对象。
        DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper
                (context, DB_NAME, null); 
        database = devOpenHelper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(database);
        daoSession = daoMaster.newSession();
    }

    public DaoSession getDaoSession() {
        return daoSession;
    }

    public UserModelDao getUserModelDao() {
        return daoSession.getUserModelDao();
    }
}
UserModel userModel = new UserModel();
userModel.setName("xiao");
userModel.setPhnoe("1821456123");
GreenDaoUtil.getInstance().getMediaRecordDao().insert(userModel);

4、默认升级后原有数据将会被清除,以下是默认生成的升级代码DaoMaster内部类

   public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }

        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            //清除所有表然后重新创建
            dropAllTables(db, true);
            onCreate(db);
        }
    }

5、往往我们升级仍需要原有数据,则需要我们进行自定义,具体方案如下

(1)将所有表生成相应的临时表,将数据保存至临时表中

(2)删除全部原有的正式表

(3)生成全部新的正式表

(4)将临时表的数据恢复到新的正式表中(其中如果新增字段是数字类型如int,而临时表中没有该字段则值为空,而空赋值给新表的int型会发生异常,因此需要先将临时表中添加没有的列并赋上默认值)

以下是具体实现代码,只需修改GreenDaoUtil即可实现,升级流程还是以上步骤即可

public class GreenDaoUtil {
    private static final String DB_NAME = "dinduoduo_greendaoutil.db";

    private static class SingleHolder {
        private static final GreenDaoUtil INSTANCE = new GreenDaoUtil();
    }

    public static GreenDaoUtil getInstance() {
        return GreenDaoUtil.SingleHolder.INSTANCE;
    }

    private GreenDaoUtil() { }

    private DaoSession daoSession;
    private SQLiteDatabase database;

    /**
     * 初始化数据库
     * 建议放在Application中执行
     */
    public void initDataBase(Context context) {
        //通过继承DaoMaster的内部类DevOpenHelper自定义升级
        MyDevOpenHelper devOpenHelper = new MyDevOpenHelper(context, DB_NAME, null); //数据库名称
        database = devOpenHelper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(database);
        daoSession = daoMaster.newSession();
        //校验是否所有表都已记录
        if (daoSession.getAllDaos().size() != getAllDaoClass().size()){
            throw new RuntimeException("检查所有表是否都已知GreenDaoUtil.getAllDaoClass()中");
        }
    }

    public DaoSession getDaoSession() {
        return daoSession;
    }

    public MediaRecordModelDao getMediaRecordDao() {
        return daoSession.getMediaRecordModelDao();
    }

    /**
     * 新增表要在这里添加,否则影响升级
     */
    public List<Class<? extends AbstractDao<?, ?>>> getAllDaoClass(){
        List<Class<? extends AbstractDao<?, ?>>> list = new ArrayList();
        list.add(UserModelDao.class);
        return list;
    }

    /**
     * 重写DaoMaster的内部类DevOpenHelper的onUpgrade,实现数据库升级不清除原有数据
     */
    private static class MyDevOpenHelper extends DaoMaster.DevOpenHelper {

        public MyDevOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(Database db, int oldVersion, int newVersion) {
            //将所有表备份至临时表
            for (Class daoClass: GreenDaoUtil.getInstance().getAllDaoClass()) {
                GreenDaoUtil.getInstance().creatTempTable(db, daoClass);
            }
            //清除所有存在的表(除临时表)
            DaoMaster.dropAllTables(db, true);
            //重新创建所有表
            DaoMaster.createAllTables(db, false);
            //从临时表恢复数据
            for (Class daoClass: GreenDaoUtil.getInstance().getAllDaoClass()) {
                GreenDaoUtil.getInstance().restoreData(db, daoClass);
            }
        }
    }

    /**
     * 创建临时表
     */
    private void creatTempTable(Database db, Class<? extends AbstractDao<?, ?>> daoClass){
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        String tableName = daoConfig.tablename;
        String tempTableName = daoConfig.tablename.concat("_TEMP");

        StringBuilder dropTableStringBuilder = new StringBuilder();
        dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");
        db.execSQL(dropTableStringBuilder.toString());
        StringBuilder createTableStringBuilder = new StringBuilder();
        createTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);
        createTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
        db.execSQL(createTableStringBuilder.toString());
    }

    /**
     * 从临时表恢复数据
     */
    private void restoreData(Database db, Class<? extends AbstractDao<?, ?>> daoClass){
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        String tempTableName = daoConfig.tablename.concat("_TEMP");
        //获取临时表所有列名
        List<String> tempTableColumnNameList = getColumnNames(db, tempTableName);
        //正式表所有列名集合
        List<String> columnNameList = new ArrayList<>();
        for (Property property : daoConfig.properties){
            //如果临时表中无正式表的列则添加该列,用于解决数字类型如int赋值为null导致异常
            if (!tempTableColumnNameList.contains(property.columnName)){
                StringBuilder alterTableStringBuilder = new StringBuilder();
                alterTableStringBuilder.append("ALTER TABLE ").append(tempTableName);
                alterTableStringBuilder.append(" ADD COLUMN ").append(property.columnName);
                if (property.type == byte.class || property.type == Byte.class){
                    alterTableStringBuilder.append(" INTEGER DEFAULT 0");
                } else if (property.type == short.class || property.type == Short.class){
                    alterTableStringBuilder.append(" INTEGER DEFAULT 0");
                }else if (property.type == int.class || property.type == Integer.class){
                    alterTableStringBuilder.append(" INTEGER DEFAULT 0");
                }else if (property.type == long.class || property.type == Long.class){
                    alterTableStringBuilder.append(" Long DEFAULT 0");
                }else if (property.type == float.class || property.type == Float.class){
                    alterTableStringBuilder.append(" REAL DEFAULT 0");
                }else if (property.type == double.class || property.type == Double.class){
                    alterTableStringBuilder.append(" REAL DEFAULT 0");
                }else if (property.type == boolean.class || property.type == Boolean.class){
                    alterTableStringBuilder.append(" INTEGER DEFAULT 0");
                }else if (property.type == byte[].class || property.type == Byte[].class){
                    alterTableStringBuilder.append(" BLOB");
                }else if (property.type == java.util.Date.class){
                    alterTableStringBuilder.append(" INTEGER");
                }else {
                    alterTableStringBuilder.append(" TEXT");
                }
                db.execSQL(alterTableStringBuilder.toString());
            }
            columnNameList.add(property.columnName);
        }
        if (columnNameList.size() > 0) {
            final String columnSQL = TextUtils.join(",", columnNameList);
            StringBuilder insertTableStringBuilder = new StringBuilder();
            insertTableStringBuilder.append("INSERT INTO ").append(daoConfig.tablename).append(" (");
            insertTableStringBuilder.append(columnSQL);
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(columnSQL);
            insertTableStringBuilder.append(" FROM ").append(tempTableName);
            insertTableStringBuilder.append(";");
            db.execSQL(insertTableStringBuilder.toString());
        }
        StringBuilder dropTableStringBuilder = new StringBuilder();
        dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
        db.execSQL(dropTableStringBuilder.toString());
    }
    /**
     * 获取表所有列名
     */
    private List<String> getColumnNames(Database db, String tableName) {
        List<String> columns = null;
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
            if (null != cursor && cursor.getColumnCount() > 0) {
                columns = Arrays.asList(cursor.getColumnNames());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null){
                cursor.close();
            }
            if (null == columns){
                columns = new ArrayList<>();
            }
        }
        return columns;
    }
}

二、多渠道打包输出多个APP的场景

首先需要明白以上步骤2中daoPackage、targetGenDir用来配置生成代码的位置,只是在Make Project进行执行,不参与编译及打包,而参与编辑的是生成后的代码类

1、如果应用A和应用B使用的表结构没有差异,则以上步骤2生成代码位置无需改动即可,编译不同渠道都会使用该目录下的代码

2、应用A使用数据表UserModel,应用B使用的数据表DogModel
两个表字段存在差异,需要根据不同的渠道生成不同的代码,在以上步骤2中,分别定义和Make Project,最终在项目目录下即生成应用A所需的代码又生成应用B所需的代码,然后根据渠道使用相应的数据库代码即可


greendao {
    //A应用数据库的版本
    schemaVersion 2
    //A应用设置DaoMaster、DaoSession、Dao包名,即这些类存放的路径。
    daoPackage 'cn.lpf.demo.database.a'
    //A应用设置DaoMaster、DaoSession、Dao目录
    targetGenDir 'src/main/java'

 
    //B应用数据库的版本
    schemaVersion 4
    //B应用设置DaoMaster、DaoSession、Dao包名,即这些类存放的路径。
    daoPackage 'cn.lpf.demo.database.b'
    //B应用设置DaoMaster、DaoSession、Dao目录
    targetGenDir 'src/main/java'
}

生成代码目录

根据不同的渠道生成相应的数据库

public class GreenDaoUtil {
    private static final String A_DB_NAME = "user.db";
    private static final String B_DB_NAME = "dog.db";
    private static final GreenDaoUtil INSTANCE = new GreenDaoUtil();
    private com.lpf.demo.database.a.DaoSession daoSessionA;
    private com.lpf.demo.database.b.DaoSession daoSessionB;
    private SQLiteDatabase databaseA;
    private SQLiteDatabase databaseB;

    private GreenDaoUtil() {}

    public static GreenDaoUtil getInstance() {
        return INSTANCE;
    }

    /**
     * 初始化数据库
     * 建议放在Application中执行
     */
    public void initDataBase(Context context) {
        //通过DaoMaster的内部类DevOpenHelper,可得到一个SQLiteOpenHelper对象。
        if("A".equals(BuildConfig.PRODUCT)){
            com.lpf.demo.database.a.DaoMaster.DevOpenHelper devOpenHelper = new com.lpf.demo.database.a.DaoMaster.DevOpenHelper
                (context, A_DB_NAME, null); 
            databaseA = devOpenHelper.getWritableDatabase();
            com.lpf.demo.database.a.DaoMaster daoMasterA = new com.lpf.demo.database.a.DaoMaster(databaseA);
            daoSessionA = daoMasterA.newSession();
        }else if("B".equals(BuildConfig.PRODUCT)){
            com.lpf.demo.database.b.DaoMaster.DevOpenHelper devOpenHelper = new 
            com.lpf.demo.database.b.DaoMaster.DevOpenHelper
                (context, B_DB_NAME, null); 
            databaseB = devOpenHelper.getWritableDatabase();
            com.lpf.demo.database.b.DaoMaster daoMasterB = new com.lpf.demo.database.b.DaoMaster(databaseB);
            daoSessionB = daoMasterB.newSession();
        }       
    }

    public com.lpf.demo.database.a.DaoSession getDaoSessionA() {
        return daoSessionA;
    }

    public UserModelDao getUserModelDao() {
        return daoSessionA.getUserModelDao();
    }

    public com.lpf.demo.database.b.DaoSession getDaoSessionB() {
        return daoSessionB;
    }

    public DogModelDao getDogModelDao() {
        return daoSessionB.getDogModelDao();
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值