資料庫框架GreenDao——比Realm好一點
說實話android的資料庫確實比較難用,尤其是資料量較大的時候。使用的SQL語句可能就比較多了。再加上佔位符,體積龐大。於是realm,greendao這些自動建立資料庫的就出現了。
Realm
realm優點:比SQLite更簡潔,更省空間,可以只儲存在記憶體,應用關閉自動清理,自帶加密功能。呼叫方法易讀,易用。
缺點:
1.資料必須與資料庫同步,第一次set之後,更新必須資料庫的資料也要更新,簡直有點霸王條款的意思?我每個物件都要寫兩次,一個對應資料庫,一個對應其他的,這不太科學。
2.不能繼承RealmObject以外的類【給的RealmModel實現方式是假的,繼承就bug】。
資料庫初始化
Realm.init(this);
RealmConfiguration config = new RealmConfiguration.Builder()
.name(Config.dbName)
.schemaVersion(Config.dbVersion)
//.isMemory()//記憶體快取用
.encryptionKey(Config.dbKey.getBytes())//加密字串長度64即byte128
.migration (DBUtil.getIntance())//升級資料庫處理類(實現RealmMigration介面)
.build();
Realm.setDefaultConfiguration(config);
資料庫升級(需要實現RealmMigration )
public class DBUtil implements RealmMigration {
private static DBUtil sIntance;
public DBUtil() {
}
/**
* 雙檢索單例
* @return
*/
public static DBUtil getIntance(){
if (sIntance == null) {
synchronized (DBUtil.class) {
if (sIntance == null) {
sIntance = new DBUtil();
}
}
}
return sIntance;
}
/**
* 獲取realm物件
* @return
*/
public Realm getRealm(){
Realm realm =getDefaultInstance();
return realm;
}
/**
* 版本升級處理
* @param realm
* @param oldVersion
* @param newVersion
*/
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
switch ((int)oldVersion){
// case 0:{
// RealmObjectSchema personSchema = schema.get("User");//獲取表
//// //新增@Required的id
// personSchema
// .addField("outerId", String.class)//新增欄位
// .transform(new RealmObjectSchema.Function() {
// @Override
// public void apply(DynamicRealmObject obj) {
// obj.set("outerId", "1");//為id設定預設值
// }
// });
//// .removeField("age");//移除age屬性
// //注意version不要break,因為前面的版本都要升級
// }
}
}
}
realm bean初始化
public class User extends RealmObject {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public NormalUser toNormalUser(){
NormalUser user = new NormalUser();
user.setId(id);
user.setName(name);
return user;
}
}
dao層(手寫)
public class UserDao implements BaseDao<User> {
private UserDao(){
}
private Realm realm;
private Realm getRealm(){
if(realm == null)
realm = DBUtil.getIntance().getRealm();
return realm;
}
private static UserDao dao;
public static UserDao getInstance(){
if(dao == null)
dao = new UserDao();
return dao;
}
@Override
public void insert(final User user) throws Exception {
getRealm().executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User u = realm.createObject(User.class);
u.setId(user.getId());
u.setName(user.getName());
}
});
}
@Override
public List<User> find() throws Exception {
List<User> mlist = null;
mlist = getRealm().where(User.class).findAllSorted("id", Sort.ASCENDING);
return mlist;
}
@Override
public void update(final User user) throws Exception {
getRealm().executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
User u=realm.where(User.class).equalTo("id",user.getId()).findFirst();
u.setName(user.getName());
}
});
}
@Override
public void delete(final String id) throws Exception {
getRealm().executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
RealmResults<User> result = realm.where(User.class).equalTo("id", id).findAll();
if(result!=null)
result.deleteAllFromRealm();
}
});
}
@Override
public void close() {
getRealm().close();
}
//分頁查詢
public static <E extends RealmModel> List<E> getLimitList(RealmResults<E> data, int offset, int limit) {
List<E> obtainList = new ArrayList();
Realm realm = Realm.getDefaultInstance();
if (data.size() == 0 ){
return obtainList;
}
for (int i = offset; i < offset + limit; i++) {
if (i >= data.size()) {
break;
}
E temp = realm.copyFromRealm(data.get(i));
obtainList.add(temp);
}
realm.close();
return obtainList;
}
}
realm插入:
try {
dao.insert(user);
dao.insert(user2);
dao.insert(user3);
List<User> users = dao.find();
for (int i = 0; i < users.size(); i++) {
Toast.makeText(this, "插入資料" + users.get(i), Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
realm刪除:
try {
List<User> users = dao.find();
if (users != null && users.size() > 0) {
User user = users.get(0);
String id = user.getId();
dao.delete(id);
Toast.makeText(this, "刪除資料", Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
realm更新
List<User> users = null;
try {
users = dao.find();
if (users != null && users.size() > 0) {
NormalUser user = users.get(0).toNormalUser();
user.setName("userChange");
dao.update(user.toDBUser());
Toast.makeText(this,"更新資料"+user, Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
realm查詢
try {
List<User> users = dao.find();
for(User user:users){
Toast.makeText(this,user.toString(), Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
e.printStackTrace();
}
GreenDao
greenDao優點:可和Sqlcipher連用【一直要用sqlcipher,感激】,entity的get,set和構造器自動生成,封裝了dao方法。
缺點:
1.不能繼承!【確切說是不能繼承欄位,繼承欄位不會成為表字段,如果有抽象方法還是可以繼承的】
2.沒有主鍵的時候刪除和更新失敗,不過還好也可以用SQL代替,勉強忍受。必須sql這點太奇怪了,sqlite本身都沒這樣要求啊。對於短的sql語句,操作不方便,還不如直接sql。
3.我用了3+的版本,驚訝的是居然不能升級。。。只能建立臨時表,好無奈,身為資料庫居然不能升級(升級後資料被刪除),只能臨時表拷貝。不過這方法不容易出現數據庫升級失敗的奇怪問題,或許可以沿用。
為什麼都不能繼承欄位呢?完全不懂啊。。。同事用的greendao介紹給我,看到他用的抽象父類抽象公共get,set方法感覺好無奈。。。不然就只能做個介面卡了,一樣的麻煩。實在不行就只能自己想辦法寫一個封裝了,自己寫的只能簡化sql呼叫過程,其他的應該做不到。。。有空再解決吧。
資料庫初始化
ublic class DBManager {
private static final String PWD = "123";
private static DaoSession encryptedSession;
private static final String DB_NAME = "my.db";
public static DaoSession getSession(){
if(encryptedSession == null){
DBHelper dbHelper = new DBHelper(CacheForAndorid.getSuperContext(),DB_NAME,null);
encryptedSession = new DaoMaster(dbHelper.getEncryptedWritableDb(PWD)).newSession();
}
return encryptedSession;
}
private static DBUserDao userDao;
public static DBUserDao getMyDBDao(){
if(userDao == null){
userDao = getSession().getDBUserDao();
}
return userDao;
}
private static DBUserNoKeyDao noKeyDao;
public static DBUserNoKeyDao getNoKeyDao(){
if(noKeyDao == null){
noKeyDao = getSession().getDBUserNoKeyDao();
}
return noKeyDao;
}
}
資料庫更新
public class MigrationHelper {
private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
private static MigrationHelper instance;
public static MigrationHelper getInstance() {
if (instance == null) {
instance = new MigrationHelper();
}
return instance;
}
public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
generateTempTables(db, daoClasses);
DaoMaster.dropAllTables(db, true);
DaoMaster.createAllTables(db, false);
restoreData(db, daoClasses);
}
/**
* 生成臨時列表
*
* @param db
* @param daoClasses
*/
private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
for (int i = 0; i < daoClasses.length; i++) {
DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
String divider = "";
String tableName = daoConfig.tablename;
String tempTableName = daoConfig.tablename.concat("_TEMP");
ArrayList<String> properties = new ArrayList<>();
StringBuilder createTableStringBuilder = new StringBuilder();
createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tableName).contains(columnName)) {
properties.add(columnName);
String type = null;
try {
type = getTypeByClass(daoConfig.properties[j].type);
} catch (Exception exception) {
exception.printStackTrace();
}
createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);
if (daoConfig.properties[j].primaryKey) {
createTableStringBuilder.append(" PRIMARY KEY");
}
divider = ",";
}
}
createTableStringBuilder.append(");");
db.execSQL(createTableStringBuilder.toString());
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(" FROM ").append(tableName).append(";");
db.execSQL(insertTableStringBuilder.toString());
}
}
/**
* 儲存新的資料庫表 以及資料
*
* @param db
* @param daoClasses
*/
private 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");
ArrayList<String> properties = new ArrayList();
for (int j = 0; j < daoConfig.properties.length; j++) {
String columnName = daoConfig.properties[j].columnName;
if (getColumns(db, tempTableName).contains(columnName)) {
properties.add(columnName);
}
}
StringBuilder insertTableStringBuilder = new StringBuilder();
insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(") SELECT ");
insertTableStringBuilder.append(TextUtils.join(",", properties));
insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
StringBuilder dropTableStringBuilder = new StringBuilder();
dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
db.execSQL(insertTableStringBuilder.toString());
db.execSQL(dropTableStringBuilder.toString());
}
}
private String getTypeByClass(Class<?> type) throws Exception {
if (type.equals(String.class)) {
return "TEXT";
}
if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
return "INTEGER";
}
if (type.equals(Boolean.class)) {
return "BOOLEAN";
}
Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
exception.printStackTrace();
throw exception;
}
private List<String> getColumns(Database db, String tableName) {
List<String> columns = new ArrayList<>();
Cursor cursor = null;
try {
cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
if (cursor != null) {
columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
}
} catch (Exception e) {
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return columns;
}
}
public class DBHelper extends DaoMaster.OpenHelper {
public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
super(context, name, factory);
}
/**
* 資料庫升級
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
//操作資料庫的更新 有幾個表升級都可以傳入到下面
MigrationHelper.getInstance().migrate(db,DBUserDao.class);
}
}
bean
有主鍵
@Entity
public class DBUser {
@Id(autoincrement = true)
private Long id;
private String name;
}
無主鍵
@Entity
public class DBUserNoKey {
private String name;
private String password;
}
新增資料(dao層不用寫,點選build自動生成)
有無主鍵相同
DBManager.getMyDBDao().insert(user);
ToastUtil.shorts("儲存成功");
刪除
有主鍵
DBManager.getMyDBDao().delete(user);
無主鍵(必須sql,否則失敗)
DBManager.getSession().getDatabase().execSQL("delete from DBUSER_NO_KEY where name='"+userName+"'");
修改
有主鍵
DBManager.getMyDBDao().update(oldUser);
無主鍵(一樣必須SQL,不sql就bug)
DBManager.getSession().getDatabase()
.execSQL("update DBUSER_NO_KEY set password=? where name=?"
,new Object[]{newUser.getPassword(),newUser.getName()});
查詢
有無主鍵相同
Query<DBUserNoKey> query= DBManager.getNoKeyDao().queryBuilder().build();
List<DBUserNoKey> dbUsers = query.list();
for(DBUserNoKey dbUser:dbUsers){
ToastUtil.longs("id="+dbUser.getName()+"\nname="+dbUser.getPassword());
}
補充,greendao的版本配置在app的build.gradle裡面
greendao {
schemaVersion 3//此處為版本號
daoPackage ‘生成dao等資料庫檔案包名’
targetGenDir ‘src/main/java’
}