需要掌握的基本:
1.反射
2.泛型
3.註解
4.clone
greenDAO 使用「Code generation(代碼生成)」的方式,這也是其性能能大幅提升的原因。
Java的Code Generation:
拼字節碼,讀懂JAVA的CLASS文件的字節碼規範,然後自己按規範拼一個一模一樣的字節碼,然後用ClassLoader的defineClass讀入並生成
————————————————
看完源碼:
1.修改數據庫的路徑
2.創建不同數據庫對應不同的table
3.怎麼保證強原子性的
4.自己寫帶有緩存的處理
自定義SQL帶來的數據不同步問題
有沒有insertOrUpdate呢,insertOrReplace有的時候會把之前的字段清空了
初始化的步驟:
1.helper對象
2.拿到Db對象
3.得到master對象
4.得到dao對象
private void setDatabase() { // 通過 DaoMaster 的內部類 DevOpenHelper,你可以得到一個便利的 SQLiteOpenHelper 對象。 // 可能你已經注意到了,你並不需要去編寫「CREATE TABLE」這樣的 SQL 語句,因爲 greenDAO已經幫你做了。 // 注意:默認的 DaoMaster.DevOpenHelper 會在數據庫升級時,刪除所有的表,意味着這將導致數據的丟失。 // 所以,在正式的項目中,你還應該做一層封裝,來實現數據庫的安全升級。 mHelper = new DaoMaster.DevOpenHelper(this, "notes-db", null); db = mHelper.getWritableDatabase(); // 注意:該數據庫連接屬於 DaoMaster,所以多個 Session 指的是相同的數據庫連接。 mDaoMaster = new DaoMaster(db); mDaoSession = mDaoMaster.newSession(); setDebugMode(true);
DaoMaster
DaoMaster是GreenDao的入口
mHelper = new DaoMaster.DevOpenHelper(this, "notes-db", null);
/** Creates underlying database table using DAOs. */ public static void createAllTables(Database db, boolean ifNotExists) { UserDao.createTable(db, ifNotExists); } /** Drops underlying database table using DAOs. */ public static void dropAllTables(Database db, boolean ifExists) { UserDao.dropTable(db, ifExists); } /** * WARNING: Drops all table on Upgrade! Use only during development. * Convenience method using a {@link DevOpenHelper}. */ public static DaoSession newDevSession(Context context, String name) { Database db = new DevOpenHelper(context, name).getWritableDb(); DaoMaster daoMaster = new DaoMaster(db); return daoMaster.newSession(); } public DaoMaster(SQLiteDatabase db) { this(new StandardDatabase(db)); } public DaoMaster(Database db) { super(db, SCHEMA_VERSION); registerDaoClass(UserDao.class); } public DaoSession newSession() { return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); } public DaoSession newSession(IdentityScopeType type) { return new DaoSession(db, type, daoConfigMap); } /** * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} - */ public static abstract class OpenHelper extends DatabaseOpenHelper { public OpenHelper(Context context, String name) { super(context, name, SCHEMA_VERSION); } public OpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory, SCHEMA_VERSION); } @Override public void onCreate(Database db) { Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); createAllTables(db, false); } } /** WARNING: Drops all table on Upgrade! Use only during development. */ public static class DevOpenHelper extends OpenHelper { public DevOpenHelper(Context context, String name) { super(context, name); } public DevOpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); dropAllTables(db, true); onCreate(db); } }
可以看出來:主要是創建數據庫,創建表,還有數據庫的升級
數據庫創建和我們原生的一樣,用SQLiteOpenHelper
public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {
getDatabaseLocked
private SQLiteDatabase getDatabaseLocked(boolean writable) {
// 首先方法接收一個是否可讀的參數
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
//數據庫沒有打開,關閉並且置空
mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {
//只讀或者數據庫已經是讀寫狀態了,則直接返回實例
return mDatabase;
}
}
注意:得到數據庫都時候,如果沒有打開,先關閉並且質空,然後重新開。
DaoMaster構造方法中會把所有的Dao類註冊到Map中,每個Dao對應一個DaoConfig配置類。
public abstract class AbstractDaoMaster { protected final Database db; protected final int schemaVersion; protected final Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap; public AbstractDaoMaster(Database db, int schemaVersion) { this.db = db; this.schemaVersion = schemaVersion; daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>(); } protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) { DaoConfig daoConfig = new DaoConfig(db, daoClass); daoConfigMap.put(daoClass, daoConfig); }
userDaoConfig = daoConfigMap.get(UserDao.class).clone();
public final class DaoConfig implements Cloneable {
Cloneable接口是一個標記接口,也就是沒有任何內容,定義如下:
這裏分析一下這個接口的用法,clone方法是在Object種定義的,而且是protected型的,只有實現了這個接口,纔可以在該類的實例上調用clone方法,否則會拋出CloneNotSupportException。Object中默認的實現是一個淺拷貝,也就是表面拷貝,如
public DaoConfig(Database db, Class<? extends AbstractDao<?, ?>> daoClass) { this.db = db; try { this.tablename = (String) daoClass.getField("TABLENAME").get(null); Property[] properties = reflectProperties(daoClass); this.properties = properties; allColumns = new String[properties.length]; List<String> pkColumnList = new ArrayList<String>(); List<String> nonPkColumnList = new ArrayList<String>(); Property lastPkProperty = null; for (int i = 0; i < properties.length; i++) { Property property = properties[i]; String name = property.columnName; allColumns[i] = name; if (property.primaryKey) { pkColumnList.add(name); lastPkProperty = property; } else { nonPkColumnList.add(name); } } String[] nonPkColumnsArray = new String[nonPkColumnList.size()]; nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray); String[] pkColumnsArray = new String[pkColumnList.size()]; pkColumns = pkColumnList.toArray(pkColumnsArray); pkProperty = pkColumns.length == 1 ? lastPkProperty : null; statements = new TableStatements(db, tablename, allColumns, pkColumns); if (pkProperty != null) { Class<?> type = pkProperty.type; keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class) || type.equals(Integer.class) || type.equals(short.class) || type.equals(Short.class) || type.equals(byte.class) || type.equals(Byte.class); } else { keyIsNumeric = false; } } catch (Exception e) { throw new DaoException("Could not init DAOConfig", e); } }
public class UserDao extends AbstractDao<User, Long> {
AbstractDao
public abstract class AbstractDao<T, K> { protected final DaoConfig config; protected final Database db; protected final boolean isStandardSQLite; protected final IdentityScope<K, T> identityScope; protected final IdentityScopeLong<T> identityScopeLong; protected final TableStatements statements; protected final AbstractDaoSession session; protected final int pkOrdinal; private volatile RxDao<T, K> rxDao; private volatile RxDao<T, K> rxDaoPlain;
long insert(T entity) { return executeInsert(entity, statements.getInsertStatement(), true); }
public void update(T entity) { assertSinglePk(); DatabaseStatement stmt = statements.getUpdateStatement(); if (db.isDbLockedByCurrentThread()) { synchronized (stmt) { if (isStandardSQLite) { updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true); } else { updateInsideSynchronized(entity, stmt, true); } } } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { synchronized (stmt) { updateInsideSynchronized(entity, stmt, true); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } }
插入的時候用實務的操作
private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) { long rowId; if (db.isDbLockedByCurrentThread()) { rowId = insertInsideTx(entity, stmt); } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { rowId = insertInsideTx(entity, stmt); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } if (setKeyAndAttach) { updateKeyAfterInsertAndAttach(entity, rowId, true); } return rowId; }
private long insertInsideTx(T entity, DatabaseStatement stmt) { synchronized (stmt) { if (isStandardSQLite) { SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement(); bindValues(rawStmt, entity); return rawStmt.executeInsert(); } else { bindValues(stmt, entity); return stmt.executeInsert(); } } }
@Override protected final void bindValues(DatabaseStatement stmt, User entity) { stmt.clearBindings(); Long id = entity.getId(); if (id != null) { stmt.bindLong(1, id); } String name = entity.getName(); if (name != null) { stmt.bindString(2, name); } }
描述index和數據的關係,最終保存進mBindArgs。到這裏,應該就能明白greenDao的核心作用。greenDao將我們熟悉的對象,轉換成sql語句和執行參數,再提交SQLite執行。
更新數據庫:如果當前線程持有,可以直接更新,然後被別的線程持有,要通過實務的操作
}
當前線程獲取數據庫鎖的情況下,直接執行insert操作即可,否則需要使用事務保證操作的原子性和一致性。
public void update(T entity) { assertSinglePk(); DatabaseStatement stmt = statements.getUpdateStatement(); if (db.isDbLockedByCurrentThread()) { synchronized (stmt) { if (isStandardSQLite) { updateInsideSynchronized(entity, (SQLiteStatement) stmt.getRawStatement(), true); } else { updateInsideSynchronized(entity, stmt, true); } } } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { synchronized (stmt) { updateInsideSynchronized(entity, stmt, true); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } }
生成Sql
sql的獲取需要用到TableStatements,
實體類:
public class UserDao extends AbstractDao<User, Long> {
緩存:
}
在執行真正的數據加載前,標記1處先查找緩存,如果有就直接返回,無就去查數據庫。標記2處準備sql語句和參數,交給rawQuery查詢,得到Cursor。
AbstractDao裏面:
public T load(K key) { assertSinglePk(); if (key == null) { return null; } if (identityScope != null) { T entity = identityScope.get(key); if (entity != null) { return entity; } } String sql = statements.getSelectByKey(); String[] keyArray = new String[]{key.toString()}; Cursor cursor = db.rawQuery(sql, keyArray); return loadUniqueAndCloseCursor(cursor); }
是一個接口,相當於一個map,增刪改查。
/** * Common interface for a identity scopes needed internally by greenDAO. Identity scopes let greenDAO re-use Java * objects. * * @author Markus * * @param <K> * Key * @param <T> * Entity */ public interface IdentityScope<K, T> { T get(K key); void put(K key, T entity); T getNoLock(K key); void putNoLock(K key, T entity); boolean detach(K key, T entity); void remove(K key); void remove(Iterable<K> key); void clear(); void lock(); void unlock(); void reserveRoom(int count); }
清除緩存:
identityScope 一個map清除
public void deleteAll() { // String sql = SqlUtils.createSqlDelete(config.tablename, null); // db.execSQL(sql); db.execSQL("DELETE FROM '" + config.tablename + "'"); if (identityScope != null) { identityScope.clear(); } }