Android数据库存储GreenDao 3.0(一对多 多对多关联) 详解实例

 今天一大早收到GreenDao 3.0 正式发布的消息,自从2014年接触GreenDao至今,项目中一直使用GreenDao框架处理数据库操作,本人使用数据库路线 Sqlite---->OrmLite---->GreenDao

GreenDao 介绍:

    greenDAO是一个对象关系映射(ORM)的框架,能够提供一个接口通过操作对象的方式去操作关系型数据库,它能够让你操作数据库时更简单、更方便。如下图所示:

 

GreenDao 优点:

  • 性能高,号称Android最快的关系型数据库

  • 内存占用小
  • 库文件比较小,小于100K,编译时间低,而且可以避免65K方法限制

  • 支持数据库加密  greendao支持SQLCipher进行数据库加密 有关SQLCipher可以参考这篇博客Android数据存储之Sqlite采用SQLCipher数据库加密实战

  • 简洁易用的API
  • 总结:效率很高,插入和更新的速度是sqlite的2倍,加载实体的速度是ormlite(也是一个数据库框架)的4.5倍,目前git上一直在做更新维护,start数量为9000多

GreenDao 3.0改动:

   使用过GreenDao的同学都知道,3.0之前需要通过新建GreenDaoGenerator工程生成Java数据对象(实体)和DAO对象,非常的繁琐而且也加大了使用成本。

GreenDao  3.0最大的变化就是采用注解的方式通过编译方式生成Java数据对象和DAO对象。

 

另外GreenDao支持Protocol buffers协议数据的直接存储 ,如果通过protobuf协议和服务器交互,不需要任何的映射。

Protocol Buffers协议:以一种高效可扩展的对结构化数据进行编码的方式。google内部的RPC协议和文件格式大部分都是使用它。
RPC:远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

 

  • 首先bean类,会自动生成一些方法,比如get set 构造方法 getSession等

 

 

GreenDao 3.0使用方式:

 1.)在build.gradle添加如下配置

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.0.0'
    }
}

apply plugin: 'org.greenrobot.greendao'


dependencies {
    compile 'org.greenrobot:greendao:3.0.1'
}

2.)新建实体

@Entity
public class User {
    @Id
    private Long id;
    private String name;
    private int age;

   //下面省去了 setter/getter
}

此时编译一下自动生成DaoMaster 、DaoSession、Dao,如图所示 默认位置:

3.)Gradle 插件配置

 

比如上面想指定生成DaoMaster 、DaoSession、Dao位置

greendao {
    targetGenDir 'src/main/java'
}

reenDao 3.0简单实战:

      1.)通过上面使用方式我们可以获取DaoMaster 、DaoSession、Dao类

 这里声明一个数据库管理者单例

public class DBManager {
    private final static String dbName = "test_db";
    private static DBManager mInstance;
    private DaoMaster.DevOpenHelper openHelper;
    private Context context;

    public DBManager(Context context) {
        this.context = context;
        openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
    }

    /**
     * 获取单例引用
     *
     * @param context
     * @return
     */
    public static DBManager getInstance(Context context) {
        if (mInstance == null) {
            synchronized (DBManager.class) {
                if (mInstance == null) {
                    mInstance = new DBManager(context);
                }
            }
        }
        return mInstance;
    }
}

2.)获取可读可写数据库

  可读数据库

  /**
     * 获取可读数据库
     */
    private SQLiteDatabase getReadableDatabase() {
        if (openHelper == null) {
            openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
        }
        SQLiteDatabase db = openHelper.getReadableDatabase();
        return db;
    }

   可写数据库

    /**
     * 获取可写数据库
     */
    private SQLiteDatabase getWritableDatabase() {
        if (openHelper == null) {
            openHelper = new DaoMaster.DevOpenHelper(context, dbName, null);
        }
        SQLiteDatabase db = openHelper.getWritableDatabase();
        return db;
    }

3.)插入数据

 /**
     * 插入一条记录
     *
     * @param user
     */
    public void insertUser(User user) {
        DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
        DaoSession daoSession = daoMaster.newSession();
        UserDao userDao = daoSession.getUserDao();
        userDao.insert(user);
    }

    /**
     * 插入用户集合
     *
     * @param users
     */
    public void insertUserList(List<User> users) {
        if (users == null || users.isEmpty()) {
            return;
        }
        DaoMaster daoMaster = new DaoMaster(getWritableDatabase());
        DaoSession daoSession = daoMaster.newSession();
        UserDao userDao = daoSession.getUserDao();
        userDao.insertInTx(users);
    }
一对多的使用

多表关联

使用GreenDao如何实现创建表、关联表(一对一,一对多,多对多)?

 

   针对数据库多表之间的关联关系,“一对多”使用的较为频繁,

而“一对一”和“多对多”使用较少,

因此本文旨在通过示例来带领大家更快的理解和使用greendao来处理“一对多”的关系。

 

 

步骤:

1. 导入Gradle插件和Dao代码生成

2、创建存储对象实体类

