官方api文档
SQLite可视化工具下载地址 Personal为免费版本
配置
在项目build.gradle中配置
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.1.0'
}
在你要使用的模块下的build.gradle配置(我是直接在app模块下)
apply plugin: 'org.greenrobot.greendao'
greendao {
// 指定数据库schema版本号,迁移等操作会用到
schemaVersion 1
// 通过gradle插件生成的数据库相关文件的包名,默认为你的entity所在的包名
daoPackage 'com.example.greendao.dao'
// 生成的数据库文件默认目录为:build/generated/source/greendao
// 自定义生成数据库文件的目录,可以将生成的文件放到java目录中,而不是build中,这样就不用额外的设置资源目录了
targetGenDir 'src/main/java'
}
dependencies {
compile 'org.greenrobot:greendao:3.1.0'
}
greendao build配置
schemaVersion: 数据库schema版本,也可以理解为数据库版本号
daoPackage:设置DaoMaster 、DaoSession、Dao包名
targetGenDir:设置DaoMaster 、DaoSession、Dao目录
targetGenDirTest:设置生成单元测试目录
generateTests:设置自动生成单元测试用例
实体@Entity注解
schema:告知GreenDao当前实体属于哪个schema
active:标记一个实体处于活动状态,活动实体有更新、删除和刷新方法
nameInDb:在数据中使用的别名,默认使用的是实体的类名
indexes:定义索引,可以跨越多个列
createInDb:标记创建数据库表
基础属性注解
@Id :主键 Long型,可以通过@Id(autoincrement = true)设置自增长
@Property:设置一个非默认关系映射所对应的列名,默认是的使用字段名 举例:@Property (nameInDb=”name”)
@NotNul:设置数据库表当前列不能为空
@Transient :添加次标记之后不会生成数据库表的列
索引注解
@Index:使用@Index作为一个属性来创建一个索引,通过name设置索引别名,也可以通过unique给索引添加约束
@Unique:向数据库列添加了一个唯一的约束
关系注解
@ToOne:定义与另一个实体(一个实体对象)的关系
@ToMany:定义与多个实体对象的关系
使用
我的entity是写在com.example.greendao.entity包下,当编译的时候,系统会自动找到@Entity注解过的类,在com.example.greendao.dao下产生greendao所需的类(这个包在build.gradle中指定了daoPackage,否则会在build/generated/source/greendao下)。
为了测试方便,写了三种entity类:person,animal.house,body,PersonWithHouse类,默认的测试关系是person和animal是1:N,person和body是1:1,person和house是N:M,PersonWithHouse是person和house的联系。
@Entity
public class Person {
//自增id类型只能是Long
@Id(autoincrement = true)
private Long id;
@Property(nameInDb = "user")
private String username;
private int age;
private Long bodyId;
//一对一,将Person表中的bodyId与body的主键关联
@ToOne(joinProperty = "bodyId")
private Body body;
//一对多
@ToMany(referencedJoinProperty = "ownerId")
private List<Animal> animals;
//多对多
@ToMany
@JoinEntity(
entity = PersonWithHouse.class,
sourceProperty = "personId",
targetProperty = "houseId"
)
private List<House> personWithHouse;
//get,set方法编译后会自动产生
}
@Entity
public class Animal {
@Id(autoincrement = true)
private Long id;
private Long ownerId;
private String name;
}
@Entity
public class Body {
@Id(autoincrement = true)
private Long id;
private int height;
private int weight;
}
@Entity
public class House {
@Id(autoincrement = true)
private Long id;
private String location;
private String price;
@ToMany
@JoinEntity(
entity = PersonWithHouse.class,
sourceProperty = "houseId",
targetProperty = "personId"
)
private List<Person> personList;
}
@Entity
public class PersonWithHouse {
@Id(autoincrement = true)
private Long id;
private Long personId;
private Long houseId;
}
封装
//在application中初始化MyDatabaseLoader.init(this,"greendao_db",null);
public class MyDatabaseLoader {
private static DaoSession daoSession;
public static void init(Context context,String databaseName,MyDataBaseOpenHelper.DataOpenHelper update){
MyDataBaseOpenHelper openHelper = new MyDataBaseOpenHelper(context,databaseName,null,update);
SQLiteDatabase database = openHelper.getWritableDatabase();
DaoMaster daoMaster =new DaoMaster(database);
daoSession = daoMaster.newSession();
}
public static DaoSession getDaoSession() {
return daoSession;
}
}
//数据库升级操作封装,使用直接更改application初始化的MyDatabaseLoader
public class MyDataBaseOpenHelper extends DaoMaster.OpenHelper {
public DataOpenHelper dataOpenHelper;
public MyDataBaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, DataOpenHelper upgrade) {
super(context, name, factory);
if(upgrade != null){
dataOpenHelper = upgrade;
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(dataOpenHelper != null){
dataOpenHelper.updata(db,oldVersion,newVersion);
}
}
public interface DataOpenHelper{
void updata(SQLiteDatabase db, int oldVersion, int newVersion);
}
}
数据库操作的封装
public interface helperDao {
//增加单条数据
<T> void add(T bean);
//增加list数据
<T> void addAll(List<T> beanList);
//通过id删除
void delete(String id);
//删除所有数据
void deleteAll();
//通过id查找
<T> T query(String id);
//通过id修改
<T> void updata(T bean);
}
public class PersonDaoHelper implements helperDao{
private PersonDao personDao;
private static PersonDaoHelper personDaoHelper;
public PersonDaoHelper(){
personDao = MyDatabaseLoader.getDaoSession().getPersonDao();
}
public static PersonDaoHelper getInstance(){
if(personDaoHelper == null ){
personDaoHelper = new PersonDaoHelper();
}
return personDaoHelper;
}
@Override
public <T> void add(T bean) {
if(personDao != null && bean != null){
personDao.insert((Person) bean);
}
}
@Override
public <T> void addAll(List<T> beanList) {
if(personDao !=null &&beanList != null){
personDao.insertInTx((List<Person>) beanList);
}
}
@Override
public void delete(String id) {
if(personDao !=null && id != null && !"".equals(id)){
personDao.deleteByKey(Long.valueOf(id));
}
}
@Override
public void deleteAll() {
if(personDao !=null){
personDao.deleteAll();
}
}
@Override
public <T> void updata(T bean) {
if(personDao !=null && bean != null){
personDao.update((Person) bean);
}
}
@Override
public List<Person> query(String id) {
if(personDao !=null && id != null && !"".equals(id)){
List<Person> list=personDao.queryBuilder().where(PersonDao.Properties.Id.eq(id)).list();
return list;
}
return null;
}
}
一张表一个helper类,其他以此类推,所有会有如下类:AnimalDaoHelper,BodyDaoHelper,HouseDaoHelper,PersonDaoHelper,PersonWithHouseDaoHelper
增
一对一
Person person =new Person();
person.setUsername("人"+j);
person.setAge(18);
Body body = new Body();
body.setHeight(180);
body.setWeight(170);
BodyDaoHelper.getinstance().add(body);
person.setBody(body);
PersonDaoHelper.getInstance().add(person);
一对多
for(int j=0;j<4;j++){
Person person =new Person();
person.setUsername("人"+j);
person.setAge(18);
Body body = new Body();
body.setHeight(180);
body.setWeight(170);
BodyDaoHelper.getinstance().add(body);
person.setBody(body);
PersonDaoHelper.getInstance().add(person);
for(int i=0;i<3;i++){
Animal animal =new Animal();
animal.setName("狗"+i);
//保存操作完成之后可以从缓存中直接取出插入的person
animal.setOwnerId(person.getId());
AnimalDaoHelper.getInsteance().add(animal);
}
}
多对多
List<House> houseList = new ArrayList<>();
List<Person> personList = new ArrayList<>();
for(int j=0;j<4;j++){
Person person =new Person();
person.setUsername("人"+j);
person.setAge(18);
personList.add(person);
}
PersonDaoHelper.getInstance().addAll(personList);
for(int j=0;j<4;j++){
House house =new House();
house.setLocation("北京"+j);
house.setPrice("10000");
houseList.add(house);
}
HouseDaoHelper.getInstance().addAll(houseList);
//person1选house1,house2
//house3有person2,person3
List<PersonWithHouse> personWithHouseList = new ArrayList<>();
PersonWithHouse personWithHouse = new PersonWithHouse(null,personList.get(1).getId(),houseList.get(1).getId());
personWithHouseList.add(personWithHouse);
personWithHouse = new PersonWithHouse(null,personList.get(1).getId(),houseList.get(2).getId());
personWithHouseList.add(personWithHouse);
personWithHouse = new PersonWithHouse(null,personList.get(2).getId(),houseList.get(3).getId());
personWithHouseList.add(personWithHouse);
personWithHouse = new PersonWithHouse(null,personList.get(3).getId(),houseList.get(3).getId());
personWithHouseList.add(personWithHouse);
PersonWithHouseDaoHelper.getInstance().addAll(personWithHouseList);
删
PersonDaoHelper.getInstance().delete("3");
改查
由于更改需要先查询所以就写在了一起
//查询
List<Person> personList = PersonDaoHelper.getInstance().query("2");
if(personList !=null){
Person person = personList.get(0);
person.setUsername("改动");
//更改
PersonDaoHelper.getInstance().updata(person);
}
注:所有的数据库操作只对单表有效,关联的表不会有影响
数据库升级
默认是删除原数据库创建新的数据库,但这样数据将无法保留,所以自定义一个升级类。MyDataBaseOpenHelper
原理是
1 把旧表改为临时表
2 建立新表
3 临时表数据写入新表,删除临时表
直接使用了开源库,GreenDaoUpgradeHelper
核心的类就是
public final class MigrationHelper {
public static boolean DEBUG = false;
private static String TAG = "MigrationHelper";
private static final String SQLITE_MASTER = "sqlite_master";
private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master";
private static WeakReference<ReCreateAllTableListener> weakListener;
public interface ReCreateAllTableListener {
void onCreateAllTables(Database db, boolean ifNotExists);
void onDropAllTables(Database db, boolean ifExists);
}
public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
printLog("【The Old Database Version】" + db.getVersion());
Database database = new StandardDatabase(db);
migrate(database, daoClasses);
}
public static void migrate(SQLiteDatabase db, ReCreateAllTableListener listener, Class<? extends AbstractDao<?, ?>>... daoClasses) {
weakListener = new WeakReference<>(listener);
migrate(db, daoClasses);
}
public static void migrate(Database database, ReCreateAllTableListener listener, Class<? extends AbstractDao<?, ?>>... daoClasses) {
weakListener = new WeakReference<>(listener);
migrate(database, daoClasses);
}
public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) {
printLog("【Generate temp table】start");
generateTempTables(database, daoClasses);
printLog("【Generate temp table】complete");
ReCreateAllTableListener listener = null;
if (weakListener != null) {
listener = weakListener.get();
}
if (listener != null) {
listener.onDropAllTables(database, true);
printLog("【Drop all table by listener】");
listener.onCreateAllTables(database, false);
printLog("【Create all table by listener】");
} else {
dropAllTables(database, true, daoClasses);
createAllTables(database, false, daoClasses);
}
printLog("【Restore data】start");
restoreData(database, daoClasses);
printLog("【Restore data】complete");
}
private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
String tempTableName = null;
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
if (!isTableExists(db, false, tableName)) {
printLog("【New Table】" + tableName);
continue;
}
try {
tempTableName = daoConfig.tablename.concat("_TEMP");
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";");
db.execSQL(dropTableStringBuilder.toString());
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName);
insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
printLog("【Table】" + tableName + "\n ---Columns-->" + getColumnsStr(daoConfig));
printLog("【Generate temp table】" + tempTableName);
} catch (SQLException e) {
Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e);
}
}
}
private static boolean isTableExists(Database db, boolean isTemp, String tableName) {
if (db == null || TextUtils.isEmpty(tableName)) {
return false;
}
String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER;
String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?";
Cursor cursor = null;
int count = 0;
try {
cursor = db.rawQuery(sql, new String[]{"table", tableName});
if (cursor == null || !cursor.moveToFirst()) {
return false;
}
count = cursor.getInt(0);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return count > 0;
}
private static String getColumnsStr(DaoConfig daoConfig) {
if (daoConfig == null) {
return "no columns";
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < daoConfig.allColumns.length; i++) {
builder.append(daoConfig.allColumns[i]);
builder.append(",");
}
if (builder.length() > 0) {
builder.deleteCharAt(builder.length() - 1);
}
return builder.toString();
}
private static void dropAllTables(Database db, boolean ifExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "dropTable", ifExists, daoClasses);
printLog("【Drop all table by reflect】");
}
private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
reflectMethod(db, "createTable", ifNotExists, daoClasses);
printLog("【Create all table by reflect】");
}
/**
* dao class already define the sql exec method, so just invoke it
*/
private static void reflectMethod(Database db, String methodName, boolean isExists, @NonNull Class<? extends AbstractDao<?, ?>>... daoClasses) {
if (daoClasses.length < 1) {
return;
}
try {
for (Class cls : daoClasses) {
Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class);
method.invoke(null, db, isExists);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private static void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
if (!isTableExists(db, true, tempTableName)) {
continue;
}
try {
// get all columns from tempTable, take careful to use the columns list
List<String> columns = getColumns(db, tempTableName);
ArrayList<String> properties = new ArrayList<>(columns.size());
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (columns.contains(columnName)) {
properties.add("`" + columnName + "`");
}
}
if (properties.size() > 0) {
final String columnSQL = TextUtils.join(",", properties);
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("REPLACE INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(columnSQL);
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
printLog("【Restore data】 to " + tableName);
}
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(dropTableStringBuilder.toString());
printLog("【Drop temp table】" + tempTableName);
} catch (SQLException e) {
Log.e(TAG, "【Failed to restore data from temp table 】" + tempTableName, e);
}
}
}
private static List<String> getColumns(Database db, String tableName) {
List<String> columns = null;
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null);
if (null != cursor && cursor.getColumnCount() > 0) {
columns = Arrays.asList(cursor.getColumnNames());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
if (null == columns)
columns = new ArrayList<>();
}
return columns;
}
private static void printLog(String info) {
if (DEBUG) {
Log.d(TAG, info);
}
}
}
要升级直接在application初始化是调用
MyDatabaseLoader.init(this, "greendao_db", new MyDataBaseOpenHelper.DataOpenHelper() {
@Override
public void updata(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion ==1 && newVersion == 2){
MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {
@Override
public void onCreateAllTables(Database db, boolean ifNotExists) {
DaoMaster.createAllTables(db, ifNotExists);
}
@Override
public void onDropAllTables(Database db, boolean ifExists) {
DaoMaster.dropAllTables(db, ifExists);
}
}, PersonDao.class);//person类更改了,还有其他的直接在后面添加
}
}
});
在build.gradle文件下更改版本号schemaVersion,运行之后就是新的表了。