GreenDao源码详解第二篇(sqlite数据库操作流程分析)

上一篇我们知道,GreenDao利用FreeMarker帮我们产生了数据库辅助类,那么以这几个类怎么用为入口,进行我们的源码分析,look代码,来一个简单的条件查询

 DaoMaster.DevOpenHelper d=new DaoMaster.DevOpenHelper(MainActivity.this, "sq");
      DaoMaster m=new DaoMaster(d.getWritableDatabase());
      DaoSession daoSession=m.newSession();
      CustomerDao dao=daoSession.getCustomerDao();
      List<Customer> listCus=dao.queryBuilder().where(CustomerDao.Properties.Id.eq(1)).list();

不管我们添加了几个表映射,都会产生一个 DaoMaster类,一个 DaoSession类,和多个表映射类和响应的Dao操作类,看代码流程大约可以看出,如果我们想要操作一个表的话首先得得到 DaoMaster,然后通过DaoMaster得到 DaoSession,相当于一个会话,从这个会话中,我们可以得到不同表的操作类Dao。

首先看一下DevOpenHelper是干嘛的?

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);
		}

		/**
		 * 这个方法怎么这么熟悉,这不是操作sqlite的数据库升级的回调接口吗
		 */
		@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);
		}
	}
点进这个静态内部类,发现了一个非常熟悉的类,额猜一下,那么这个静态内部类就是用来操作Sqlite是的。
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);
		}
	}

进入父类又有一个父类,擦

/**
 * 这个是啥不就是操作sqlite的原生类吗
 * @author Administrator
 *
 */
public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {

    private final Context context;
    private final String name;
    private final int version;

    private EncryptedHelper encryptedHelper;
    private boolean loadSQLCipherNativeLibs = true;

    public DatabaseOpenHelper(Context context, String name, int version) {
        this(context, name, null, version);
    }

    public DatabaseOpenHelper(Context context, String name, CursorFactory factory, int version) {
        super(context, name, factory, version);
        this.context = context;
        this.name = name;
        this.version = version;
    }

果然不出所料,最终就是通过android的原生操作Sqlite的

/**
	 * 得到包装读、写
	 */
	public Database getWritableDb() {
		return wrap(getWritableDatabase());

	}

	/**
	 * 得到包装的读类
	 * 
	 * @return
	 */
	public Database getReadableDb() {
		return wrap(getReadableDatabase());
	}
其中这两个方法为我们返回原生的操作数据库的类,而在原有数据类的基础上包装了一个代理类,不用说肯定是干点坏事了

 * 
	 * @param sqLiteDatabase
	 *            包装一下数据库操作类,
	 * @return
	 */
	protected Database wrap(SQLiteDatabase sqLiteDatabase) {
		return new StandardDatabase(sqLiteDatabase);
	}
向下面这个代理,为客户端返回加密的数据库操作类,用的是开源的 SQLCipher

/**
	用 SQLCipher进行加密
	 */
	public Database getEncryptedWritableDb(String password) {
		EncryptedHelper encryptedHelper = checkEncryptedHelper();
		return encryptedHelper.wrap(encryptedHelper
				.getWritableDatabase(password));
	}

	/**
	/**
	用 SQLCipher进行加密
	 */
	public Database getEncryptedWritableDb(char[] password) {
		EncryptedHelper encryptedHelper = checkEncryptedHelper();
		return encryptedHelper.wrap(encryptedHelper
				.getWritableDatabase(password));
	}
最终返回的是SQLCipher提供的类

	private class EncryptedHelper extends
			net.sqlcipher.database.SQLiteOpenHelper {
		public EncryptedHelper(Context context, String name, int version,
				boolean loadLibs) {
			super(context, name, null, version);
			if (loadLibs) {
				net.sqlcipher.database.SQLiteDatabase.loadLibs(context);
			}
		}

		@Override
		public void onCreate(net.sqlcipher.database.SQLiteDatabase db) {
			DatabaseOpenHelper.this.onCreate(wrap(db));
		}

		@Override
		public void onUpgrade(net.sqlcipher.database.SQLiteDatabase db,
				int oldVersion, int newVersion) {
			DatabaseOpenHelper.this.onUpgrade(wrap(db), oldVersion, newVersion);
		}

		@Override
		public void onOpen(net.sqlcipher.database.SQLiteDatabase db) {
			DatabaseOpenHelper.this.onOpen(wrap(db));
		}

		protected Database wrap(
				net.sqlcipher.database.SQLiteDatabase sqLiteDatabase) {
			return new EncryptedDatabase(sqLiteDatabase);
		}

	}
对SQLCipher不熟悉的可以去官方查看用法 SQLCipher
接下来就涉及到DaoMaster的初始化了

public DaoMaster(Database db) {
		super(db, SCHEMA_VERSION);
		registerDaoClass(NoteDao.class);
		registerDaoClass(CustomerDao.class);
		registerDaoClass(OrderDao.class);
	}

此处SCHEMA_VERSION的值为3,记得上一篇我们设置数据库版本号为3,初始化的DaoMaster持有一个数据库操作对象,那么接下来开始注册表的操作类,有多少个表就注册多少次

 protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
        DaoConfig daoConfig = new DaoConfig(db, daoClass);
        daoConfigMap.put(daoClass, daoConfig);
    }