使用GreenDao存储数据只需要在存储数据类前面声明@Entity注解就让GreenDao为其生成必要的代码:

 

3.GreenDao初始化

我们可以在Application中维持一个全局的会话。我们在Applicaiton进行数据库的初始化操作:

 

4. 使用GreenDao实现增删改查

 

 应用场景:从照片中找出包含有用户人脸的照片,并保存该照片中的人脸特征、使用该特征和用户人脸特征对比,满足条件,照片就保存到该用户表里

 

添加GreenDao插件到项目中 

 

classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'

buildscript {

    

    repositories {

    ..............................................................

    }

    dependencies {

     .............................................................

        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'

    

    }

}

添加依赖到模块中

 

apply plugin: 'org.greenrobot.greendao' // apply plugin

 

dependencies {

............

    implementation 'org.greenrobot:greendao:3.2.2' // add library

}

实体类

 

@Entity

public class PortraitPhoto {

 

  @Id(autoincrement = true)

//id参数类型要使用Long,不然保存后id一直为0

  private Long id;

  private String name;

  private String path;

  private int faceCount;

  private String sdCardId;

//一对多的关系,一张照片可能有多个人,所以会有多个人脸特征(一张脸一个特征)

  @ToMany(referencedJoinProperty = "portraitPhotoId")

  List<Feature> featureList;

}

 

 

@Entity

public class Feature {

  @Id(autoincrement = true)

  private Long id;

  private Long portraitPhotoId;

  private byte[] features;

}

用于保存用户id ,因为用户id是唯一的,所以我用它来做UserPhoto表的主键,也就是userId,下面保存多对多关系时会用到

 

 

@Entity public class UserPhoto {

  @Id private Long id;

//照片和用户是多对多的关系

  @ToMany

  @JoinEntity(entity = PhotoJoinUser.class, sourceProperty = "userId", targetProperty = "portraitPhotoId")

  private List<PortraitPhoto> portraitphotoList;

多对多的关系,需要建立中间表,一张照片里面可能有多个用户,一个用户可以出现多张照片里面 

 

@Entity

public class PhotoJoinUser {

 

  @Id

  private Long id;

  private Long portraitPhotoId;

  private Long userId;

}

 

--------------------- 

一对多:比如一个人有一群朋友(朋友也是person)

按照我们上面的思路,那么person类里面就应该有一个外键指向自身的主键id

 

 

2. 一对多

问题描述:

现有一个实体类Card(证件) 
  其中有属性:id、证件名称、姓名、卡号、备注,以及证件的照片(1到四张),其中证件的照片可能有多张,故需要用到一对多的关系。

 

一个人拥有多个信用卡
做法:

  1. 在我们在Student中设置@ToMany(referencedJoinProperty = "id");
  2. 我们在CreditCard中设置编写对应的id主键;
  3.  
#person类中
 private Long id;//自身id
 private Long fid;//外键关联id
 @ToMany(referencedJoinProperty ="fid" )//指定与之关联的其他类的id
 private List<Person> friends;

 


如果一个人的id是1,他有3个朋友,那么friends里面person的fid都是1,这样这个人调用getFriends就能拿到自己的3个朋友。主要就是通过设置id来构建关联关系的。

多对多:(需要一张中间表格)

多对多的话就比较复杂,不是两个表或者两个对象直接关联,而是要通过一个“第三者”

 

一个学生有多个老师,老师有多个学生。
做法:

  1. 我们需要创建一个学生老师管理器(StudentAndTeacherBean),用来对应学生和老师的ID;

  2. 我们需要在学生对象中,添加注解:

    @ToMany
    @JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = "studentId",targetProperty = "teacherId")
    List<Teacher> teacherList;

  3. 我们需要在老师对象中,添加注解:@ToMany

 

#person类中
    @Id(autoincrement = true)
    private Long id;
    private String name;
   // 对多,@JoinEntity注解:entity 中间表;sourceProperty 实体属性;targetProperty 外链实体属性
    @ToMany
    @JoinEntity(
            entity = JoinStudentToPerson.class,
            sourceProperty = "pid",
            targetProperty = "sid"
    )
    private List<Student> students;
//中间表   “第三者”
@Entity
public class JoinStudentToPerson {
   @Id(autoincrement = true)
    private Long id;
    //和person关联的id
    private Long pid;
    //和student关联的id
    private Long sid;
}
@Entity
public class Student {
    @Id
    private Long id;
    private String name;
    // 对多,@JoinEntity注解:entity 中间表;sourceProperty 实体属性;targetProperty 外链实体属性
    @ToMany
    @JoinEntity(
            entity = JoinStudentToPerson.class,
            sourceProperty = "sid",
            targetProperty = "pid"
    )
    private List<Person> persons;
}

