https://blog.csdn.net/qq_35956194/article/details/79167897
一、在项目根目录中的build.gradle
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0' //添加greendao
}
}
二、在app目录下的build.gradle配置
apply plugin: 'org.greenrobot.greendao' //所有标签之外添加
//配置
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.0.0'
}
}
//配置数据库
greendao {
schemaVersion 1 //数据库版本号
//设置DaoMaster、DaoSession、Dao包名,也就是要放置这些类的包的全路径。(此路径下自动生成一些dao文件。)
daoPackage 'com.yunlu.greendao.gen'
//设置DaoMaster、DaoSession、Dao目录
targetGenDir 'src/main/java'
}
dependencies {
//greenDao 数据库
compile 'org.greenrobot:greendao:3.2.0'
}
三、创建一个User的实体类(数据表对应的bean文件)
@Entity
public class User {
@Id(autoincrement = true)
private Long id;
private String name;
private int age;
private String pic;
private String content;
private String updateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());//设置默认值为当前时间
}
@Entity:将我们的java普通类变为一个能够被greenDAO识别的数据库类型的实体类
@Id:通过这个注解标记的字段必须是Long类型的,这个字段在数据库中表示它就是主键,并且它默认就是自增的(设置了数值后,下一条数据会自动继续加一)
autoincrement = true使用自增长策略
@NotNull:此字段不能为空
@Index(unique = true) //设置为唯一主键
当前,entity必须有一个long或者Long的属性作为主键,但是有时候我们的主键不一定是long或者Long型的可能是string呀之类的,这个时候我们就可以定义索引属性并且注明独一无二。 如下:
@Index(name = "keyword", unique = true)
private String key;
name用于指明这个索引的列名,unique表示这个指是不可重复的。
执行Build->Make Model app ,会自动根据配置在项目中对应的com.yunlu.greendao.gen目录下生成DaoMaster、DaoSession、UserDao三个文件
四、GreenDao使用
public class MyApplication extends Application {
private DaoMaster.DevOpenHelper mHelper;
private SQLiteDatabase db;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
public static MyApplication instances;
@Override
public void onCreate() {
super.onCreate();
instances = this;
setDatabase();
}
public static MyApplication getInstances(){
return instances;
}
private void setDatabase() {
// 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
// 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。
// 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。
// 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。
mHelper = new DaoMaster.DevOpenHelper(this, "notes.db", null);
db = mHelper.getWritableDatabase();
// 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
mDaoMaster = new DaoMaster(db);
mDaoSession = mDaoMaster.newSession();
}
public DaoSession getDaoSession() {
return mDaoSession;
}
public SQLiteDatabase getDb() {
return db;
}
}
DevOpenHelper有两个重载方法:
•DevOpenHelper(Context context,String name)
•DevOpenHelper(Context context,String name,CursorFactory factory)
context上下文这个不用多说,name数据库的名字,cursorFactory游标工厂,一般不用,传入null或者使用两个参数的方法即可。我们对外提供一个getDaoSession()的方法供外部使用。
1.unique() // 返回唯一结果或者 null
2.list() // 返回结果集进内存
3.long count() // 获取结果数量
获取UserDao对象:
UserDao mUserDao = MyApplication.getInstances().getDaoSession().getUserDao();
(1)orderAsc:升序排序
(2)orderDesc: 降序排序
(3)eq():==
(4)noteq():!=
(5)gt(): >
(6)lt():<
(7)ge:>=
(8)le:<=
(9)like():包含 (模糊查询) "%"+string+"%"
(10)isNotNull:不为空
“whereOr” where语句里面写的条件都是用“且”连接,whereOr里的语句使用“或”连接
“distinct” 直接过滤掉重负字段
“limit” 分页n个一页,一般和offset结合使用
“offset” 忽略查询出的前n条结果
“preferLocalizedStringOrder” 本地化字符串排序
“orderCustom” 自定义排序 里面需要传两个参数: 一个属性 和对应的排序方案 ASC 或是 DESC
“orderRaw” 也是自定义排序, 把字段和 排序方案 写在一个字符串传入
“stringOrderCollation” 也是自定义排序 可以合并多个升降排序方案 以日期升序 且 价格降序
“notEq” 和eq相反,别傻傻在再去外面敲“!”取反
“notIn” 同上
“or” 或者
“between” 也就是BETWEEN ? AND ? 可以取两个值的区间 (但是这条语句要慎用,不同的数据库不一样,有的是A<条件<B,有的是A<=条件<=B)
“gt” 相当于 >
“ge”相当于 >=
“lt” 相当于 <
“le”相当于 <=
“isNull” 为空
userList = mUserDao.queryBuilder()
.where(UserDao.Properties.UserName.like("%"+filterStr+"%"))
.list();
(10)between:俩者之间
(11)in:在某个值内 (匹配数组中的某一个)
(12)notIn:不在某个值内
简单的增删改查实现:
- 增
mUser = new User();
mUser.setName("wqtest");
try {
mUserDao.insert(mUser); //插入数据时主键数据重复时会抛出异常,挂掉
}catch (Exception e){
Log.d("e", String.valueOf(e));
}
•插入一组数据
List<MovieCollect> listMovieCollect;
mMovieCollectDao.insertInTx(listMovieCollect);
•插入或替换数据
//插入的数据如果已经存在表中,则替换掉旧数据(根据主键来检测是否已经存在)
MovieCollect movieCollect;
mMovieCollectDao.insertOrReplace(movieCollect);//单个数据
List<MovieCollect> listMovieCollect;
mMovieCollectDao.insertOrReplace(listMovieCollect);//一组数据
2.删
•deleteBykey(Long key) :根据主键删除一条记录。
•delete(User entity) :根据实体类删除一条记录,一般结合查询方法,查询出一条记录之后删除。
•deleteAll(): 删除所有记录。
•删除一组数据
List<MovieCollect> listMovieCollect;
mMovieCollectDao.deleteInTx(listMovieCollect);
List users = mUserDao.queryBuilder()
.where(TabUserDao.Properties.SiteCode.eq(user.getSiteCode()),TabUserDao.Properties.UserCode.eq(user.getUserCode()))
.list();
if (users!=null &&users.size()==1){
try {
mUserDao.delete((TabUser) users.get(0));
}catch (Exception e){
Log.d("e", String.valueOf(e));
}
}
3.改
•update(User entity):更新一条记录
•修改一组数据
List<MovieCollect> listMovieCollect;
mMovieCollectDao.updateInTx(listMovieCollect);
4.查
•loadAll():查询所有记录
•load(Long key):根据主键查询一条记录
•queryBuilder().list():返回:List
•queryBuilder().where(UserDao.Properties.Name.eq("")).list():返回:List
•queryRaw(String where,String selectionArg):返回:List
and/or
//and
//查询电影年份大于2012年且电影名以“我的”开头的电影
List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().and(MovieCollectDao.Properties.Year.gt(2012), MovieCollectDao.Properties.Title.like("我的%")).list();
//or
//查询电影年份小于2012年或者大于2015年的电影
List<MovieCollect> movieCollect = mMovieCollectDao.queryBuilder().or(MovieCollectDao.Properties.Year.lt(2012), MovieCollectDao.Properties.Year.gt(2015)).list();
//按LoginTime倒序排序,查出时间最近的一条数据
List<TabUser> tabUsers = mUserDao.queryBuilder().orderDesc(TabUserDao.Properties.LoginTime).list();
User user = null;
if (tabUsers!=null &&tabUsers.size()>0){
return getUserInfo(tabUsers.get(0));
}
//分组查询
List<User> userList = mUserDao.queryBuilder()
.orderDesc(UserDao.Properties.Id)
.offset(3) //从第3条开始 (从0开始数)
.limit(4).list(); //查询4条数据
(99,88,77,66,55,44,33,22,11)查出来是:(66,55,44,33)
//利用sql语句查询(单列去重)
Cursor c = mUserDao.getDatabase().rawQuery("SELECT distinct SITE_NAME FROM USER", null);
•缓存问题
由于GreenDao默认开启了缓存,所以当你调用A查询语句取得X实体,然后对X实体进行修改并更新到数据库,接着再调用A查询语句取得X实体,会发现X实体的内容依旧是修改前的。其实你的修改已经更新到数据库中,只是查询采用了缓存,所以直接返回了第一次查询的实体。
解决方法:查询前先清空缓存,清空方法如下
//清空所有数据表的缓存数据
DaoSession daoSession = DaoManager.getInstance().getDaoSession();
daoSession .clear();
//清空某个数据表的缓存数据
MovieCollectDao movieCollectDao = DaoManager.getInstance().getDaoSession().getMovieCollectDao();
movieCollectDao.detachAll();
升级
1.新建MyOpenHelper.Java
public class MyOpenHelper extends DaoMaster.DevOpenHelper{
public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
//切记不要调用super.onUpgrade(db,oldVersion,newVersion)
if (oldVersion < newVersion) {
MigrationHelper.migrate(db,TabUserDao.class);//所修改表或新建表
MigrationHelper.migrate(db,TabTestDao.class);
}
}
}
2.Javabean数据表类加相应的字段
3.MyApplication中用MyOpenHelper替代DaoMaster.DevOpenHelper
4.build.gradle中升级数据库版本
附件
package com.msd.standard.Taiguo.greendao;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.internal.DaoConfig;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 类名:MigrationHelper
* 类描述:用于数据库升级的工具类
* 创建日期: 2017/9/26.
* 版本:V1.0
*/
public class MigrationHelper {
/**
* 调用升级方法
* @param db
* @param daoClasses 一系列dao.class
*/
public static void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//1 新建临时表
generateTempTables(db, daoClasses);
//2 创建新表
createAllTables(db, false, daoClasses);
//3 临时表数据写入新表,删除临时表
restoreData(db, daoClasses);
}
/**
* 生成临时表,存储旧的表数据
* @param db
* @param daoClasses
*/
private static void generateTempTables(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;
if (!checkTable(db,tableName))
continue;
String tempTableName = daoConfig.tablename.concat("_TEMP");
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("alter table ")
.append(tableName)
.append(" rename to ")
.append(tempTableName)
.append(";");
db.execSQL(insertTableStringBuilder.toString());
}
}
/**
* 检测table是否存在
* @param db
* @param tableName
*/
private static Boolean checkTable(Database db,String tableName){
StringBuilder query = new StringBuilder();
query.append("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='").append(tableName).append("'");
Cursor c = db.rawQuery(query.toString(), null);
if (c.moveToNext()){
int count = c.getInt(0);
if(count>0){
return true;
}
return false;
}
return false;
}
/**
* 删除所有旧表
* @param db
* @param ifExists
* @param daoClasses
*/
private static void dropAllTables(Database db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "dropTable", ifExists, daoClasses);
}
/**
* 创建新的表结构
* @param db
* @param ifNotExists
* @param daoClasses
*/
private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "createTable", ifNotExists, daoClasses);
}
/**
* 创建根删除都在NoteDao声明了,可以直接拿过来用
* dao class already define the sql exec method, so just invoke it
*/
private static void reflectMethod(Database db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
if (daoClasses.length < 1) {
return;
}
try {
for (Class cls : daoClasses) {
//根据方法名,找到声明的方法
Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
method.invoke(null, db, isExists);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 临时表的数据写入新表
* @param db
* @param daoClasses
*/
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 (!checkTable(db,tempTableName))
continue;
// 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(";");
db.execSQL(insertTableStringBuilder.toString());
}
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(dropTableStringBuilder.toString());
}
}
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;
}
}
网友评论