greenDao github地址
// 根目录下 build.gradle 文件:
buildscript {
repositories {
jcenter()
mavenCentral() // add repository
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin
}
}
// app项目 build.gradle 文件:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
android {
//...其他相关配置
//...compileSdkVersion / buildToolsVersion
//数据库配置项[必填]
greendao {
schemaVersion 1 //数据库版本号
daoPackage 'com.micro.testgreengao.greendao.gen' //自动生成的工具类包名
targetGenDir 'src/main/java' //自动代码生成路径
}
}
dependencies {
compile 'org.greenrobot:greendao:3.2.2' // add library
}
可参考配置:点这里
新建User.java文件,如下:
@Entity
public class User {
@Id(autoincrement = true)
@Property(nameInDb = "_id")
private Long _id ;
@Property(nameInDb = "_name" )
private String name ;
@Property(nameInDb = "_age")
private int age ;
@Generated()
public User() {}
这里声明一下,各种注解稍后解释,大家先看着就行,User的属性getter/setter方法不需要你生成,使用Ctrl+F9(或者Build -> make module)进行编译,然后在你所写的greendao/package路径下生成了三个文件:UserDao,DaoMaster,DaoSession,如下图(其他文件无视即可):
同时再回过头来,你会发现你的User.java文件也生成了getter/setter方法,那么说明自动生成已经凑效了。
下面就greenDao的注解,简单说明一下各个注解的含义:
@Entity 标识实体类,greenDAO会映射成sqlite的一个表,表名为实体类名的大写形式
@Id 标识主键,该字段的类型为long或Long类型,autoincrement设置是否自动增长
@Property 标识该属性在表中对应的列名称, nameInDb设置名称
@Transient 标识该属性将不会映射到表中,也就是没有这列
@NotNull 设置表中当前列的值不可为空
@Convert 指定自定义类型(@linkPropertyConverter)
@Generated greenDAO运行所产生的构造函数或者方法,被此标注的代码可以变更或者下次运行时清除
@Index 使用@Index作为一个属性来创建一个索引;定义多列索引(@link Entity#indexes())
@JoinEntity 定义表连接关系
@JoinProperty 定义名称和引用名称属性关系
@Keep 注解的代码段在GreenDao下次运行时保持不变 1.注解实体类:默认禁止修改此类 2.注解其他代码段,默认禁止修改注解的代码段
@OrderBy 指定排序
@ToMany 定义与多个实体对象的关系
@ToOne 定义与另一个实体(一个实体对象)的关系
@Unique 向数据库列添加了一个唯一的约束
注解需要在实际中运用,光这个我也是记不住的,写多了自然就记住了。
编写dao处理对象:
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(ctx,DB_NAME);
DaoMaster mDaoMaster = new DaoMaster(devOpenHelper.getWritableDb());
DaoSession mDaoSession = mDaoMaster.newSession();
具体代码可以参考这里;
新增操作
public void addData(User user) {
UserDao userDao = mDaoSession.getUserDao();
long insertId = userDao.insert(user);
LogUtils.d("insertId:"+insertId);
}
注意,User的初始化为:
User user = new User(null,"zhangsan",18);
User的Id应该设置为null,不然id默认为0,在自增主键情况下,插入数据将会报错。
同时还有以下方法:
userDao.insertWithoutSettingPk(user); //如果没有对象没有设置主键,使用这个方法,你基本上放进去的东西就废了,因为不知道怎么找出来:)
userDao.insertOrReplace(user); //如果主键一致,则替换为新的User对象
userDao.insertInTx(List); //批量插入User对象
删除
public void deleteUser(long _id) {
UserDao userDao = mDaoSession.getUserDao();
userDao.deleteByKey(_id);
LogUtils.d("delete User finished");
}
上面是使用primary_key删除的,还有以下删除方式:
userDao.deleteAll(); //删除全部
userDao.deleteByKeyInTx(List idList); //删除id为idList中的User
userDao.deleteInTx(List userList); //删除集合中的user
userDao.delete(user); //删除目标User[需要包含主键]
还可以使用条件删除,使用StringCondition模式,删除name=micro和age>20岁的人
QueryBuilder queryBuilder = mDaoSession.queryBuilder(User.class);;
WhereCondition.StringCondition stringCondition =
new WhereCondition.StringCondition("_name = 'micro'");
WhereCondition.StringCondition stringCondition1 =
new WhereCondition.StringCondition("_age > 20");
queryBuilder.where(stringCondition,stringCondition1).
buildDelete().executeDeleteWithoutDetachingEntities();
删除name=micro 或者 name>20的user
queryBuilder.whereOr(stringCondition,stringCondition1).buildDelete().executeDeleteWithoutDetachingEntities();
当然还可以使用Property进行删除:
QueryBuilder queryBuilder = mDaoSession.queryBuilder(User.class);
WhereCondition nameCondition = null ;
WhereCondition ageCondition = null;
Property[] properties = mDaoSession.getUserDao().getProperties();
for (Property p : properties) {
if(p.columnName.equals("_name")) {
nameCondition = p.eq("micro");
}
if(p.columnName.equals("_age")){
ageCondition = p.gt(20);
}
}
queryBuilder.whereOr(nameCondition,ageCondition).
buildDelete().executeDeleteWithoutDetachingEntities();
基本套路是一致的。
修改
基本代码如下:
public void updateUser(User user) {
UserDao userDao = mDaoSession.getUserDao();
userDao.update(user);
LogUtils.d("update user finished");
}
这里User中需要有主键,不然没法修改的。
当然还有
userDao.updateInTx(Iterator); //批量修改User
查找
这个查找是SQL中最复杂最难的一个科目,也是考验程序员经验的时候,面对大数据量的数据库,一条有经验的SQL往往能节省很多时间,到了我们android这里,就不扯了,greenDao已经做了很好的优化了,你只需要怎么查找就行了。
通过Id查找
这个最简单了,就不说了,代码为:
public void btnGetById(View view) {
User user = mDaoSession.load(User.class,2L);
if(null != user) {
mTvInfo.setText(user.toString());
}else{
Toast.makeText(this, "查询数据失败", Toast.LENGTH_SHORT).show();
}
}
通过原生SQL查找
这个一般在条件语句中用的比较多:
public void btnGetBySQL(View view) {
String sql = "select * from " + UserDao.TABLENAME + " where _age > ? " ;
Cursor cursor = mDaoSession.getDatabase().rawQuery(sql,new String[]{String.valueOf(23)});
while (null != cursor && cursor.moveToNext()) {
Long _id = cursor.getLong(cursor.getColumnIndex("_id"));
String name = cursor.getString(cursor.getColumnIndex("_name")) ;
int age = cursor.getInt(cursor.getColumnIndex("_age"));
//..... 原生的SQLite写法
}
}
通过StringCondition查找
这个其实也说过了,就是上面的删除项,其实删除就是你先查出来再删除啊:
public void btnGetByStringCondition(View view) {
QueryBuilder queryBuilder = mDaoSession.queryBuilder(User.class);
WhereCondition.StringCondition stringCondition =
new WhereCondition.StringCondition("_name = 'micro'");
WhereCondition.StringCondition stringCondition1 =
new WhereCondition.StringCondition("_age > 20");
List userList = queryBuilder.where(stringCondition,stringCondition1).
build().list();
Log.d("TAG","---->>>" + userList);
}
通过PropertyCondition查找
QueryBuilder queryBuilder = mDaoSession.queryBuilder(User.class);
WhereCondition nameCondition = null ;
WhereCondition ageCondition = null;
Property[] properties = mDaoSession.getUserDao().getProperties();
for (Property p : properties) {
if(p.columnName.equals("_name")) {
nameCondition = p.eq("micro");
//p.eq,p.nep,p.like,p.between,p.in,p.notIn,p.isNull,p.isNotNull 各种SQL操作
}
if(p.columnName.equals("_age")){
ageCondition = p.gt(20);
}
}
List userList = queryBuilder.where(queryBuilder.or(nameCondition,ageCondition)).build().list();
目前greenDao支持的数据库模型为一对比一,一对多模型,暂未支持多对多模型。这是这篇博客的重点所在,在项目中,遇到几张表,没什么逻辑关系,那么使用原生的SQLite就可以搞定了,就不比请greenDao大佬了。由于在项目中遇到了很多表出现了相互依赖的关系,那么使用greenDao简单配置一下,就可以使用了。
一对一关系
什么是一对一,读官方的解释我不会,就举个例子吧,每个人对应一张身份证信息,那么提取对象人与身份证 就是一对一的关系。在greenDao中,建立这种一对一的关系,先要确定谁是主导关系,这里是人,因为有人才有身份证信息,那么看IdCard和Person对象:
IdCard对象
@Entity
public class IdCard {
@Id(autoincrement = true)
private Long _id ;
private String cardName;
private String location;
@Generated()
public IdCard() {
}
}
Person对象
@Entity
public class Person {
@Id(autoincrement = true)
private Long _id;
private String name ;
private Long idCardId ;
@ToOne(joinProperty = "idCardId")
private IdCard idCard;
@Generated
public Person(){}
}
可以看到,Person对象中存在了IdCard的引用,而且IdCard的注解上为@ToOne,而其中的joinProperty对应了Person中一个私有属性idCardId。
完成编译之后,我们可以打开greenDao给我们建立的数据库,其中Person与IdCard表的结构为:
我们看到了Person表里面引用了IdCard的id,那么添加数据时应该这样:
public void addPersonAndIdCard(View view) {
IdCard idCard = new IdCard(null,"421111111133","shanghai22");
mDaoSession.getIdCardDao().insert(idCard);
Person p = new Person(null,"lisi",null);
p.setIdCard(idCard);
Long insertId = mDaoSession.getIdCardDao().getPersonDao().insert(p);
Toast.makeText(this, "the insert id is " + insertId, Toast.LENGTH_SHORT).show();
}
结果为:
第一种:
@ToMany(referencedJoinProperty = “referenceId”)
一个人可有多个产品订单,抽象之后产生:顾客Customer与订单Order对象。一个顾客对应多个订单:
Order.java:
@Entity(nameInDb = "t_order")
public class Order {
@Id
private Long _id ;
private java.util.Date date ;
//引用了顾客的Id
private long customerId ;
@Generated()
public Order() {
}
Customer.java:
@Entity
public class Customer {
@Id
private Long id ;
@ToMany(referencedJoinProperty = "customerId")
@OrderBy("date ASC")
private List orders;
@Generated()
public Customer() {
}
在Custom中使用注解@ToMany, 其referencedJoinProperty 对应Order对象中的customerId 对应的多个订单为List类型,生成的表的结构为:
插入数据:
public void btnAdd(View view) {
CustomerDao customerDao = mSession.getCustomerDao();
Customer customer = new Customer(null);
customerDao.insert(customer);
OrderDao orderDao = mSession.getOrderDao();
Order order1 = new Order(null,new Date(),customer.getId());
Order order2 = new Order(null,new Date(),customer.getId());
Order order3 = new Order(null,new Date(),customer.getId());
orderDao.insert(order1);
orderDao.insert(order2);
orderDao.insert(order3);
Toast.makeText(this, "add data finished...", Toast.LENGTH_SHORT).show();
}
第二种:
@ToMany(joinProperties = {
@JoinProperty(name = “current_object_param”,referencedName = “reference_object_params”)
})
什么意思呢?不好解释,那还是举个例子吧,比如一个成功的人SuccessfulMan拥有很多家公司,抽象出来,这个SuccessfulMan对应多个公司对象,那么相对应的对象分别为:
SuccessFulMan.java
@Entity
public class SuccessfulMan {
@Id(autoincrement = true)
private Long _id;
private int age ;
@NotNull
private String name;
@ToMany(joinProperties = {
@JoinProperty(name = "name",referencedName = "successfulManName")
})
@OrderBy("date ASC")
private List companyList;
@Generated()
public SuccessfulMan (){}
}
Company.java:
@Entity
public class Company {
@Id(autoincrement = true)
private Long _id;
@NotNull
private String successfulManName ;
//当前公司名称
private String companyName ;
private java.util.Date date ;
//销售额
private double price;
@Generated()
public Company(){}
}
编译成功之后,我们查看两张表的结构:
SuccessfulMan表中的name字段与Company表中的successful_man_name绑定了,我们来添加数据试一下:
public void addBtn(View view) {
SuccessfulMan man = new SuccessfulMan(null, "micro", 34);
mDaoSession.getSuccessfulManDao().insert(man);
Company company = new Company(null, "micro", "baidu1", new Date(), 2000);
Company company2 = new Company(null, "micro", "tencent1", new Date(), 4000);
Company company3 = new Company(null, "micro", "ali1", new Date(), 5000);
mDaoSession.getCompanyDao().insert(company);
mDaoSession.getCompanyDao().insert(company2);
mDaoSession.getCompanyDao().insert(company3);
Toast.makeText(this, "insert data finished....", Toast.LENGTH_SHORT).show();
}
可以看到的结果为:
可以看到绑定的name是一致的。
第三种:
@ToMany
@JoinEntity(entity = A_AND_B.class,
sourceProperty = “referenceAId” ,
targetProperty = “referenceBId”)
个人感觉这个是最好理解的,同时感觉这个也是最greenDao中最接近多对多关系的一种模式。还是拿例子说话吧。一个城市图书馆很多书,那么图书馆与书就是一对多的关系,抽象成代码为:
Book.java:
@Entity
public class Book {
@Id
private Long _id;
private String name ;
private double price ;
@Generated
public Book(){}
City.java:[library不会写!!!]
@Entity
public class City {
@Id
private Long _id;
private String name ;
@ToMany
@JoinEntity(entity = CityAndBooks.class,
sourceProperty = "cityId" ,
targetProperty = "bookId")
private List bookList;
@Generated
public City(){}
中间体CityAndBooks.java:
@Entity
public class CityAndBooks {
@Id
private Long _id;
private long bookId;
private long cityId ;
@Generated()
public CityAndBooks() {
}
编译之后,获取的表结构为:
图中可以看出,此时的一对多关系建立了三张表,而city_and_book表记录了city表的id,book表的id,相互查询时就是按照这张中间表进行的,我们插入数据看一下结果:
City city = new City(null,"shanghai");
City city1 = new City(null,"北京");
Book book = new Book(null,"one night",40);
Book book1 = new Book(null,"thinking in java",68);
mDaoSession.getCityDao().insert(city);
mDaoSession.getCityDao().insert(city1);
mDaoSession.getBookDao().insert(book);
mDaoSession.getBookDao().insert(book1);
CityAndBooks cb = new CityAndBooks(null,book.get_id(),city.get_id());
CityAndBooks cb1 = new CityAndBooks(null,book1.get_id() , city1.get_id());
mDaoSession.getCityAndBooksDao().insert(cb);
mDaoSession.getCityAndBooksDao().insert(cb1);
Toast.makeText(this, "insert data finished....", Toast.LENGTH_SHORT).show();
我们来看一下结果:
可以看出,我们的city与book已经建立了联系,那么查询起来应该不是那么困难吧。
对于greenDao,或者对于SQLite数据库更新,一般比较负责任的方式一般是这样的:
* 对于需要更新的表,创建临时表,复制数据
* 删除需要更新的表
* 创建新的目标表
* 将临时数据复制到新的目标表中,并删除临时表
这个就不说,下面的地址中会给出代码更新代码。
好了,greenDao的基本用法就介绍得差不多了,当然还有很多细节问题需要大家去注意了,这个只有大家在应用中去体会了,东西还是很多的,如果有问题或者疑惑,请留言。
所有代码地址为:https://github.com/Microhx/studyCodes/tree/master/testgreengao
效果图为: