GreenDAO的使用:
1.在project的build.gradle里邊添加:
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://jitpack.io' }
// greenDao數(shù)據(jù)庫(kù)
mavenCentral()
google()
jcenter()
}
dependencies {
//Gradle編譯版本
classpath "com.android.tools.build:gradle:4.1.2"
// greenDao數(shù)據(jù)庫(kù)
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
}
}
allprojects {
repositories {
// 阿里云云效倉(cāng)庫(kù)菇民,提升編譯速度
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/repository/central' }
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://jitpack.io' }
jcenter()
google()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
在app模塊的build.gradle里的android{}節(jié)點(diǎn)里添加:
greendao {
schemaVersion 1 // 數(shù)據(jù)庫(kù)版本號(hào)
daoPackage 'com.my.dao.greendao' // 設(shè)置DaoMaster、DaoSession、Dao包名痢缎,也就是要放置這些類的包的全路徑叹侄。
targetGenDir 'src/main/java' // 設(shè)置 DaoMaster梁只、DaoSession绝淡、Dao 目錄
//generateTests false //設(shè)置為true以自動(dòng)生成單元測(cè)試昌抠。
//targetGenDirTests 'src/main/java' //應(yīng)存儲(chǔ)生成的單元測(cè)試的基本目錄。默認(rèn)為 src / androidTest / java轨功。
}
dependencies{}節(jié)點(diǎn)里邊添加
// greenDao數(shù)據(jù)庫(kù)
implementation 'org.greenrobot:greendao:3.3.0'
3.創(chuàng)建實(shí)體對(duì)象類
@Entity
public class TestIdCard {
@Id
private Long cardId;
private String cardNo;
}
@Entity
public class TestPerson {
@Id(autoincrement = true)
private Long personId;
private String personName;
@ToOne(joinProperty = "personId")
private TestIdCard testIdCard;
}
這個(gè)類用不著為了看他的注解方法 忽略這個(gè)
//@Entity
//public class Student {
// //@Id:主鍵旭斥,通過(guò)這個(gè)注解標(biāo)記的字段必須是Long類型的,這個(gè)字段在數(shù)據(jù)庫(kù)中表示它就是主鍵古涧,并且它默認(rèn)就是自增的
// @Id(autoincrement = true)
// private long id;
//
// //姓名
// @NotNull // @NotNull 設(shè)置數(shù)據(jù)庫(kù)表當(dāng)前列不能為空
// @Unique //唯一
// private String name;
//
// //年齡
// //@Property:設(shè)置一個(gè)非默認(rèn)關(guān)系映射所對(duì)應(yīng)的列名垂券,默認(rèn)是使用字段名,例如:@Property(nameInDb = "name")
// @Property(nameInDb = "userage")
// private int age;
//}
4.點(diǎn)擊Android Studio窗口上的Build , Make Project會(huì)自動(dòng)生成數(shù)據(jù)庫(kù)相應(yīng)的表和實(shí)體類中的方法羡滑,注意實(shí)體類都必須用@Entity注解修飾菇爪,主鍵用@Id
此處只記錄下GreenDAO的多表關(guān)聯(lián),其他的介紹可自行前往了解
必要的一步啄栓,在Application中初始化數(shù)據(jù)庫(kù):
import android.app.Application;
import android.database.sqlite.SQLiteDatabase;
import com.my.dao.greendao.DaoMaster;
import com.my.dao.greendao.DaoSession;
/**
* Author : i小灰
* time : 2021/3/11 14:52
* desc : 程序入口
*/
public class App extends Application {
//數(shù)據(jù)庫(kù)名稱
private static final String DB_NAME = "hui_dao.db";
private static DaoSession mDaoSession;
//單例模式-懶漢式
private static App app;
public App() {
App.app = this;
}
public static App getInstance() {
if (app == null) {
//雙重校驗(yàn)鎖
synchronized (App.class) {
if (app == null) {
app = new App();
}
}
}
return app;
}
@Override
public void onCreate() {
super.onCreate();
//初始化GreenDao數(shù)據(jù)庫(kù)
initGreenDao();
}
private void initGreenDao() {
//生成數(shù)據(jù)庫(kù)文件娄帖,名為 students-db
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, DB_NAME, null);
//獲取可寫(xiě)數(shù)據(jù)庫(kù)
SQLiteDatabase db = helper.getWritableDatabase();
//建立特定模式下的所有的 DAO 對(duì)象和數(shù)據(jù) db 對(duì)象的映射
DaoMaster daoMaster = new DaoMaster(db);
//管理特定模式下的所有 DAO 對(duì)象尿褪,并提供一些通用的 CRUD 持久化方法
mDaoSession = daoMaster.newSession();
}
//供外部調(diào)用操作數(shù)據(jù)庫(kù)
public static DaoSession getmDaoSession() {
return mDaoSession;
}
}
這里補(bǔ)充插入一點(diǎn):關(guān)聯(lián)表查詢舔株,在不設(shè)置的前提下缀蹄,在主動(dòng)端關(guān)聯(lián)查詢允粤,可以查詢到被動(dòng)端的數(shù)據(jù)孽拷。而從被動(dòng)端查詢是不知道自己屬于誰(shuí)和誰(shuí)有關(guān)聯(lián)的音五。
一對(duì)一 例如:一個(gè)人對(duì)應(yīng)一張身份證料滥,關(guān)聯(lián)時(shí)會(huì)將這個(gè)人的ID設(shè)置給這張身份證徘跪,那么這個(gè)人是主動(dòng)端淳梦,身份證是被動(dòng)端析砸。這個(gè)人是知道哪張身份證屬于自己,而被動(dòng)端的身份證是不知道自己屬于哪個(gè)人爆袍。
一對(duì)多 例如:一個(gè)老師有多個(gè)學(xué)生首繁,如果我們不給學(xué)生設(shè)置他對(duì)應(yīng)哪個(gè)老師的情況下。外鍵是設(shè)置在學(xué)生這個(gè)多 的一方(被動(dòng)的一方)陨囊,從老師端查詢弦疮,可以查詢到他擁有的學(xué)生數(shù)據(jù),而從學(xué)生端查詢是無(wú)法知道他對(duì)應(yīng)的老師的信息的蜘醋,只有給學(xué)生設(shè)置了對(duì)應(yīng)的老師的信息胁塞,那么這個(gè)學(xué)生才知道自己數(shù)據(jù)哪個(gè)老師。
結(jié)論:在不給擁有外鍵這個(gè)多的一方設(shè)置的情況下压语,從擁有外鍵的一方關(guān)聯(lián)查詢只能查詢到自己的信息啸罢,而從多的一方是可以查詢到擁有外鍵這方的信息。(主動(dòng)方將自己的主鍵發(fā)給被動(dòng)方持有胎食,雙方由此產(chǎn)生關(guān)聯(lián))
一對(duì)一
一對(duì)一實(shí)際上是表A中一項(xiàng)元素的主鍵等于表B中一項(xiàng)元素的主鍵扰才,那么這兩個(gè)元素有一對(duì)一的關(guān)聯(lián)。一對(duì)一要用到@ToOne注解斥季,例如一個(gè)人只有一張身份證
用到的實(shí)體類就是上面的兩個(gè)
測(cè)試:
private void oneToOne() {
//存儲(chǔ)數(shù)據(jù)
TestPerson testPerson = new TestPerson();
testPerson.setPersonName("i小灰");
App.getmDaoSession().insert(testPerson);
TestIdCard testIdCard = new TestIdCard();
testIdCard.setCardId(testPerson.getPersonId());
testIdCard.setCardNo("12334555");
App.getmDaoSession().insert(testIdCard);
//查詢數(shù)據(jù)
List<TestPerson> testPeople = App.getmDaoSession().queryAll(TestPerson.class);
for (TestPerson person : testPeople) {
LogUtils.d("person :" + person.toString());
TestIdCard testIdCard1 = person.getTestIdCard();
LogUtils.d("card: " + testIdCard1.toString());
}
}
一對(duì)多
一對(duì)多實(shí)際上是在多的一方的表中創(chuàng)建一個(gè)外鍵训桶,如果多的一方表中的某個(gè)元素的外鍵等于一的一方表中的主鍵累驮,那么多的一方表中的這個(gè)元素就跟一的一方表中的這個(gè)元素產(chǎn)生了一對(duì)多的關(guān)聯(lián)酣倾。一對(duì)多要用到@ToMany注解舵揭,例如一個(gè)用戶可以擁有多張信用卡
新建實(shí)體類:
TestUser
@Entity
public class TestUser {
@Id(autoincrement = true)
private Long userId;
private String userName;
private String age;
//一對(duì)多
@ToMany(referencedJoinProperty = "credit_user_id")
private List<TestCreditCard> creditCards;
}
TestCreditCard
@Entity
public class TestCreditCard {
//主鍵必須是唯一的
@Id
private Long cId;
private String creditCardNo;
private String creditCardName;
//外鍵必須是唯一的
@Unique
private Long credit_user_id;
}
同樣記得build Make Project
測(cè)試
/**
* 一對(duì)多
* user and creditCard
*/
private void oneToMany() {
//存儲(chǔ)數(shù)據(jù)
TestUser testUser = new TestUser();
testUser.setUserName("狗哥");
testUser.setAge("23");
App.getmDaoSession().insert(testUser);
for (int i = 3; i < 6; i++) {
TestCreditCard testCreditCard = new TestCreditCard();
testCreditCard.setCId((i + 1L));
testCreditCard.setCredit_user_id((i + 1L));
testCreditCard.setCreditCardName("招商信用卡_"+i);
testCreditCard.setCreditCardNo("12345678");
App.getmDaoSession().insert(testCreditCard);
}
//查詢數(shù)據(jù)
List<TestUser> testUsers = App.getmDaoSession().queryAll(TestUser.class);
for (TestUser user : testUsers) {
LogUtils.d("用戶:" + user.toString());
List<TestCreditCard> creditCards = user.getCreditCards();
for (TestCreditCard creditCard : creditCards) {
LogUtils.d("信用卡:" + creditCard.toString());
}
}
}
多對(duì)多
多對(duì)多創(chuàng)建中間表,在中間表中分別有兩個(gè)外鍵指向一的一方躁锡。如果中間表中的某個(gè)元素的外鍵等于一的一方中某個(gè)元素的主鍵午绳,那么這兩項(xiàng)元素就產(chǎn)生了一對(duì)多的關(guān)聯(lián)。中間表分別指向兩邊產(chǎn)生一對(duì)多的關(guān)聯(lián)映之,那么兩邊就產(chǎn)生了多對(duì)多的關(guān)聯(lián)拦焚。多對(duì)多的關(guān)系其實(shí)也就類似于MySQL中的多對(duì)多關(guān)系,通過(guò)建立中間表來(lái)分解為一對(duì)多的關(guān)系杠输。我們以student和teacher為例赎败,下面創(chuàng)建兩個(gè)實(shí)體類
@Entity
public class Student {
@Id(autoincrement = true)
Long studentId;
String studentName;
@ToMany
@JoinEntity(entity = StudentAndTeacherBean.class, sourceProperty = "sId", targetProperty = "tId")
List<Teacher> teacherList;
...setter and getter
}
@Entity
public class Teacher {
@Id(autoincrement = true)
Long teacherId;
String teacherName;
@ToMany
@JoinEntity(entity = StudentAndTeacherBean.class, sourceProperty = "tId", targetProperty = "sId")
List<Student> studentList;
...setter and getter
}
build Make Project后,我們來(lái)進(jìn)行測(cè)試蠢甲。分為兩個(gè)方法:insertDataToMany是創(chuàng)建并保存數(shù)據(jù)僵刮;
queryDataToMany是分別從學(xué)生的角度查詢關(guān)聯(lián)的老師和從老師的角度查詢關(guān)聯(lián)的學(xué)生
測(cè)試
/**
* 多對(duì)多
*/
public void insertDataToMany(View view) {
for (int i = 0; i < 2; i++) {
if (i == 0) {
Teacher teacher = new Teacher();
teacher.setTeacherName("李老師");
long insert = App.getmDaoSession().insert(teacher);
Log.i(TAG, "老師存儲(chǔ)的結(jié)果:" + insert);
for (int i1 = 0; i1 < 2; i1++) {
if (i1 == 0) {
Student student = new Student();
student.setStudentName("大同學(xué)1號(hào)");
long insertStudent = App.getmDaoSession().insert(student);
Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);
StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
studentAndTeacherBean.setTId(teacher.getTeacherId());
studentAndTeacherBean.setSId(student.getStudentId());
long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
}else if (i1==1){}
if (i1 == 0) {
Student student = new Student();
student.setStudentName("大同學(xué)2號(hào)");
long insertStudent = App.getmDaoSession().insert(student);
Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);
StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
studentAndTeacherBean.setTId(teacher.getTeacherId());
studentAndTeacherBean.setSId(student.getStudentId());
long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
}
}
} else if (i == 1) {
Teacher teacher = new Teacher();
teacher.setTeacherName("劉老師");
long insert = App.getmDaoSession().insert(teacher);
Log.i(TAG, "老師存儲(chǔ)的結(jié)果:" + insert);
for (int i1 = 0; i1 < 2; i1++) {
if (i1 == 0) {
Student student = new Student();
student.setStudentName("小同學(xué)1號(hào)");
long insertStudent = App.getmDaoSession().insert(student);
Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);
StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
studentAndTeacherBean.setTId(teacher.getTeacherId());
studentAndTeacherBean.setSId(student.getStudentId());
long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
}else if (i1==1){
Student student = new Student();
student.setStudentName("小同學(xué)2號(hào)");
long insertStudent = App.getmDaoSession().insert(student);
Log.i(TAG, "學(xué)生存儲(chǔ)的結(jié)果:" + insertStudent);
StudentAndTeacherBean studentAndTeacherBean = new StudentAndTeacherBean();
studentAndTeacherBean.setTId(teacher.getTeacherId());
studentAndTeacherBean.setSId(student.getStudentId());
long insertStudentTeacher = App.getmDaoSession().insert(studentAndTeacherBean);
Log.i(TAG, "存儲(chǔ)中間表的結(jié)果:" + insertStudentTeacher);
}
}
}
}
}
/**
* 查詢數(shù)據(jù)
*/
public void queryDataToMany(View view) {
List<Teacher> teachers = App.getmDaoSession().loadAll(Teacher.class);
Log.i(TAG, "所有老師的數(shù)量:" + teachers.size());
for (Teacher teacher : teachers) {
Log.i(TAG, "老師:" + teacher.toString());
List<Student> studentList = teacher.getStudentList();
Log.i(TAG, "學(xué)生的數(shù)量:" + studentList.size());
for (Student student : studentList) {
Log.i(TAG,"學(xué)生的信息:"+student.toString());
}
}
List<Student> students = App.getmDaoSession().loadAll(Student.class);
Log.i(TAG, "所有學(xué)生的數(shù)量:" + students.size());
for (Student student : students) {
Log.i(TAG, "學(xué)生:" + student.toString());
List<Teacher> teacherList = student.getTeacherList();
Log.i(TAG, "老師的數(shù)量:" + teacherList.size());
for (Teacher teacher : teacherList) {
Log.i(TAG,"老師的信息:"+teacher.toString());
}
}
}
數(shù)據(jù)庫(kù)的升級(jí)更新:文章參考http://www.reibang.com/p/53083f782ea2,在此感謝分享!
我們自定義一個(gè)類繼承自O(shè)penHelper
public class MyDaoMaster extends DaoMaster.OpenHelper {
private static final String TAG = "MyDaoMaster";
public MyDaoMaster(Context context, String name) {
super(context, name);
}
public MyDaoMaster(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
super.onUpgrade(db, oldVersion, newVersion);
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);
}
},YouThingDao.class);
Log.e(TAG, "onUpgrade: " + oldVersion + " newVersion = " + newVersion);
}
}
在MyMasterDao中YouThingDao.class是你項(xiàng)目中的Dao數(shù)據(jù)類,這里可以傳一個(gè)Dao類的數(shù)組
/**
* 數(shù)據(jù)庫(kù)升級(jí)的思路:
* 1.先創(chuàng)建當(dāng)前已存在的所有表鹦牛,對(duì)應(yīng)臨時(shí)的表
* 2.然后刪除當(dāng)前已存在的所有的表
* 3.創(chuàng)建數(shù)據(jù)庫(kù)升級(jí)后的所有新的表
*
*/
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";
/**
* 重新創(chuàng)建所有表格監(jiān)聽(tīng)的弱引用
*/
private static WeakReference<ReCreateAllTableListener> weakListener;
/**
* 重新創(chuàng)建所有表格的監(jiān)聽(tīng)
*/
public interface ReCreateAllTableListener{
void onCreateAllTables(Database db, boolean ifNotExists);
void onDropAllTables(Database db, boolean ifExists);
}
/**
* 遷移
* @param db
* @param daoClasses
*/
public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
//舊的數(shù)據(jù)庫(kù)版本
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);//生成臨時(shí)的表
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);
}
//回復(fù)原來(lái)表的數(shù)據(jù)
printLog("【Restore data】start");
restoreData(database, daoClasses);
printLog("【Restore data】complete");
}
/**
* 聲場(chǎng)新的表
*/
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);
}
}
}
/**
*
* @param db
* @param isTemp
* @param tableName
* @return
*/
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();
}
}
/**
* 恢復(fù)數(shù)據(jù)
* @param db
* @param daoClasses
*/
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<TableInfo> newTableInfos = TableInfo.getTableInfo(db, tableName);
List<TableInfo> tempTableInfos = TableInfo.getTableInfo(db, tempTableName);
ArrayList<String> selectColumns = new ArrayList<>(newTableInfos.size());
ArrayList<String> intoColumns = new ArrayList<>(newTableInfos.size());
for (TableInfo tableInfo : tempTableInfos) {
if (newTableInfos.contains(tableInfo)) {
String column = '`' + tableInfo.name + '`';
intoColumns.add(column);
selectColumns.add(column);
}
}
// NOT NULL columns list
for (TableInfo tableInfo : newTableInfos) {
if (tableInfo.notnull && !tempTableInfos.contains(tableInfo)) {
String column = '`' + tableInfo.name + '`';
intoColumns.add(column);
String value;
if (tableInfo.dfltValue != null) {
value = "'" + tableInfo.dfltValue + "' AS ";
} else {
value = "'' AS ";
}
selectColumns.add(value + column);
}
}
if (intoColumns.size() != 0) {
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("REPLACE INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", intoColumns));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", selectColumns));
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);
}
}
private static class TableInfo {
int cid;
String name;
String type;
boolean notnull;
String dfltValue;
boolean pk;
@Override
public boolean equals(Object o) {
return this == o
|| o != null
&& getClass() == o.getClass()
&& name.equals(((TableInfo) o).name);
}
@Override
public String toString() {
return "TableInfo{" +
"cid=" + cid +
", name='" + name + '\'' +
", type='" + type + '\'' +
", notnull=" + notnull +
", dfltValue='" + dfltValue + '\'' +
", pk=" + pk +
'}';
}
private static List<TableInfo> getTableInfo(Database db, String tableName) {
String sql = "PRAGMA table_info(" + tableName + ")";
printLog(sql);
Cursor cursor = db.rawQuery(sql, null);
if (cursor == null)
return new ArrayList<>();
TableInfo tableInfo;
List<TableInfo> tableInfos = new ArrayList<>();
while (cursor.moveToNext()) {
tableInfo = new TableInfo();
tableInfo.cid = cursor.getInt(0);
tableInfo.name = cursor.getString(1);
tableInfo.type = cursor.getString(2);
tableInfo.notnull = cursor.getInt(3) == 1;
tableInfo.dfltValue = cursor.getString(4);
tableInfo.pk = cursor.getInt(5) == 1;
tableInfos.add(tableInfo);
// printLog(tableName + ":" + tableInfo);
}
cursor.close();
return tableInfos;
}
}
}
數(shù)據(jù)庫(kù)的文件有變動(dòng)搞糕,記得修改build.gradle中的數(shù)據(jù)庫(kù)版本號(hào),當(dāng)新的數(shù)據(jù)庫(kù)版本號(hào)大于舊的版本號(hào)時(shí)才會(huì)進(jìn)行數(shù)據(jù)庫(kù)的更新升級(jí)!
多表關(guān)聯(lián):一對(duì)多和多對(duì)多時(shí)曼追,把一的一方的外鍵給多的一方存儲(chǔ);一對(duì)一中窍仰,把被動(dòng)一方的外鍵給主動(dòng)一方存儲(chǔ)