android 数据库GreenDao的详细用法

Android几种常用存储数据库的区别,想查看区别的朋友请移步查看

前言

使用GreenDao主要有几大核心的类:
1.DaoMaster:它保存了sqlitedatabase对象以及操作DAO classes。其提供了一些创建和删除table的静态方法,其内部类OpenHelper和DevOpenHelper实现了SQLiteOpenHelper并创建数据库的框架。
2.DaoSession:会话层。操作具体的DAO对象,比如各种getter方法。
3.XXXDao:实际生成的xxDAO类,通常对应具体的java类,比如FoodsBeanDao等。其有更多的权限和方法来操作数据库元素。
4.XXXEntity:持久的实体对象。通常代表了一个数据库row的标准java properties。
先贴一下初始化代码过后再讲解

 //创建数据库foodlist.db"
  DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "foodlist.db", null);
 //获取可写数据库
  SQLiteDatabase db = helper.getWritableDatabase();
 //获取数据库对象
  DaoMaster daoMaster = new DaoMaster(db);
 //获取Dao对象管理者
  DaoSession daoSession = daoMaster.newSession();
配置工程:

(参考github上的版本号,此处我用的是3.2.2)
在Project的build.gradle文件中

dependencies {
    //GreenDao3依赖
    classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
}

在module的build.gradle文件中

apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
android {
sourceSets {
        main {
            java.srcDirs = ['src/main/java', 'src/main/java-gen']
        }
    }
}
greendao {
        schemaVersion 1  //数据库版本号
        generateTests false //设置为true以自动生成单元测试。
        daoPackage 'com.greendao.gen' // 设置DaoMaster、DaoSession、Dao 包名
        targetGenDir 'src/main/java-gen'  //应存储生成的单元测试的基本目录。默认为 src / androidTest / java。
    }
dependencies {
implementation 'org.greenrobot:greendao:3.2.2' // add library
}

注意:此处配置完后先不要运行程序,等把bean类写好后再编译

改写bean类:

以为项目中为例,看代码:

@Entity
public class AddFoodEntity {
    /**
     * foodType : 食物分类1
     * foods : [{"foodId":1,"foodName":"食物1","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":2,"foodName":"食物2","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":3,"foodName":"食物3","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":4,"foodName":"食物4","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":5,"foodName":"食物5","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":6,"foodName":"食物6","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":7,"foodName":"食物7","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":8,"foodName":"食物8","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":9,"foodName":"食物9","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0},{"foodId":10,"foodName":"食物10","foodType":"食物分类1","foodTag":"钙","foodIngredient":"营养成分","foodEnergy":234,"unit":"100g","foodWeight":0}]
     */
     
    private String foodType;
    @Convert(converter = FoodsBeanConverter.class, columnType = String.class)
    private List<FoodsBean> foods;
    
    public String getFoodType() {
        return foodType;
    }
    public void setFoodType(String foodType) {
        this.foodType = foodType;
    }
    public List<FoodsBean> getFoods() {
        return foods;
    }
    public void setFoods(List<FoodsBean> foods) {
        this.foods = foods;
    }
}

注意:此处的bean类跟往常的不相同,在类名上加注解@Entity,除此之外还加了一个@Convert(converter = FoodsBeanConverter.class, columnType = String.class),这是必须要加上的,否则编译的时候会报错。

上面代码的FoodsBeanConverter.class是哪里来的,,这里要自己写一个XXXConverter类,如下代码

public class FoodsBeanConverter implements PropertyConverter<List<FoodsBean>,String> {
    @Override
    public List<FoodsBean> convertToEntityProperty(String databaseValue) {
        if (databaseValue == null) {
            return null;
        }
        JsonArray jsonArray = new JsonParser().parse(databaseValue).getAsJsonArray();
        Gson gson = new Gson();
        ArrayList<FoodsBean> userBeanList = new ArrayList<>();
        //循环遍历
        for (JsonElement user : jsonArray) {
            //通过反射 得到UserBean.class
            FoodsBean userBean = gson.fromJson(user, FoodsBean.class);
            userBeanList.add(userBean);
        }
        return userBeanList;
//      return JSON.parseArray(databaseValue, EmpPointListEntity.class); //如果你是用fastjson解析数据就用这个,上方的是Gson
    }
    @Override
    public String convertToDatabaseValue(List<FoodsBean> entityProperty) {
        return new Gson().toJson(entityProperty);
//      return JSON.toJSONString(entityProperty); //fastjson 如果你是用fastjson解析数据就用这个,上方的是Gson
    }
}

注意:此处生成的是 Listfoods 集合,这里有人会问,那万一我这里是FoodsBean foods对象呢?那也要写一个对象的Converter实现PropertyConverter接口,,只要记住json里面不管是包裹的list还是Object都要写一个类实现PropertyConverter接口

这里写一个json里包裹的Object实体类,如下代码:

public class FoodsObjectConverter implements PropertyConverter<FoodsBean, String> {
    @Override
    public FoodsBean convertToEntityProperty(String databaseValue) {
//        return JSON.parseObject(databaseValue, FoodsBean.class); fastjson
        return new Gson().fromJson(databaseValue, FoodsBean.class);
    }
    @Override
    public String convertToDatabaseValue(FoodsBean entityProperty) {
//        return JSON.toJSONString(entityProperty); fastjson
        return new Gson().toJson(entityProperty, FoodsBean.class);
    }
}

