Android GreenDao

离线开发

开发工具: androidStudio ,SQLite3

使用开源库:GreenDao 3.2.2

离线已实现:

1.  增、删、改、查

2. 新增表,字段,更改字段,升级数据迁移

3. 从服务器下载数据,并更新到本地数据表,避免重复添加

4. 代码封装

集成:

1. 项目根目录添加环境

buildscript {
    ...
    dependencies {
        classpath 'com.android.tools.build:gradle:3.6.0'
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 添加 GreenDao 插件
    }
}
allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' } // 添加
    }
}

2. app 下添加依赖

// 与 application 同级
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao'

// android 节点中,rootProject.ext.publish.versionCode 文尾介绍
greendao {
  // 数据库升级视为 App 重大升级,跟随 versionCode ,方便记忆
  schemaVersion rootProject.ext.publish.versionCode
}

// dependencies 节点中
implementation 'org.greenrobot:greendao:3.2.2' // 添加 

3. 对象,也就是表

@Entity(nameInDb = "Users")
public class UserBean {
    @Id(autoincrement = true)
    @Index(unique = true)//设置唯一性
    @Property(nameInDb = "F_id")
    private Long F_id;
    @Property(nameInDb = "F_byyx")
    private String F_byyx;
    @Property(nameInDb = "F_csrq")
    private String F_csrq;
    
	...
}

4. 普通的增删改查

UserBeanDao userBeanDao = DaoManager.getInstance().getDaoSession().getUserBeanDao();

// 增
userBeanDao.insert(new UserBean(100L, "王大锤", "女"));

// 删
userBeanDao.deleteByKey(100L);

// 改
UserBean bean = mUserBeanDao.load(32L);
bean.setF_csrq("改改");
userBeanDao.update(bean);

// 查
StringBuilder sb = new StringBuilder();
List<UserBean> userBeans = mUserBeanDao.loadAll();
for (UserBean userBean : userBeans) {
    sb.append("id:").append(userBean.getId())
       .append("f_id:").append(userBean.getF_id())
       .append("F_byyx:").append(userBean.getF_byyx())
       .append("F_csrq:").append(userBean.getF_csrq())
        .append("\n");
}
Log.i(TAG, "showDataList: "+sb.toString();

5. 新增表,字段,更改字段,升级时数据迁移,最后全部代码介绍

// 这里升级最好是将所有的表都写上,工具类会自动进行数据迁移
		MigrationHelper.migrate(
			db,
			StuffBeanDao.class,
			DepartmentBeanDao.class
        );

6. 从服务器下载数据,并更新到本地数据表,业务上会一次下载多张表,所以我统一放到了一个子线程中处理(真正实现过程使用的 IntentService ,其内部就是子线程,用完后,会自动销毁)

// 模拟网络数据
List<UserBean> list = new ArrayList<>();
list.add(new UserBean( 111L, "王大宝", "男"));
list.add(new UserBean( 112L, "王大宝", "男"));
list.add(new UserBean( 113L, "王大宝", "男"));
list.add(new UserBean( 114L, "王大宝", "男"));
list.add(new UserBean( 115L, "王大宝", "男"));
list.add(new UserBean( 116L, "王大宝", "男"));
list.add(new UserBean( 117L, "王大宝", "男"));

// 更新或插入操作
for (int i = 0; i < list.size(); i++) {

	UserBean bean = list.get(i);
	
	// dao.hasKey(bean) 这句话只是说这个对象中是否存在主键,因为我设置了主键所以肯定存在
	// mUserBeanDao.load(bean.getF_id()) != null ,查询看看是否存在,存在则更新,不存在则插入
	
	if (mUserBeanDao.load(bean.getF_id()) != null) {

		mUserBeanDao.update(bean);

	} else {
		mUserBeanDao.insert(bean);
	}

}

全部源码

// 常量
public interface DbConstants {

    String DB_NAME = "test.db";// 数据库名称

}

// 应用启动类
public class GreenDaoApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        initGreenDao();
    }
    /**
     * 初始化 GreenDao 数据库
     */
    private void initGreenDao() {
        DaoManager.getInstance().init(this);
        DaoManager.getInstance().getDaoMaster();
    }
}

// 数据迁移类
public final class MigrationHelper {

	private static final String TAG = "MigrationHelper";
	private static final String SQLITE_MASTER = "sqlite_master";
	private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master";

