1.引言
目前做的一款app,全部都是基于本地数据库,于是采用第三方数据库greendao。因为之前对greendao没有深层次的研究,都是照着博客上学的,没想到被博客坑了。网上搜索的千篇一律,都是照着官网上翻译的,有很多需要着重注意的地方都被忽略了,导致我在使用的时候遇到大量的坑。因此我单独的写了一个简单的项目来验证我踩过的坑。但是有些情况却没有出现,不得其解。
官网的地址
2.正文
2.1 @ToOne
一节课一个学生对应一个老师,所以学生:老师=1:1
@Entity
public class Student {
@Id
private Long id;//主键
private Long tid;//外键
@ToOne(joinProperty = "tid")
private Teacher teacher;
}
teacher:
@Entity
public class Teacher {
@Id
private Long id;//主键
private Long idCard;//身份证号码
}
插入student和techaer:
DaoSession daoSession = daoMaster.newSession();
StudentDao studentDao = daoSession.getStudentDao();
TeacherDao teacherDao = daoSession.getTeacherDao();
Student student=new Student();
Teacher teacher=new Teacher();
teacher.setIdCard(333L);
Long key=teacherDao.insert(teacher);
student.setTid(key);
studentDao.insert(student);
当然通过student.setTeacher();来进行级联插入也是可以的。亲测。
注意:load()和loadDeep()
load()查询。不会把外键的信息查询出来。但是假如人为的调用setTeacher,之后再load()查询,就会把Teacher的信息也查找出来
loadDeep:会将所有的信息都查询出来,包括外键。
2.2 外键必须是关联表的主键。
我想把身份证id作为student表中的外键。但是实际make project的时候报错。
student表:
@Entity
public class Student {
@Id
private Long id;//主键
private String teacherIdCard;//外键
@ToOne(joinProperty = "teacherIdCard")
private Teacher teacher;
}
teacher表:
@Entity
public class Teacher {
@Id
private Long id;//主键
private String idCard;//身份证号码
public String getIdCard() {
return this.idCard;
}
}
2.3 @Unite的使用。
项目中,我本地表都是已经存在的不是自己建立的。或许就是因为自己建立的而不是生成的导致。@Unique没得反应,md。
@Entity(createInDb = false)
public class Teacher implements Serializable{
@Id
@Property(nameInDb = "tid")
private Long tid;//主键
@Unique
private String idCard;//身份证号码
这样做就行了。
2.4 将生成的db文件放到指定的文件路径下。网上有现成的代码。在此把项目中的粘贴下。
public class GreenDaoContext extends ContextWrapper {
private String currentUserId;
private Context mContext;
public GreenDaoContext() {
super(XinYiApplication.instance);
this.mContext = XinYiApplication.instance;
}
/**
* 获得数据库路径,如果不存在,则创建对象
* @param dbName
*/
@Override
public File getDatabasePath(String dbName) {
// 判断是否存在sd卡
boolean sdExist = android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState());
if (!sdExist) {// 如果不存在,
Log.e("SD卡管理:", "SD卡不存在,请加载SD卡");
return null;
} else {// 如果存在
// 获取sd卡路径
String dbDir = android.os.Environment.getExternalStorageDirectory().getAbsolutePath();
dbDir += "/mlapp";// 数据库所在目录
String dbPath = dbDir+"/"+dbName;// 数据库路径
// 判断目录是否存在,不存在则创建该目录
File dirFile = new File(dbDir);
if (!dirFile.exists())
dirFile.mkdirs();
// 数据库文件是否创建成功
boolean isFileCreateSuccess = false;
// 判断文件是否存在,不存在则创建该文件
File dbFile = new File(dbPath);
if (!dbFile.exists()) {
try {
isFileCreateSuccess = dbFile.createNewFile();// 创建文件
} catch (IOException e) {
e.printStackTrace();
}
} else {
isFileCreateSuccess = true;
}
// 返回数据库文件对象
if (isFileCreateSuccess) {
return dbFile;
} else {
return super.getDatabasePath(dbName);
}
}
}
/**
* 重载这个方法,是用来打开SD卡上的数据库的,android 2.3及以下会调用这个方法。
*
* @param name
* @param mode
* @param factory
*/
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode,
SQLiteDatabase.CursorFactory factory) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
return result;
}
/**
* Android 4.0会调用此方法获取数据库。
*
* @param name
* @param mode
* @param factory
* @param errorHandler
* @see android.content.ContextWrapper#openOrCreateDatabase(java.lang.String, int,
* android.database.sqlite.SQLiteDatabase.CursorFactory,
* android.database.DatabaseErrorHandler)
*/
@Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, SQLiteDatabase.CursorFactory factory,
DatabaseErrorHandler errorHandler) {
SQLiteDatabase result = SQLiteDatabase.openOrCreateDatabase(getDatabasePath(name), factory);
return result;
}
}
然后通过如下的方法得到daoSession
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(new GreenDaoContext(), "POSTerminal.db", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
DaoSession daoSession = daoMaster.newSession();
2.4 将db文件拷贝到指定文件夹下
public class CopyDBUtil {
public static void copyRawDBToApkDb() {
OutputStream outputStream=null;
InputStream inputStream=null;
String apkDbPath = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/POSTerminal";
String dbName = "/POSTerminal.db";
boolean b = false;
File f = new File(apkDbPath);
if (!f.exists()) {
f.mkdirs();
}
File dbFile = new File(apkDbPath + dbName);
if (dbFile.exists()) {
dbFile.delete();
}
try {
dbFile.createNewFile();
outputStream = new FileOutputStream(dbFile);//写入流
inputStream = new FileInputStream(new File(android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/mlapp/POSTerminal.db"));
byte[] bytes = new byte[1024];
int length;
while ((length = inputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, length);
}
//写完后刷新
outputStream.flush();
ToastUtil.showLong(XinYiApplication.instance,"复制成功");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (inputStream != null) {//关闭流,释放资源
inputStream.close();
}
if(outputStream!=null){
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.5 建立索引(或者设置唯一属性)
@Entity( indexes = {
@Index(value = "idCard", unique = true)
})
public class Teacher implements Serializable{
@Id
@Property(nameInDb = "tid")
private Long tid;//主键
private String idCard;//身份证号码
}
idCard属性被插入一样的值 就会报错。
2.6 @ToMany
一个老师对应多个学生即1:N。
@ToMany 有三种情况,根据需要选用不同的情况。
2.61 referencedJoinProperty 关联
@Entity
public class Teacher implements Serializable {
@Id
private Long id;//主键
private String name;
@Unique
private Long idCard;//身份证号码
//与上面的属性没得关系。referencedJoinProperty 与Student里面的idCard 相关。且是主键关联
@ToMany(referencedJoinProperty = "idCard")
private List<Student> students;
}
student表:
@Entity
public class Student {
@Id
public Long id;
private String name;
//Teacher的主键(必须是主键,)
public Long idCard;
}
总结:A:B=1:N 假如是通过referencedJoinProperty 关联的话,B里面有A的主键,属性不同没得关系..A里面的referencedJoinProperty 指向B中的A的主键表示的属性。。例如上面的B_idCard.
2.62 joinProperties 关联
teacher类的定义:
@Entity
public class Teacher {
@Id
private Long id;
private String teacherName;
//一下的是一体的
@NotNull
private Long tidCard;
@ToMany(joinProperties = {
@JoinProperty(name = "tidCard", referencedName = "tidCard")
})
private List<Student> students;
student类的定义:
@Entity
public class Student {
@Id
private Long id;
@NotNull
private Long tidCard;
joinProperties 属性将Teacher 里面的独一的非主键,当做Student的外键。referencedJoinProperty 关联 必须是主键作为Student的外键。
@ToMany注意:
通过查找teacher,能获得List<Student>。实际上当你查找得到一个Teacher 对象的时候,其实List<Student>这个时候为空的。只有当你调用getStudents()的时候才会进行查找。。这个与@ToOne的差别。
greendao的总结就到这里,以后看博客真的要先看英文文档。