然后测试代码

  private void test() {

        PersonDao personDao = GreendaoHelper.getDaoSession().getPersonDao();
        HeadDao headDao = GreendaoHelper.getDaoSession().getHeadDao();
        StudentDao studentDao = GreendaoHelper.getDaoSession().getStudentDao();
        JoinStudentToPersonDao spDao = GreendaoHelper.getDaoSession().getJoinStudentToPersonDao();

//        Head head = new Head();
//        head.setId(14l);
//        head.setName("head");

        Person p1 = new Person();
        p1.setId(1l);
        p1.setName("jafir1");
        Person p2 = new Person();
        p2.setId(2l);
        p2.setName("jafir2");
        Person p3 = new Person();
        p3.setId(3l);
        p3.setName("jafir3");


        Student stu1 = new Student();
        stu1.setId(1l);
        stu1.setName("stu1");
        Student stu2 = new Student();
        stu2.setId(2l);
        stu2.setName("stu2");
        Student stu3 = new Student();
        stu3.setId(3l);
        stu3.setName("stu3");


        // 模拟 多对多关系
        // 假如 p1有3个:stu1\stu2\stu3
        //  stu1 stu2 stu3 都有2个 :p1\p2

        //p1有stu1 stu2 stu3     那么反过来stu123都有p1
        JoinStudentToPerson sp1 = new JoinStudentToPerson();
        sp1.setPid(1l);
        sp1.setSid(1l);
        JoinStudentToPerson sp2 = new JoinStudentToPerson();
        sp2.setPid(1l);
        sp2.setSid(2l);
        JoinStudentToPerson sp3 = new JoinStudentToPerson();
        sp3.setPid(1l);
        sp3.setSid(3l);

        //p2有stu1 stu2 stu3     那么反过来stu123都有p2
        JoinStudentToPerson sp4 = new JoinStudentToPerson();
        sp4.setPid(2l);
        sp4.setSid(1l);
        JoinStudentToPerson sp5 = new JoinStudentToPerson();
        sp5.setPid(2l);
        sp5.setSid(2l);
        JoinStudentToPerson sp6 = new JoinStudentToPerson();
        sp6.setPid(2l);
        sp6.setSid(3l);

        spDao.insert(sp1);
        spDao.insert(sp2);
        spDao.insert(sp3);
        spDao.insert(sp4);
        spDao.insert(sp5);
        spDao.insert(sp6);

        personDao.insert(p1);
        personDao.insert(p2);
        personDao.insert(p3);

        studentDao.insert(stu1);
        studentDao.insert(stu2);
        studentDao.insert(stu3);

//        headDao.insert(head);
//        personDao.insert(p1);

        List<Person> persons = personDao.queryBuilder().build().list();

        for (Person person : persons) {
            Log.d("debug","person:"+person.toString());
        }
    }

注意:在person和student的toString里面不能都写getStudents getPerson,不然会你调我,我调你,然后死循环,出现log打印Stack Overflow。只能单独打印测试结果

//打印person的测试结果
Person{
students=[
Student{name='stu1', id=1}, 
Student{name='stu2', id=2},
 Student{name='stu3', id=3}], 
head=null, hid=null, name='jafir1', id=1}

Person{
students=[
Student{name='stu1', id=1}, 
Student{name='stu2', id=2}, 
Student{name='stu3', id=3}],
 head=null, hid=null, name='jafir2', id=2}

Person{
students=[], 
head=null, hid=null, name='jafir3', id=3}

students的测试结果就不列出了。
总之实现起来就是这样。

 

 

greendao的原理:

将项目从greenDAO从2.x版本升级到最新的3.2版本,最大变化是可以用注解代替以前的java生成器。实现这点,需要引入相应的gradle插件,具体配置参考官网。


 

图1

 

图1是从官网盗来的主结构图,注解Entity后,只需要build工程,DaoMaster、DaoSession和对应的Dao文件就会自动生成。分析greenDAO的实现原理,将会依照这幅图的路线入手,分析各个部分的作用,最重要是研究清楚greenDAO是怎样调用数据库的CRUD。

GreenDao向SQLite数据库提供了一个对象导向的接口,它为用户省下了很多重复的工作,而且提供了简便的操作接口。
为了使用GreenDao,需要在新建一个Java工程(工程中需要导入greendao-generator-x.x.x.jar,freemarker-x.x.xx.jar),根据GreenDao的规则在其中描述数据库的表结构,运行之后它会构建你的实体模型和DAO工具类。具体包括:

DaoMaster:

  • 持有数据库对象(SQLiteDatabase) ,并管理一些DAO类(不是对象)
  • 能够创建和删除数据库表
  • 它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper的实现类,用于创建SQLite数据库的模式

DaoSession:

  • 管理制定模式下所有可用的DAO对象
  • 能对实体进行插入、加载、更新、刷新、删除操作。

DAO:

  • 每个实体都有一个DAO,相对于DaoSession,它有更多的方法,比如:加载全部、InsertTx

Entity

  • 可持久化的对象,由generator 生成。相当于数据库中的一张表,所有字段都是使用标准的Java对象的属性

通过generator生成的这些工具类,你就可以在自己的Android工程中对进行数据库操作,完全不需要写任何SQL语句。

 

 

参考地址:

https://www.jianshu.com/p/53083f782ea2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值