需要掌握的基本:
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(); } }