今天一大早收到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到四张),其中证件的照片可能有多张,故需要用到一对多的关系。
一个人拥有多个信用卡
做法:
- 在我们在Student中设置@ToMany(referencedJoinProperty = "id");
- 我们在CreditCard中设置编写对应的id主键;
#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来构建关联关系的。
多对多:(需要一张中间表格)
多对多的话就比较复杂,不是两个表或者两个对象直接关联,而是要通过一个“第三者”
一个学生有多个老师,老师有多个学生。
做法:
-
我们需要创建一个学生老师管理器(StudentAndTeacherBean),用来对应学生和老师的ID;
-
我们需要在学生对象中,添加注解:
@ToMany
@JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = "studentId",targetProperty = "teacherId")
List<Teacher> teacherList; -
我们需要在老师对象中,添加注解:@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语句。
参考地址: