1、配置
分为以下几步:
1)app级别的build.gradle(app)中进行配置:
id 'org.greenrobot.greendao' // apply plugin
...
greendao {
schemaVersion 1 //数据库版本号
daoPackage 'com.example.greendaotest.greendao' //填写自己的包名+文件夹test
// 设置DaoMaster、DaoSession、Dao 包名
targetGenDir 'src/main/java/'//设置DaoMaster、DaoSession、Dao目录
//主要此时的这行代码如果在生成之后没有出现以下的三个类那么采取注释掉
}
...
implementation 'org.greenrobot:greendao:3.3.0' // add library
2)项目级别的build.gradle(项目)中进行配置:
mavenCentral() // add repository
...
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.1' // add plugin
3)新建一个学生类,并重新编译生成xxxDao文件
@Entity
public class Student {
@Id(autoincrement = true)@Unique
Long id;//long类型才能自增
String name;
int age;
public Student() {
}
}
4)重新编译项目,GreenDao会自动生成DaoMaster,DaoSession,StudentDao三个文件,并在Student.class中添加get、set等方法。
- DaoMaster::DaoMaster保存数据库对象(SQLiteDatabase)并管理特定模式的DAO类(而不是对象)。它有静态方法来创建表或删除它们。它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper实现,它们在SQLite数据库中创建模式。
- DaoSession:管理特定模式的所有可用DAO对象,您可以使用其中一个getter方法获取该对象。DaoSession还提供了一些通用的持久性方法,如实体的插入,加载,更新,刷新和删除。
- XXXDao:数据访问对象(DAO)持久存在并查询实体。对于每个实体,greenDAO生成DAO。它具有比DaoSession更多的持久性方法,例如:count,loadAll和insertInTx。
5)之后需要声明一个Application的子类(MyAPP):
public class MyAPP extends Application {
@Override
public void onCreate() {
super.onCreate();
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "school.db");
SQLiteDatabase writableDatabase = devOpenHelper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(writableDatabase);
daoSession = daoMaster.newSession();
}
public DaoSession getDaoSession() {
return daoSession;
}
private DaoSession daoSession;
}
声明Application的子类需要在配置文件中声明:
6)接下来就可以进行数据库的相关操作了,例如:
public class MainActivity extends AppCompatActivity {
private DaoSession daoSession;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
daoSession = ((MyAPP) getApplication()).getDaoSession();
//插入一条数据
Student student = new Student();
student.name = "张三";
student.age = 18;
daoSession.insert(student);
Log.d("lxl", "插入数据: "+student.toString());
//查询
List<Student> students = daoSession.loadAll(Student.class);
Iterator<Student> iterator = students.iterator();
for (Iterator<Student> it = iterator; it.hasNext(); ) {
Student s = it.next();
Log.d("lxl", "数据库数据: "+s.name);
}
}
}
数据库操作
1)增 insert()、insertOrReplace()
insert()是向数据库中插入一条数据,而insertOrReplace()则是没有即插入,有的话直接替换。
2)删 delete()、deleteAll()
delete()删除一条数据、deleteAll()删除所有数据。
daoSession.delete(student);
daoSession.deleteAll(Student.class);
3)改
@Override
public void updataData(Student s) {
DaoSession daoSession = ((MyAPP) getApplication()).getDaoSession();
daoSession.update(s);
}
Student stu = students.get(3);
stu.setAge(28);
stu.setName("李四");
daoSession.update(stu);
Log.d("lxl", stu.toString());
4)查:
- loadAll():查询所有数据。
- queryRaw():根据条件查询。
- queryBuilder() : 方便查询的创建。
List<Student> students = daoSession.loadAll(Student.class);
Iterator<Student> iterator = students.iterator();
for (Iterator<Student> it = iterator; it.hasNext(); ) {
Student s = it.next();
Log.d("lxl", "数据库数据: "+s.name);
}
students = daoSession.queryRaw(Student.class, " where age < ?", String.valueOf(40));
for(Student student:students){
Log.d("lxl", student.toString());
}
queryBuilder()
语法:
- where(WhereCondition cond, WhereCondition… condMore): 查询条件,参数为查询的条件!
- or(WhereCondition cond1, WhereCondition cond2, WhereCondition… condMore): 嵌套条件或者,用法同or。
- and(WhereCondition cond1, WhereCondition cond2, WhereCondition… condMore): 嵌套条件且,用法同and。
- join(Property sourceProperty, Class destinationEntityClass):多表查询,后面会讲。
输出结果有四种方式,选择其中一种最适合的即可,list()返回值是List,而其他三种返回值均实现Closeable,需要注意的不使用数据时游标的关闭操作: - list ()所有实体都加载到内存中。结果通常是一个没有魔法的 ArrayList。最容易使用。
- listLazy ()实体按需加载到内存中。首次访问列表中的元素后,将加载并缓存该元素以供将来使用。必须关闭。
- listLazyUncached ()实体的“虚拟”列表:对列表元素的任何访问都会导致从数据库加载其数据。必须关闭。
- listIterator ()让我们通过按需加载数据(懒惰)来迭代结果。数据未缓存。必须关闭。
- orderAsc() 按某个属性升序排;
- orderDesc() 按某个属性降序排;
SQL语句缩写:
- eq():“equal (‘=?’)” 等于;
- notEq() :“not equal (‘<>?’)” 不等于;
- like():" LIKE ?" 值等于;
- between():" BETWEEN ? AND ?" 取中间范围;
- in():" IN (" in命令;
- notIn():" NOT IN (" not in 命令;
- gt():“>?” 大于;
- lt():"<? " 小于;
- ge():“>=?” 大于等于;
- le():"<=? " 小于等于;
- isNull():" IS NULL" 为空;
- isNotNull():" IS NOT NULL" 不为空;
1)简单查询:
首先创建一个daoSession对象,之后利用queryBuilder()指定操作的数据库,最后利用where()方法进行查找,查找出的类型存入list列表。
DaoSession daoSession1 = ((MyAPP) getApplication()).getDaoSession();
QueryBuilder<Student> studentQueryBuilder = daoSession1.queryBuilder(Student.class);
List<Student> list = studentQueryBuilder.where(StudentDao.Properties.Name.eq("李四")).list();
for (Student stu : list){
Log.d("lxl", "查到的数据:"+stu.toString());
}
2)原始查询:
利用new WhereCondition.StringCondition()写入sql查询语句,需要利用in字符。
DaoSession daoSession1 = ((MyAPP) getApplication()).getDaoSession();
QueryBuilder<Student> studentQueryBuilder = daoSession1.queryBuilder(Student.class);
Query<Student> build = studentQueryBuilder.where(new WhereCondition.StringCondition("name in (select name from student where age > 100)")).build();
List<Student> list = build.list();
for (Student stu : list){
Log.d("lxl", "查到的数据:"+stu.toString());
}
3)嵌套查询
DaoSession daoSession1 = ((MyAPP) getApplication()).getDaoSession();
QueryBuilder<Student> studentQueryBuilder = daoSession1.queryBuilder(Student.class);
List<Student> list = studentQueryBuilder.where(studentQueryBuilder.or(StudentDao.Properties.Name.eq("李四"),StudentDao.Properties.Age.ge(110))).list();
for (Student stu : list){
Log.d("lxl", "查到的数据:"+stu.toString());
}
4)多次执行查找:
避免每次创建新的SQL查询语句。
DaoSession daoSession1 = ((MyAPP) getApplication()).getDaoSession();
QueryBuilder<Student> studentQueryBuilder = daoSession1.queryBuilder(Student.class);
Query<Student> query = studentQueryBuilder.where(StudentDao.Properties.Age.ge(110)).limit(5).offset(2).build();
List<Student> list = query.list();
for (Student stu : list){
Log.d("lxl", "查到的数据:"+stu.toString());
}
query.setParameter(0,108);
list = query.list();
for (Student stu : list){
Log.d("lxl", "查到的数据:"+stu.toString());
}
5)使用QueryBuilder进行批量删除操作
使用QueryBuilder进行批量删除操作,不会删除单个实体,但会删除符合某些条件的所有实体。要执行批量删除,请创建QueryBuilder,调用其 buildDelete()方法,然后执行返回的 DeleteQuery。
public boolean deleteItem(){
DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
QueryBuilder<Student> where = daoSession.queryBuilder(Student.class).where(StudentDao.Properties.Id.gt(5));
DeleteQuery<Student> deleteQuery = where.buildDelete();
deleteQuery.executeDeleteWithoutDetachingEntities();
return false;
}
6)多线程中的QueryBuilder:
如果在多个线程中使用查询,则必须调用 forCurrentThread ()以获取当前线程的Query实例。Query的对象实例绑定到构建查询的拥有线程。
可以安全地在Query对象上设置参数,而其他线程不会干扰。如果其他线程尝试在查询上设置参数或执行绑定到另一个线程的查询,则会抛出异常。像这样,您不需要同步语句。实际上,您应该避免锁定,因为如果并发事务使用相同的Query对象,这可能会导致死锁。
每次调用forCurrentThread ()时, 参数都会在使用其构建器构建查询时设置为初始参数。
注解
1. @Entity注解
@Entity是GreenDao必不可少的注解,只有在实体类中使用了@Entity注解GreenDao才会创建对应的表。当然我们也可以使用@Entity配置一些细节:
- schema:如果你有多个架构,你可以告诉GreenDao当前属于哪个架构。
- active:标记一个实体处于活跃状态,活动实体有更新、删除和刷新方法。
- nameInDb:在数据中使用的别名,默认使用的是实体的类名。
- indexes:标记如果DAO应该创建数据库表(默认为true),如果您有多个实体映射到一个表,或者表的创建是在greenDAO之外进行的,那么将其设置为false。
- createInDb:标记创建数据库表。
- generateGettersSetters:如果缺少,是否应生成属性的getter和setter方法。
@Entity(
schema = "myschema",
active = true,
nameInDb = "AWESOME_USERS",
indexes = {
@Index(value = "message DESC", unique = true)
},
createInDb = false,
generateConstructors = true,
generateGettersSetters = true
)
public class Student{
……
}
2. 基础属性注解(@Id,@Property,@NotNull,@Transient)
1) @Id
@Id注解选择 long / Long属性作为实体ID。在数据库方面,它是主键。参数autoincrement = true 表示自增,id不给赋值或者为赋值为null即可(这里需要注意,如果要实现自增,id必须是Long,为long不行!)。
@Entity
public class Student {
@Id(autoincrement = true)
Long id;
……
}
@Property
允许您定义属性映射到的非默认列名。如果不设置,GreenDAO将以SQL-ish方式使用字段名称(大写,下划线而不是camel(驼峰)情况,例如 name将成为 NAME)。注意:您当前只能使用内联常量来指定列名。
@Entity
public class Student {
@Id(autoincrement = true)
Long id;
@Property (nameInDb="name") //设置了,数据库中的表格属性名为"name",如果不设置,数据库中表格属性名为"NAME"
String name;
……
}
@NotNull :设置数据库表当前列不能为空 。
@Transient :添加次标记之后不会生成数据库表的列。标记要从持久性中排除的属性。将它们用于临时状态等。或者,您也可以使用Java中的transient关键字。
3. 索引注解
- @Index:使用@Index作为一个属性来创建一个索引,通过name设置索引别名,也可以通过unique给索引添加约束。
- @Unique:向索引添加UNIQUE约束,强制所有值都是唯一的。
@Entity
public class Student {
@Id(autoincrement = true)
Long id;
@Property(nameInDb="name")
@Index(unique = true)
String name;
……
}
注意: 上面这种情况,约定name为唯一值,向数据库中通过insert方法继续添加已存在的name数据,会抛异常:
10-08 20:59:46.274 31939-31939/com.example.aserbao.aserbaosandroid E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.aserbao.aserbaosandroid, PID: 31939
android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: STUDENT.name (Sqlite code 2067), (OS error - 2:No such file or directory)
……
若使用insertOrReplace()方法添加数据,当前数据库中不会有重复的数据,但是重复的这条数据的id会被修改!若项目中有用到id字段进行排序的话,这一点需要特别注意。
4. 关系注解
关系型注解GreenDao中主要就两个:
- @ToOne:定义与另一个实体(一个实体对象)的关系
- @ToMany:定义与多个实体对象的关系
一对一,一对多,多对多关系表的创建
平常项目中,我们经常会使用到多表关联,如文章开头所说的数据库表结构设置的那样!接下来我们来讲如何通过GreenDao实现多表关联。
1) 一对一
一个学生对应一个身份证号:
做法:
- 我们在Student中设置一个注解@ToOne(joinProperty = “name”)
- 在创建Student的时候,将对应的数据传递给IdCard;
学生Student代码:
class Student {
(autoincrement = true)
Long id;
int studentNo;//学号
int age; //年龄
String telPhone;//手机号
String sex; //性别
String name;//姓名
String address;//家庭住址
String schoolName;//学校名字
String grade;//几年级
(joinProperty = "name")
IdCard student;
……getter and setter ……
}
public
身份证IdCard代码:
class IdCard {
String userName;//用户名
String idNo;//身份证号
……getter and setter ……
}
public
insert一组数据:
public void addStudent(){
DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
Student student = new Student();
student.setStudentNo(i);
int age = mRandom.nextInt(10) + 10;
student.setAge(age);
student.setTelPhone(RandomValue.getTel());
String chineseName = RandomValue.getChineseName();
student.setName(chineseName);
if (i % 2 == 0) {
student.setSex("男");
} else {
student.setSex("女");
}
student.setAddress(RandomValue.getRoad());
student.setGrade(String.valueOf(age % 10) + "年纪");
student.setSchoolName(RandomValue.getSchoolName());
daoSession.insert(student);
//插入对应的IdCard数据
IdCard idCard = new IdCard();
idCard.setUserName(userName);
idCard.setIdNo(RandomValue.getRandomID());
daoSession.insert(idCard);
}
2)一对多
一个人拥有多个信用卡
做法:
- 在我们在Student中设置@ToMany(referencedJoinProperty = “studentId”);
- 我们在CreditCard中设置编写对应的id主键;
Student的代码:
@Entity
public class Student {
@Id(autoincrement = true)
Long id;
@Unique
int studentNo;//学号
int age; //年龄
String telPhone;//手机号
String sex; //性别
String name;//姓名
String address;//家庭住址
String schoolName;//学校名字
String grade;//几年级
@ToMany(referencedJoinProperty = "studentId) // 这个studentId是对应在CreditCard中的studentId
List<CreditCard> creditCardsList;
……getter and setter ……
}
CreditCard的代码:
class CreditCard {
Long id;
Long studentId;
Long teacherId;
String userName;//持有者名字
String cardNum;//卡号
String whichBank;//哪个银行的
int cardType;//卡等级,分类 0 ~ 5
……getter and setter ……
}
public
添加数据代码:
public void addStudent(){
DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
Student student = new Student();
student.setStudentNo(i);
int age = mRandom.nextInt(10) + 10;
student.setAge(age);
student.setTelPhone(RandomValue.getTel());
String chineseName = RandomValue.getChineseName();
student.setName(chineseName);
if (i % 2 == 0) {
student.setSex("男");
} else {
student.setSex("女");
}
student.setAddress(RandomValue.getRoad());
student.setGrade(String.valueOf(age % 10) + "年纪");
student.setSchoolName(RandomValue.getSchoolName());
daoSession.insert(student);
//插入对应的CreditCard数据
for (int j = 0; j < random.nextInt(5) + 1 ; j++) {
CreditCard creditCard = new CreditCard();
creditCard.setUserId(id);
creditCard.setUserName(userName);
creditCard.setCardNum(String.valueOf(random.nextInt(899999999) + 100000000) + String.valueOf(random.nextInt(899999999) + 100000000));
creditCard.setWhichBank(RandomValue.getBankName());
creditCard.setCardType(random.nextInt(10));
daoSession.insert(creditCard);
}
}
3.)多对多
一个学生有多个老师,老师有多个学生。
做法:
- 我们需要创建一个学生老师管理器(StudentAndTeacherBean),用来对应学生和老师的ID;
- 我们需要在学生对象中,添加注解:@ToMany
@JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = “studentId”,targetProperty = “teacherId”)
List teacherList; - 我们需要在老师对象中,添加注解:@ToMany
@JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = "teacherId",targetProperty = "studentId")
List<Student> studentList;
StudentAndTeacherBean代码:
@Entity
public class StudentAndTeacherBean {
@Id(autoincrement = true)
Long id;
Long studentId;//学生ID
Long teacherId;//老师ID
……getter and setter ……
}
Student 代码:
class Student {
(autoincrement = true)
Long id;
int studentNo;//学号
int age; //年龄
String telPhone;//手机号
String sex; //性别
String name;//姓名
String address;//家庭住址
String schoolName;//学校名字
String grade;//几年级
(entity = StudentAndTeacherBean.class,sourceProperty = "studentId",targetProperty = "teacherId")
List<Teacher> teacherList;
……getter and setter ……
}
public
Teacher代码:
class Teacher {
(autoincrement = true)
Long id;
int teacherNo;//职工号
int age; //年龄
String sex; //性别
String telPhone;
String name;//姓名
String schoolName;//学校名字
String subject;//科目
(entity = StudentAndTeacherBean.class,sourceProperty = "teacherId",targetProperty = "studentId")
List<Student> studentList;
……getter and setter ……
}
public
数据添加:
public void addData(){
Student student = new Student();
student.setStudentNo(i);
int age = mRandom.nextInt(10) + 10;
student.setAge(age);
student.setTelPhone(RandomValue.getTel());
String chineseName = RandomValue.getChineseName();
student.setName(chineseName);
if (i % 2 == 0) {
student.setSex("男");
} else {
student.setSex("女");
}
student.setAddress(RandomValue.getRoad());
student.setGrade(String.valueOf(age % 10) + "年纪");
student.setSchoolName(RandomValue.getSchoolName());
daoSession.insert(student);
Collections.shuffle(teacherList);
for (int j = 0; j < mRandom.nextInt(8) + 1; j++) {
if(j < teacherList.size()){
Teacher teacher = teacherList.get(j);
StudentAndTeacherBean teacherBean = new StudentAndTeacherBean(student.getId(), teacher.getId());
daoSession.insert(teacherBean);
}
}
}
数据库的升级
GreenDao的OpenHelper下有个 onUpgrade(Database db, int oldVersion, int newVersion)方法,当设置的数据库版本改变时,在数据库初始化的时候就会回调到这个方法,我们可以通过继承OpenHelper重写onUpgrade方法来实现数据库更新操作:
GreenDao的升级思路:
- 创建临时表TMP_,复制原来的数据库到临时表中;
- 删除之前的原表;
- 创建新表;
- 将临时表中的数据复制到新表中,最后将TMP_表删除掉;
一篇技术好文之Android数据库 GreenDao的使用完全解析
GreenDao数据库加密
通过SQLCipher来进行加密处理
1、导入加密库文件
implementation 'net.zetetic:android-database-sqlcipher:3.5.6'
2、修改DaoSession的生成方式
//MyDaoMaster helper = new MyDaoMaster(this, "aserbaos.db"); //数据库升级写法
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "aserbao.db");
//SQLiteDatabase db = helper.getWritableDatabase(); //不加密的写法
Database db = helper.getEncryptedWritableDb("aserbao"); //数据库加密密码为“aserbao"的写法
DaoMaster daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();