	public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
		LogUtil.d(TAG,"【The Old Database Version】" + db.getVersion());
		Database database = new StandardDatabase(db);
		migrate(database, daoClasses);
	}


	public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) {
		LogUtil.d(TAG,"【Generate temp table】start");
		generateTempTables(database, daoClasses);
		LogUtil.d(TAG,"【Generate temp table】complete");

		LogUtil.d(TAG,"【Drop all table and recreate all table】");
		for (int i = 0; i < daoClasses.length; i++) {
			DaoConfig daoConfig = new DaoConfig(database, daoClasses[i]);
			dropTable(database, true, daoConfig);
			createTable(database, false, daoConfig);
		}

		LogUtil.d(TAG,"【Restore data】start");
		restoreData(database, daoClasses);
		LogUtil.d(TAG,"【Restore data】complete");
	}

	private static void dropTable(Database database, boolean ifExists, DaoConfig daoConfig) {
		String sql = String.format("DROP TABLE %s\"%s\"", ifExists ? "IF EXISTS " : "", daoConfig.tablename);
		database.execSQL(sql);
	}

	private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
		for (int i = 0; i < daoClasses.length; i++) {
			String tempTableName = null;

			DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
			String tableName = daoConfig.tablename;
			if (!isTableExists(db, false, tableName)) {
				LogUtil.d(TAG,"【New Table】" + tableName);
				continue;
			}
			try {
				tempTableName = daoConfig.tablename.concat("_TEMP");
				StringBuilder dropTableStringBuilder = new StringBuilder();
				dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");
				LogUtil.d(TAG,"【Generate temp table】 dropTableStringBuilder:" + dropTableStringBuilder);
				db.execSQL(dropTableStringBuilder.toString());

				StringBuilder insertTableStringBuilder = new StringBuilder();
				insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);
				insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
				LogUtil.d(TAG,"【Generate temp table】 insertTableStringBuilder:" + insertTableStringBuilder);
				db.execSQL(insertTableStringBuilder.toString());
				LogUtil.d(TAG,"【Table】" + tableName +"\n ---Columns-->" + getColumnsStr(daoConfig));
				LogUtil.d(TAG,"【Generate temp table】" + tempTableName);
			} catch (SQLException e) {
				Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e);
			}
		}
	}

	private static boolean isTableExists(Database db, boolean isTemp, String tableName) {
		if (db == null || TextUtils.isEmpty(tableName)) {
			return false;
		}
		String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER;
		String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?";
		Cursor cursor=null;
		int count = 0;
		try {
			cursor = db.rawQuery(sql, new String[]{"table", tableName});
			if (cursor == null || !cursor.moveToFirst()) {
				return false;
			}
			count = cursor.getInt(0);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (cursor != null)
				cursor.close();
		}
		return count > 0;
	}


	private static String getColumnsStr(DaoConfig daoConfig) {
		if (daoConfig == null) {
			return "no columns";
		}
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < daoConfig.allColumns.length; i++) {
			builder.append(daoConfig.allColumns[i]);
			builder.append(",");
		}
		if (builder.length() > 0) {
			builder.deleteCharAt(builder.length() - 1);
		}
		return builder.toString();
	}


	private static void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
		for (int i = 0; i < daoClasses.length; i++) {
			DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
			String tableName = daoConfig.tablename;
			String tempTableName = daoConfig.tablename.concat("_TEMP");

			if (!isTableExists(db, true, tempTableName)) {
				continue;
			}

			try {
				// get all columns from tempTable, take careful to use the columns list
				List<String> columns = getColumns(db, tempTableName);
				ArrayList<String> properties = new ArrayList<>(columns.size());
				for (int j = 0; j < daoConfig.properties.length; j++) {
					String columnName = daoConfig.properties[j].columnName;
					if (columns.contains(columnName)) {
						properties.add(columnName);
					}
				}
				if (properties.size() > 0) {
					final String columnSQL = TextUtils.join(",", properties);

					StringBuilder insertTableStringBuilder = new StringBuilder();
					insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
					insertTableStringBuilder.append(columnSQL);
					insertTableStringBuilder.append(") SELECT ");
					insertTableStringBuilder.append(columnSQL);
					insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
					LogUtil.d(TAG,"【Restore data】 db sql: " + insertTableStringBuilder);
					db.execSQL(insertTableStringBuilder.toString());
					LogUtil.d(TAG,"【Restore data】 to " + tableName);
				}
				StringBuilder dropTableStringBuilder = new StringBuilder();
				dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
				db.execSQL(dropTableStringBuilder.toString());
				LogUtil.d(TAG,"【Drop temp table】" + tempTableName);
			} catch (SQLException e) {
				Log.e(TAG, "【Failed to restore data from temp table 】" + tempTableName, e);
			}
		}
	}

	private static List<String> getColumns(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;
	}

	public static void createTable(Database db, boolean ifNotExists, DaoConfig daoConfig) {
		String tableName = daoConfig.tablename;
		StringBuilder builder = new StringBuilder();
		builder.append("CREATE TABLE ");
		builder.append(ifNotExists ? "IF NOT EXISTS ": "");
		builder.append(tableName);
		builder.append(getColumnsSql(daoConfig));
		LogUtil.d(TAG,"【createTable】 sql:" + builder.toString());
		db.execSQL(builder.toString()); // 6: Description
	}

	private static String getColumnsSql(DaoConfig daoConfig) {
		if (daoConfig == null) {
			return "";
		}
		StringBuilder builder = new StringBuilder(" (");
		for (int i = 0; i < daoConfig.properties.length; i++) {
			builder.append(String.format("\"%s\" %s,", daoConfig.properties[i].columnName,
					getPropertyType(daoConfig.properties[i].type)));
		}
		if (daoConfig.properties.length > 0 && builder.length() > 0) {
			builder.deleteCharAt(builder.length() - 1);
		}
		builder.append("); ");
		return builder.toString();
	}

	/**
	 * 根据字段类型返回对应的数据库字段语句
	 * @param type
	 * @return
	 */
	private static String getPropertyType(Class<?> type) {
		if (type.equals(byte[].class)) {
			return "BLOB";
		} else if (type.equals(String.class)) {
			return "TEXT DEFAULT ''";
		} else if (type.equals(boolean.class) || type.equals(Boolean.class)
				|| type.equals(int.class) || type.equals(Integer.class)
				|| type.equals(long.class) || type.equals(Long.class)
				|| type.equals(Date.class) || type.equals(Byte.class)) {
			return "INTEGER DEFAULT (0)";
		} else if (type.equals(float.class) || type.equals(Float.class)
				|| type.equals(double.class) || type.equals(Double.class)){
			return "REAL DEFAULT (0)";
		}
		return "TEXT DEFAULT ''";
	}

}