可能你们也会问那FoodsBean这个实体类里需要写什么吗?其实这里面不需要写什么,跟往常一样,还是贴一下代码给你们看一下:

public class FoodsBean implements Parcelable{
    /**
     * foodId : 1
     * foodName : 食物1
     * foodType : 食物分类1
     * foodTag : 钙
     * foodIngredient : 营养成分
     * foodEnergy : 234
     * unit : 100g
     * foodWeight : 0
     */
    private int foodId;
    private String foodName;
    private String foodType;

    public int getFoodId() {
        return foodId;
    }

    public void setFoodId(int foodId) {
        this.foodId = foodId;
    }

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public String getFoodType() {
        return foodType;
    }

    public void setFoodType(String foodType) {
        this.foodType = foodType;
    }
}

就跟往常一样该实现Parcelable接口就实现,做到这一步就可以编译运行程序了,运行程序完成后你就可以注意一下目录结构,发现在build.gradle设置的greendao,就生成你设置的样子。

运用:

在Application 中设置添加:

public class MyGDApplication extends Application {
private DaoSession daoSession;
 
@Override
public void onCreate() {
    super.onCreate();
    //设置数据库
    setGreenDaoBase();
}
 
private void setGreenDaoBase() {
    //创建数据库topnews.db"
    DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "foodlist.db", null);
    //获取可写数据库
    SQLiteDatabase db = helper.getWritableDatabase();
    //获取数据库对象
    DaoMaster daoMaster = new DaoMaster(db);
    //获取Dao对象管理者
    daoSession = daoMaster.newSession();
}
 
public DaoSession getDaoSession() {
    return daoSession;
}
}

这跟最上面设置的是一样的,在Application里设置完后,要在你想要保存数据的Activity中保存数据和读取数据。

在该Activity中初始化获取DaoSession并进行相关的操作,已经创建好了DaoSession和Bean对象,编译后会自动生成我们的AddFoodEntityDao对象,可通过DaoSession获得:

 DaoSession daoSession =BaseApp.instance().getDaoSession();
 AddFoodEntityDao foodsDao=daoSession.getAddFoodEntityDao();

当你第一次加载页面请求数据成功时,就保存下来,下次再进页面就不用重复的请求数据了:

public void saveGreenDaoData() {
         //foodsDao是获取的数据库操作对象,先把数据库里有的先清除再保存;
        foodsDao.deleteAll();
        //此处的list是我实例化的 List<AddFoodEntity> list=new ArrayList<>();
        if(list!=null&&list.size()>0) {
            for (int i=0;i<list.size();i++){
                foodsDao.insert(list.get(i));
            }
        }
    }

当下次进来后就不需要去请求数据了,此时就读取保存在本地的数据:

 public void setGreenDaoNoNetwork(){
//        Query<AddFoodEntity> notesQuery = foodsDao.queryBuilder().build();
//        List<AddFoodEntity> dbTopList = notesQuery.list();
        List<AddFoodEntity> dbTopList = foodsDao.loadAll();
        //重新装配数据并刷新
        list.clear();
        list.addAll(dbTopList);
        //这里是我刷新列表用的adapter
        adapter.setRefreshData(list.get(0).getFoods()); 
    }

这里我是保存在本地的SharePrefences一个标识判断是否请求或者加载本地数据。到了这里一般的问题都会解决。有很多才接触到GreenDao的朋友,在bean类注解会出现编译不通过,主要是没理解到PropertyConverter接口没加上这段代码,导致编译失败,反正只要json里不管是集合还是对象bean,都要写一个类实现PropertyConverter接口。

补充
GreenDao如何进行多表联查?

GreenDao是通过表的主键和外键来查询的。

一对一查询:是通过 @ToOne(joinProerty = “外键ID”)注解,来声明外表

添加数据时主表的外键要和外表的主键一致 insertOrReplace方法添加表对象

查询通过queryBuilder().where(条件)方法

一对多:是通过 @ToMany(referencedJoinProperty = “外键ID”) 注解 来声明外表 因为是一对多的关系所以是外键表集合(List集合)

添加数据时主表的外键要和外表的主键一致 insertOrReplaceInTx方法添加集合表对象

查询通过queryBuilder().where(条件)方法GreenDao升级方式?

1.新建一个类继承OpenHelper(SQLite的OpenHelper)

2.重写里面的OnUpgrate

3.判断版本是否一致,一致执行原本的步骤,不一致执行版本升级

GreeDao 基本操作:

添加数据: foodsDao.insert();
删除数据: foodsDao.delete();
修改数据: foodsDao.update();
查询数据: foodsDao.loadAll();
条件查询: foodsDao.queryBuilder().where(FoodsBean.Properties.foodName.eq( “食物一” )) .list();

注意问题

1.当配置完运行程序后,在目录结构中java-gen文件里包的层级是递进的,或者里面的class类出现红色的j,这说明没成功,主要原因是写的bean有问题,上面的写得很清楚,可以重点看一下,最好加上’src/main/java-gen’。
2.如果不能自动生成,那就注意一下代码或者配置是否出错,按照上面的步骤实现,应该就不会错。
3.要使用其他关于数据的操作,得到foodsDao类的实体类就可以操作其他方法。
4.如有不懂得请留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值