最终就是把Dao类保存进集合中,接下来看一下 DaoSession

public DaoSession newSession() {
		return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
	}

创建这个会话类传入数据库操作对象和保存 Dao的集合的引用,接着看一下DaoSession类的构造方法都做了什么

 public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);

        noteDaoConfig = daoConfigMap.get(NoteDao.class).clone();
        noteDaoConfig.initIdentityScope(type);

        customerDaoConfig = daoConfigMap.get(CustomerDao.class).clone();
        customerDaoConfig.initIdentityScope(type);

        orderDaoConfig = daoConfigMap.get(OrderDao.class).clone();
        orderDaoConfig.initIdentityScope(type);

        noteDao = new NoteDao(noteDaoConfig, this);
        customerDao = new CustomerDao(customerDaoConfig, this);
        orderDao = new OrderDao(orderDaoConfig, this);

        registerDao(Note.class, noteDao);
        registerDao(Customer.class, customerDao);
        registerDao(Order.class, orderDao);
    }

这个构造函数主要做了创建各个表的操作对象,然后和表的映射类绑定存到集合中,并且将参数属性全部放进Config与Dao类绑定。既然表操作的Dao类都创建了,那么接下来增删改查都用dao来操作,先看一下插入 dao.insert(entity),进入


 public long insert(T entity) {
        return executeInsert(entity, statements.getInsertStatement(), true);
    }

 private long executeInsert(T entity, DatabaseStatement stmt, boolean setKeyAndAttach) {
        long rowId;
        //判断数据库是否被其他线程锁上
        if (db.isDbLockedByCurrentThread()) {
            rowId = insertInsideTx(entity, stmt);
        } else {
           //开启事务插入数据
            db.beginTransaction();
            try {
                rowId = insertInsideTx(entity, stmt);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
        /**
         * 插入完之后保存插入的行号,行号就是自动增长的id
         */
        if (setKeyAndAttach) {
            updateKeyAfterInsertAndAttach(entity, rowId, true);
        }
        return rowId;
    }
这里出现了一个关键的对象 DatabaseStatement,这个对象是干嘛的呢?上边提到一个普通的数据库类和一个加密的数据库类,sqlcipher此篇幅暂且不讨论,那么DatabaseStatement的实现类为

public class StandardDatabaseStatement implements DatabaseStatement {
    private final SQLiteStatement delegate;

    public StandardDatabaseStatement(SQLiteStatement delegate) {
        this.delegate = delegate;
    }

用到了静态代理,真正操作数据库的是SQLiteStatement ,android自带的数据库操作类,那么又有问题了为什么不用sqlite的最常用的插入语句呢,比如

sqliteDatabase.insert(table, nullColumnHack, values);
我看一下源码怎么实现的
public long insertWithOnConflict(String table, String nullColumnHack,
            ContentValues initialValues, int conflictAlgorithm) {
        acquireReference();
        try {
          ...此处省略30行代码

            SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
            try {
                return statement.executeInsert();
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    }

源码最终还是创建了SQLiteStatement对象来操作数据库,那么GreenDao的第二个数据库操作快的优势体现出来了,用SQLiteStatement的话,代码执行量就少了很多,少一行代码,速度就快上毫秒级,肯定同样的功能代码量少的实现方式会快一些,那么积少成多,操作数据千条万条的时候,速度就明显的被看出来,看一下直接执行sql语句是不是也是创建了此对象

sqliteDatabase.execSQL(sql);

private int executeSql(String sql, Object[] bindArgs) throws SQLException {
        acquireReference();
        try {
            if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
                boolean disableWal = false;
                synchronized (mLock) {
                    if (!mHasAttachedDbsLocked) {
                        mHasAttachedDbsLocked = true;
                        disableWal = true;
                    }
                }
                if (disableWal) {
                    disableWriteAheadLogging();
                }
            }
//没错又是它
            SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
            try {
                return statement.executeUpdateDelete();
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    }

没错又是这个家伙这就是GreenDao比起其他数据库操作框架快的原因之一了吧。


 private long insertInsideTx(T entity, DatabaseStatement stmt) {
        synchronized (stmt) {
            if (isStandardSQLite) {
            	/**
            	 * 得到SQLiteStatement
            	 */
                SQLiteStatement rawStmt = (SQLiteStatement) stmt.getRawStatement();
                /**
                 * 绑定数据
                 */
                bindValues(rawStmt, entity);
                //执行插入操作
                return rawStmt.executeInsert();
            } else {
                bindValues(stmt, entity);
                return stmt.executeInsert();
            }
        }
    }


这个方法是将对象的数据绑定到第几列数据


protected final void bindValues(DatabaseStatement stmt, Customer entity) {
    	//清除绑定
        stmt.clearBindings();
 
        Long id = entity.getId();
        if (id != null) {
            stmt.bindLong(1, id);
        }
        stmt.bindString(2, entity.getName());
    }


最终awStmt.executeInsert()将保存到SQLiteStatement的数据插入到数据库表中,插入数据看完了,看看改数据

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();
            }
        }
    }


进入updateInsideSynchronized方法


 protected void updateInsideSynchronized(T entity, SQLiteStatement stmt, boolean lock) {
        // To do? Check if it's worth not to bind PKs here (performance).
    	//绑定数据
        bindValues(stmt, entity);
        int index = config.allColumns.length + 1;
        K key = getKey(entity);
        if (key instanceof Long) {
            stmt.bindLong(index, (Long) key);
        } else if (key == null) {
            throw new DaoException("Cannot update entity without key - was it inserted before?");
        } else {
            stmt.bindString(index, key.toString());
        }
        //执行更新操作
        stmt.execute();
        attachEntity(key, entity, lock);
    }


和插入类似,只不过检查了key值必须存在,delete删除方法也类似,最后看一下查询方法

List<Customer> listCus=dao.queryBuilder().where(CustomerDao.Properties.Id.eq(1)).list();

这句代码查询出id等于1的customer的一行数据,其中queryBuilder()方法创建查询的辅助类


/**
 * 创建查询查询的辅助类
 * @return
 */
    public QueryBuilder<T> queryBuilder() {
        return QueryBuilder.internalCreate(this);
    }

看命名是使用了构造者模式,接着进入internalCreate方法

 public static <T2> QueryBuilder<T2> internalCreate(AbstractDao<T2, ?> dao) {
        return new QueryBuilder<T2>(dao);
    }

继续看where(CustomerDao.Properties.Id.eq(1))

/*
    * 保存条件进集合
    */
    public QueryBuilder<T> where(WhereCondition cond, WhereCondition... condMore) {
        whereCollector.add(cond, condMore);
        return this;
    }

看完这一个方法后,大体可以猜测到QueryBuilder这个类主要用于暂时保存需要查询的拼接参数,而CustomerDao.Properties.Id.eq(1)返回PropertyCondition对象用于保存查询条件的类,那么真正实现查询的是list()方法了,看到这我们可以大胆的猜测一下,下一步就是将保存在QueryBuilder中的参数进行拼接转化为sql语句,然后进行查询了


/**
  * 拼接sql语句
  * @return
  */
    public Query<T> build() {
        StringBuilder builder = createSelectBuilder();
        int limitPosition = checkAddLimit(builder);
        int offsetPosition = checkAddOffset(builder);

        String sql = builder.toString();
        checkLog(sql);

        return Query.create(dao, sql, values.toArray(), limitPosition, offsetPosition);
    }

 private void appendJoinsAndWheres(StringBuilder builder, String tablePrefixOrNull) {
        values.clear();
        for (Join<T, ?> join : joins) {
            builder.append(" JOIN ").append(join.daoDestination.getTablename()).append(' ');
            builder.append(join.tablePrefix).append(" ON ");
            SqlUtils.appendProperty(builder, join.sourceTablePrefix, join.joinPropertySource).append('=');
            SqlUtils.appendProperty(builder, join.tablePrefix, join.joinPropertyDestination);
        }
        boolean whereAppended = !whereCollector.isEmpty();
        if (whereAppended) {
            builder.append(" WHERE ");
            whereCollector.appendWhereClause(builder, tablePrefixOrNull, values);
        }
        for (Join<T, ?> join : joins) {
            if (!join.whereCollector.isEmpty()) {
                if (!whereAppended) {
                    builder.append(" WHERE ");
                    whereAppended = true;
                } else {
                    builder.append(" AND ");
                }
                join.whereCollector.appendWhereClause(builder, join.tablePrefix, values);
            }
        }
    }


所有的方法都证明了QueryBuilder就是用来拼接sql语句的,那么继续

public List<T> list() {
        checkThread();
        /**
         * 开始查询数据
         */
        Cursor cursor = dao.getDatabase().rawQuery(sql, parameters);
        //将数据赋值给对象
        return daoAccess.loadAllAndCloseCursor(cursor);
    }


 public Customer readEntity(Cursor cursor, int offset) {
        Customer entity = new Customer( //
            cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
            cursor.getString(offset + 1) // name
        );
        return entity;
    }


果然像我们猜想的那样,到此sqlite数据库操作流程分析就完成了,现在可以试着写一个自己的数据库操作库了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值