一、数据库升级(如增加字段)
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();
}
}