// 数据库管理类,封装
public class DaoManager {

    private Context mContext;

    //创建数据库的名字
    private static final String DB_NAME = DbConstants.DB_NAME;

    //多线程中要被共享的使用volatile关键字修饰  GreenDao管理类
    private volatile static DaoManager mInstance;

    //它里边实际上是保存数据库的对象
    private static DaoMaster mDaoMaster;

    //创建数据库的工具
    private static DbOpenHelper mHelper;

    //管理gen里生成的所有的Dao对象里边带有基本的增删改查的方法
    private static DaoSession mDaoSession;


    private DaoManager() {
    }

    /**
     * 单例模式获得操作数据库对象
     *
     * @return
     */
    public static DaoManager getInstance() {
        if (mInstance == null) {
            synchronized (DaoManager.class) {
                if (mInstance == null) {
                    mInstance = new DaoManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 初始化上下文创建数据库的时候使用
     */
    public void init(Context context) {
        this.mContext = context;
    }

    /**
     * 判断是否有存在数据库,如果没有则创建
     *
     * @return
     */
    public DaoMaster getDaoMaster() {
        if (mDaoMaster == null) {
            mHelper = new DbOpenHelper(mContext,DB_NAME);
            mDaoMaster = new DaoMaster(mHelper.getWritableDatabase());
        }
        return mDaoMaster;
    }

    /**
     * 完成对数据库的添加、删除、修改、查询操作,
     *
     * @return
     */
    public DaoSession getDaoSession() {
        if (mDaoSession == null) {
            if (mDaoMaster == null) {
                mDaoMaster = getDaoMaster();
            }
            mDaoSession = mDaoMaster.newSession(IdentityScopeType.None);
        }
        return mDaoSession;
    }

    /**
     * 关闭所有的操作,数据库开启后,使用完毕要关闭
     */
    public void closeConnection() {
        closeHelper();
        closeDaoSession();
    }

    public void closeHelper() {
        if (mHelper != null) {
            mHelper.close();
            mHelper = null;
        }
    }

    public void closeDaoSession() {
        if (mDaoSession != null) {
            mDaoSession.clear();
            mDaoSession = null;
        }
    }
}

// 升级时需要的类

public class DbOpenHelper extends DaoMaster.OpenHelper {

	public DbOpenHelper(Context context, String name) {
		super(context, name);
	}

	@Override
	public void onCreate(Database db) {
		super.onCreate(db);
		startMigrate(db);
	}

	@Override
	public void onUpgrade(Database db, int oldVersion, int newVersion) {
		super.onUpgrade(db, oldVersion, newVersion);
		startMigrate(db);
	}

	private void startMigrate(Database db) {

		// 这里升级最好是将所有的表都写上,关键是数据库一定要更新,版本 和 app 同步,
		MigrationHelper.migrate(
				db,
				StuffBeanDao.class,
				DepartmentBeanDao.class
		);

	}

}

文章结尾:

解释:schemaVersion rootProject.ext.publish.versionCode ,就是全局配置,如果依赖库多了方便管理

步骤1:在项目根目录新建 config.gradle 文件

ext {

    android = [
            compileSdkVersion: 29,
            buildToolsVersion: "28.0.0",
            minSdkVersion    : 19,
            targetSdkVersion : 28,
            supportLibVersion: "28.0.0"
    ]

  
    publish = [
            applicationId: "com.test.test",
            versionCode  : 1,
            versionName  : "1.0"
    ]
}

步骤2:在项目根目录 build.gradle 文件顶部引入

apply from: "config.gradle"

步骤3:在 App build.gradle 文件中使用

greendao {
	// 跟随 versionCode todo 数据库升级视为 App 重大升级,直接升级 VersionCode & VersionName
	schemaVersion rootProject.ext.publish.versionCode
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值