在上一篇Android Studio开发之初入门(一)文章后,一直在研究Android App端的开发,心里一直惦记着一个Android App如何架构,后来干脆不想了。先找到一个点进行突破吧,App本地需要缓存数据吧,那么本地数据如何处理最为方便、简洁、省力。一款app应用,在本地肯定会缓存一些数据,比如用户信息,在Android App端最常用的数据库是sqlite。
根据网友们的推荐,选择了greenDao,研究一下,果真简便、省代码。接下来本文就从这几个方面,说说greenDao是啥,如何使用的,最重要的,附加代码和配置。
1.什么是greenDao
2.greenDao如何配置
3.greenDao使用,实体类、Dao的生成
4.Dao层的具体使用,增删改查、条件查询、批量修改
5.数据库表改动,数据库的升级操作
一.什么是greenDao
根据greenDao官网描述,greenDao是针对Android端SQLited数据库的ORM(对象关系映射)型数据库中间件,大家都知道,SQLite并不是关系型数据库,数据类型也有限,但是中间件greenDao可以让我们像操作对象一样,去操作SQLite数据库,是何等的方便和简洁,省去了多少麻烦。
二.GreenDao如何配置
第一步,新建一个Android App项目,编程语言选择java语言;
图-1图-2
图-3
第二步,添加配置;
1)首先在项目的build.gradle,dependencies增加配置:
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 版本建议最新
图-4
2)然后在app模块的build.gradle,头部增加配置
apply plugin: 'org.greenrobot.greendao'
android的配置里增加:
// greendao 配置
greendao {
schemaVersion 1 // 数据库版本号,根据需要修改
daoPackage 'com.example.myapplication.dao' // greenDao 自动生成的代码保存的包名
targetGenDir 'src/main/java' // 自动生成的代码存储的路径,默认是 build/generated/source/greendao.
}
图-5
Dependencies里增加:
// GreenDao 数据库ORM
implementation 'org.greenrobot:greendao:3.2.2'
// GreenDao 生成dao和model的generator的项目 发布时可以去掉
implementation 'org.greenrobot:greendao-generator:3.2.2'
图-6
三.GreenDao使用,实体类、Dao的生成
1)新建实体类User,放在entity文件夹里, User实体类内容如下:
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.NotNull;
import org.greenrobot.greendao.annotation.Unique;
import org.greenrobot.greendao.annotation.Generated;
@Entity(nameInDb = "user")
public class User {
@Id
@Unique
@NotNull
private String userId;
@Unique
@NotNull
private String userName;
}
2)运行项目,生成了三个文件,UserDao、DaoSession、DaoMaster类
图-7
3)打开生成的文件,发现并没有指定数据库名称,新建BaseApplication指明数据库名称;
import android.app.Application;
import android.database.sqlite.SQLiteDatabase;
import com.example.myapplication.dao.DaoMaster;
import com.example.myapplication.dao.DaoSession;
import static com.example.myapplication.dao.DaoMaster.*;
public class BaseApplication extends Application {
private DevOpenHelper mHelper;
private SQLiteDatabase db;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
private static DaoSession daoSession;
public static BaseApplication instances;
@Override
public void onCreate() {
super.onCreate();
//配置数据库
// 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
// 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。
// 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。
// 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。
mHelper = new DevOpenHelper(this.getBaseContext(), "greendao-db", null);
db = mHelper.getWritableDatabase();
// 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
mDaoMaster = new DaoMaster(db);
mDaoSession = mDaoMaster.newSession();
instances = this;
}
public static BaseApplication getInstances(){
return instances;
}
public DaoSession getDaoSession() {
return mDaoSession;
}
public SQLiteDatabase getDb() {
return db;
}
}
4) 需要在AndroidManifest.xml,增加代码,为application指定名称
android:name=".dbconfig.BaseApplication"
四.Dao层的具体使用,增删改查、条件查询、批量修改;
1)在MainActivity添加增删改查的代码:
public void addOnClick(View view){
Toast.makeText(this, "add", Toast.LENGTH_SHORT).show();
//新增
User user = new User();
// user.setUserId("12345678");
// user.setUserName("bbbbbb");
// getUserDao().insert(user);
//
// //根据主键进行查询
// User user1 = getUserDao().load("123456");
// if(user1 != null ){
// Toast.makeText(this, user1.getUserName(), Toast.LENGTH_SHORT).show();
// }
//修改
// user.setUserName("bbbbbbbb");
// getUserDao().update(user);
//
// User user1 = getUserDao().load("123456");
// if(user1 != null ){
// Toast.makeText(this, user1.getUserName(), Toast.LENGTH_SHORT).show();
// }
//删除
// getUserDao().delete(user);
// User user1 = getUserDao().load("123456");
// if(user1 != null ){
// Toast.makeText(this, user1.getUserName(), Toast.LENGTH_SHORT).show();
// }else{
// Toast.makeText(this, "找不到", Toast.LENGTH_SHORT).show();
// }
//查询所有
// List<User> list = getUserDao().loadAll();
// if(list != null && list.size() > 0){
// //批量修改
// for(User tempUser : list){
// tempUser.setUserName(tempUser.getUserId() + tempUser.getUserName());
// }
// getUserDao().updateInTx(list);
// Toast.makeText(this, list.size() + "条", Toast.LENGTH_SHORT).show();
// }else{
// Toast.makeText(this, "找不到", Toast.LENGTH_SHORT).show();
// }
//
//
//
//条件查询
String where = " where user_id = ? and user_name= ? ";
List<User> list = getUserDao().queryRaw(where, new String[]{"123456","123456aaaaaa"});
if(list != null && list.size() > 0){
user = list.get(0);
Toast.makeText(this, list.size() + "条" + user.getUserName(), Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, "找不到", Toast.LENGTH_SHORT).show();
}
//
}
/**
* 获取dao
* @return
*/
private UserDao getUserDao(){
return BaseApplication.getInstances().getDaoSession().getUserDao();
}
2)并在 xml 页面给按钮增加click事件:
android:onClick="addOnClick"
图-8
3) 接下来就可以点击运行进行测试了,经过测试,测试结果是OK的。
五.数据库表改动,数据库的升级操作;
如果数据库表中有改动,或者新添加了数据库表,怎么办,继续配置;
1)新增DbManager类
package com.example.myapplication.dbconfig;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.example.myapplication.dao.DaoMaster;
import com.example.myapplication.dao.DaoSession;
import static com.example.myapplication.dbconfig.BaseApplication.DB_NAME;
/**
* 数据库升级管理类
*/
public class DbManager {
// 是否加密
public static final boolean ENCRYPTED = false;
private static DbManager mDbManager;
private static DaoMaster.DevOpenHelper mDevOpenHelper;
private static DaoMaster mDaoMaster;
private static DaoSession mDaoSession;
private Context mContext;
private DbManager(Context context) {
this.mContext = context;
// 初始化数据库信息
mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
getDaoMaster(context);
getDaoSession(context);
}
public static DbManager getInstance(Context context) {
if (null == mDbManager) {
synchronized (DbManager.class) {
if (null == mDbManager) {
mDbManager = new DbManager(context);
}
}
}
return mDbManager;
}
/**
* 获取可读数据库
*
* @param context
* @return
*/
public static SQLiteDatabase getReadableDatabase(Context context) {
if (null == mDevOpenHelper) {
getInstance(context);
}
return mDevOpenHelper.getReadableDatabase();
}
/**
* 获取可写数据库
*
* @param context
* @return
*/
public static SQLiteDatabase getWritableDatabase(Context context) {
if (null == mDevOpenHelper) {
getInstance(context);
}
return mDevOpenHelper.getWritableDatabase();
}
/**
* 获取DaoMaster
*
* 判断是否存在数据库,如果没有则创建数据库
* @param context
* @return
*/
public static DaoMaster getDaoMaster(Context context) {
if (null == mDaoMaster) {
synchronized (DbManager.class) {
if (null == mDaoMaster) {
MyOpenHelper helper = new MyOpenHelper(context,DB_NAME,null);
mDaoMaster = new DaoMaster(helper.getWritableDatabase());
}
}
}
return mDaoMaster;
}
/**
* 获取DaoMaster
*
* @param context
* @return
*/
// public static DaoMaster getDaoMaster(Context context) {
// if (null == mDaoMaster) {
// synchronized (DbManager.class) {
// if (null == mDaoMaster) {
//
// mDaoMaster = new DaoMaster(getWritableDatabase(context));
// }
// }
// }
// return mDaoMaster;
// }
/**
* 获取DaoSession
*
* @param context
* @return
*/
public static DaoSession getDaoSession(Context context) {
if (null == mDaoSession) {
synchronized (DbManager.class) {
mDaoSession = getDaoMaster(context).newSession();
}
}
return mDaoSession;
}
}
2)新增MigrationHelper类
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
import com.example.myapplication.dao.DaoMaster;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.internal.DaoConfig;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MigrationHelper {
private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
private static MigrationHelper instance;
public static MigrationHelper getInstance() {
if(instance == null) {
instance = new MigrationHelper();
}
return instance;
}
@SafeVarargs
public final void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(db, daoClasses);
DaoMaster.dropAllTables(db, true);
DaoMaster.createAllTables(db, false);
restoreData(db, daoClasses);
}
/**
* 生成临时表
* @param db
* @param daoClasses
*/
@SafeVarargs
private final void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (Class<? extends AbstractDao<?, ?>> daoClass : daoClasses) {
DaoConfig daoConfig = new DaoConfig(db, daoClass);
String divider = "";
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList<>();
StringBuilder createTableStringBuilder = new StringBuilder();
createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tableName).contains(columnName)) {
properties.add(columnName);
String type = null;
try {
type = getTypeByClass(daoConfig.properties[j].type);
} catch (Exception exception) {
// Crashlytics.logException(exception);
}
createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);
if (daoConfig.properties[j].primaryKey) {
createTableStringBuilder.append(" PRIMARY KEY");
}
divider = ",";
}
}
createTableStringBuilder.append(");");
db.execSQL(createTableStringBuilder.toString());
String insertTableStringBuilder = "INSERT INTO " + tempTableName + " (" +
TextUtils.join(",", properties) +
") SELECT " +
TextUtils.join(",", properties) +
" FROM " + tableName + ";";
db.execSQL(insertTableStringBuilder);
}
}
/**
* 存储新的数据库表以及数据库
* @param db
* @param daoClasses
*/
@SafeVarargs
private final void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (Class<? extends AbstractDao<?, ?>> daoClass : daoClasses) {
DaoConfig daoConfig = new DaoConfig(db, daoClass);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
List<String> properties = new ArrayList<>();
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tempTableName).contains(columnName)) {
properties.add(columnName);
}
}
String insertTableStringBuilder = "INSERT INTO " + tableName + " (" +
TextUtils.join(",", properties) +
") SELECT " +
TextUtils.join(",", properties) +
" FROM " + tempTableName + ";";
db.execSQL(insertTableStringBuilder);
db.execSQL("DROP TABLE " + tempTableName);
}
}
private String getTypeByClass(Class<?> type) throws Exception {
if(type.equals(String.class)) {
return "TEXT";
}
if(type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
return "INTEGER";
}
if(type.equals(Boolean.class)) {
return "BOOLEAN";
}
// Crashlytics.logException(exception);
throw new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
}
private static List<String> getColumns(Database db, String tableName) {
List<String> columns = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
if (cursor != null) {
columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return columns;
}
}
3)新增MyOpenHelper类
import com.example.myapplication.dao.DaoMaster;
import com.example.myapplication.dao.UserDao;
import org.greenrobot.greendao.database.Database;
public class MyOpenHelper extends DaoMaster.OpenHelper {
public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
/**
* 数据库升级
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
//操作数据库的更新 有几个表升级都可以传入到下面
MigrationHelper.getInstance().migrate(db, UserDao.class);
}
4)在user实体类中增加sex属性
private String sex;
5)在app的配置文件gradle将schemaVersion改为2,以后每次递增1,然后运行,运行后生成了新增的字段,测试后可见。
图-9
总结:
1)如果表的唯一标识是string类型的,只要增加@Id的,就可以使用load方法进行查询,不一定非得用Long型的id。
2)数据库表中的数据类型如果是integer时,那么不能储存空,所以字段类型最好用String类型。
3)表中增加字段或新增表时,配置文件里的版本好必须加1,可以从低版本往高版本升级,不可从高往低降级,如果建立了多个app项目时,每个项目的数据库名称如果是使用的同一个,必须统一版本号,否则会报错。
本文参考文档:
https://blog.csdn.net/xy8199/article/details/78690545
https://www.jianshu.com/p/a490daa82fcb
https://blog.csdn.net/ITxiaodong/article/details/81570800
https://www.jianshu.com/p/ec37ea99ef69
数据库的升级:
https://blog.csdn.net/pingping_010/article/details/80669282
网友评论