Android xUtils3源码解析之数据库模块

来源:互联网 发布:淘宝有毛片吗 编辑:程序博客网 时间:2024/05/21 05:57

本文已授权微信公众号《非著名程序员》原创首发,转载请务必注明出处。

xUtils3源码解析系列

一. Android xUtils3源码解析之网络模块
二. Android xUtils3源码解析之图片模块
三. Android xUtils3源码解析之注解模块
四. Android xUtils3源码解析之数据库模块

配置数据库

    DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()            .setDbName("test.db")            .setDbVersion(1)            .setDbOpenListener(new DbManager.DbOpenListener() {                @Override                public void onDbOpened(DbManager db) {                    // 开启WAL, 对写入加速提升巨大                    db.getDatabase().enableWriteAheadLogging();                }            })            .setDbUpgradeListener(new DbManager.DbUpgradeListener() {                @Override                public void onUpgrade(DbManager db, int oldVersion, int newVersion) {                    ...                }            });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

xUtil3支持数据库多库的配置,使用不同的DaoConfig,可以创建多个.db文件,每个.db文件彼此独立。

数据库操作

初始化

由于xUtils3设计的是在需要使用数据库的时候,才创建数据表。所以下文以save操作为例,跟进初始化数据表的过程。示例代码:

DbManager db = x.getDb(daoConfig);Parent parent = new Parent();parent.setName("CSDN 一口仨馍");db.save(parent);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

数据库的操作比较耗时,真实应该异步执行。可以看到,xUtils3提供的数据库操作是非常简单的,首先getDb,之后调用save()方法即可。其中save方法接受List

创建数据库文件

x.getDb(daoConfig)

public final class x {    public static DbManager getDb(DbManager.DaoConfig daoConfig) {        return DbManagerImpl.getInstance(daoConfig);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

这里只是简单的返回了一个DbManagerImpl实例,看样子真正的初始化操作都在DbManagerImpl里。跟进。

public final class DbManagerImpl extends DbBase {    private DbManagerImpl(DaoConfig config) {        if (config == null) {            throw new IllegalArgumentException("daoConfig may not be null");        }        this.daoConfig = config;        this.allowTransaction = config.isAllowTransaction();        this.database = openOrCreateDatabase(config);        DbOpenListener dbOpenListener = config.getDbOpenListener();        if (dbOpenListener != null) {            dbOpenListener.onDbOpened(this);        }    }    public synchronized static DbManager getInstance(DaoConfig daoConfig) {        if (daoConfig == null) {//使用默认配置            daoConfig = new DaoConfig();        }        DbManagerImpl dao = DAO_MAP.get(daoConfig);        if (dao == null) {            dao = new DbManagerImpl(daoConfig);            DAO_MAP.put(daoConfig, dao);        } else {            dao.daoConfig = daoConfig;        }        // update the database if needed        SQLiteDatabase database = dao.database;        int oldVersion = database.getVersion();        int newVersion = daoConfig.getDbVersion();        if (oldVersion != newVersion) {            if (oldVersion != 0) {                DbUpgradeListener upgradeListener = daoConfig.getDbUpgradeListener();                if (upgradeListener != null) {                    upgradeListener.onUpgrade(dao, oldVersion, newVersion);                } else {                    try {                        dao.dropDb();                    } catch (DbException e) {                        LogUtil.e(e.getMessage(), e);                    }                }            }            database.setVersion(newVersion);        }        return dao;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

乍一看代码有些长,其实也没做太多操作,绝大部分是些缓存赋值相关的操作。这里注意两个地方

  1. 在数据库版本更新时,如果没有设置DbUpgradeListener,那么在更新的时候会直接删除旧表。
  2. 在获取DbManagerImpl实例的时候,创建了数据库,例如:“test.db”。如果指定了数据库的位置(通过DaoConfig#setDbDir()),则在指定位置创建,默认在data/data/package name/database/下创建。

由于返回的是DbManagerImpl实例,所以实际调用的是DbManagerImpl.save()。

DbManagerImpl.save()

public final class DbManagerImpl extends DbBase {    public void save(Object entity) throws DbException {        try {            // 开启事务            beginTransaction();            // 判断将要保存的是对象还是对象的集合            if (entity instanceof List) {                // 向上转型为List                List<?> entities = (List<?>) entity;                if (entities.isEmpty()) return;                // 依据被注解的类获取数据表对应的包装类                TableEntity<?> table = this.getTable(entities.get(0).getClass());                // 如果没有表则创建                createTableIfNotExist(table);                // 遍历插入数据库                for (Object item : entities) {                    // 拼接sql语句,执行数据库插入操作                    execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, item));                }            } else {                TableEntity<?> table = this.getTable(entity.getClass());                createTableIfNotExist(table);                execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, entity));            }            // 设置事务成功            setTransactionSuccessful();        } finally {            // 结束事务            endTransaction();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

接下来每行都有注释,这些是我在看的过程中写下的。我只说贴代码的逻辑吧。先看下创建TableEntity

JavaBean到TableEntity的转化

创建表的包装类

public final class TableEntity<T> {    /*package*/ TableEntity(DbManager db, Class<T> entityType) throws Throwable {        this.db = db;        this.entityType = entityType;        this.constructor = entityType.getConstructor();        this.constructor.setAccessible(true);        // 被保存的类没有没Table注解,这里会抛出NullPointerException。        // ps:作者这里应该验证下为null的问题        Table table = entityType.getAnnotation(Table.class);        // 获取表名        this.name = table.name();        // 获取创建表之后执行的SQL语句        this.onCreated = table.onCreated();        // 获取列Map,Map<列的类型,列的包装类>        this.columnMap = TableUtils.findColumnMap(entityType);        // 遍历查找列的包装类,直到找到id列        for (ColumnEntity column : columnMap.values()) {            if (column.isId()) {                this.id = column;                break;            }        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这里涉及到Table注解,从Table注解中获取表名。之后封装了一个Map,key为列名,value为列的包装类,例如:Map

/* package */ final class TableUtils {    static synchronized LinkedHashMap<String, ColumnEntity> findColumnMap(Class<?> entityType) {        LinkedHashMap<String, ColumnEntity> columnMap = new LinkedHashMap<String, ColumnEntity>();        addColumns2Map(entityType, columnMap);        return columnMap;    }    private static void addColumns2Map(Class<?> entityType, HashMap<String, ColumnEntity> columnMap) {        // 递归出口        if (Object.class.equals(entityType)) return;        try {            // 获取表实体类的所有属性            Field[] fields = entityType.getDeclaredFields();            for (Field field : fields) {                // 获取属性的修饰符                int modify = field.getModifiers();                // 修饰符不能是static或者transient                if (Modifier.isStatic(modify) || Modifier.isTransient(modify)) {                    continue;                }                // 为下面判断属性有没有被Column注解修饰做准备                Column columnAnn = field.getAnnotation(Column.class);                if (columnAnn != null) {                    // 判断属性是否支持转换                    if (ColumnConverterFactory.isSupportColumnConverter(field.getType())) {                        // 新建列(属性)的包装类                        ColumnEntity column = new ColumnEntity(entityType, field, columnAnn);                        if (!columnMap.containsKey(column.getName())) {                            columnMap.put(column.getName(), column);                        }                    }                }            }            // 递归解析属性            addColumns2Map(entityType.getSuperclass(), columnMap);        } catch (Throwable e) {            LogUtil.e(e.getMessage(), e);        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

创建列的包装类

    /**     * @param entityType 实体类     * @param field 属性     * @param column 注解     */    /* package */ ColumnEntity(Class<?> entityType, Field field, Column column) {        // 设置属性可访问        field.setAccessible(true);        this.columnField = field;        // 获取数据库中列的名称,一般和属性值保持一致        this.name = column.name();        // 获取属性的值        this.property = column.property();        // 是否是主键        this.isId = column.isId();        // 获取属性的类型        Class<?> fieldType = field.getType();        // 是否自增,int、Integer、long、Long类型的主键,默认自增        this.isAutoId = this.isId && column.autoGen() && ColumnUtils.isAutoIdType(fieldType);        // String为例,返回的是StringColumnConverter        this.columnConverter = ColumnConverterFactory.getColumnConverter(fieldType);        // 查找get方法。例如:对于age属性,查找getAge()方法        this.getMethod = ColumnUtils.findGetMethod(entityType, field);        if (this.getMethod != null && !this.getMethod.isAccessible()) {            // 设置可反射访问            this.getMethod.setAccessible(true);        }        // 查找set方法        this.setMethod = ColumnUtils.findSetMethod(entityType, field);        if (this.setMethod != null && !this.setMethod.isAccessible()) {            this.setMethod.setAccessible(true);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

数据操作的时候不用每次都这么繁琐,因为表格有tableMap缓存,下次直接就能取出相应的表包装类TableEntity。下面跟进下创建表的过程。

创建数据表

createTableIfNotExist()

    // 创建数据表    protected void createTableIfNotExist(TableEntity<?> table) throws DbException {        // 根据系统表SQLITE_MASTER判断指定表格是否存在        if (!table.tableIsExist()) {            synchronized (table.getClass()) {                // 表不存在                if (!table.tableIsExist()) {                    // 获取创建表格语句                    SqlInfo sqlInfo = SqlInfoBuilder.buildCreateTableSqlInfo(table);                    // 执行创建表格语句                    execNonQuery(sqlInfo);                    // 获取创建表格之后的语句,例如:可用于创建索引。PS:Table注解中的属性                    String execAfterTableCreated = table.getOnCreated();                    if (!TextUtils.isEmpty(execAfterTableCreated)) {                        // 执行创建表之后的语句                        execNonQuery(execAfterTableCreated);                    }                    // 再次设置"表已创建"标志位                    table.setCheckedDatabase(true);                    // 获取监听                    TableCreateListener listener = this.getDaoConfig().getTableCreateListener();                    if (listener != null) {                        // 调用创建表之后的监听                        listener.onTableCreated(this, table);                    }                }            }        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

创建表的语句如下

    public static SqlInfo buildCreateTableSqlInfo(TableEntity<?> table) throws DbException {        ColumnEntity id = table.getId();        StringBuilder builder = new StringBuilder();        builder.append("CREATE TABLE IF NOT EXISTS ");        builder.append("\"").append(table.getName()).append("\"");        builder.append(" ( ");        if (id.isAutoId()) {            builder.append("\"").append(id.getName()).append("\"").append(" INTEGER PRIMARY KEY AUTOINCREMENT, ");        } else {            builder.append("\"").append(id.getName()).append("\"").append(id.getColumnDbType()).append(" PRIMARY KEY, ");        }        Collection<ColumnEntity> columns = table.getColumnMap().values();        for (ColumnEntity column : columns) {            if (column.isId()) continue;            builder.append("\"").append(column.getName()).append("\"");            builder.append(' ').append(column.getColumnDbType());            builder.append(' ').append(column.getProperty());            builder.append(',');        }        builder.deleteCharAt(builder.length() - 1);        builder.append(" )");        return new SqlInfo(builder.toString());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

就是拼接了一条创建数据表的语句,而且使用的是CREATE TABLE IF NOT EXISTS。最后执行下创建表的语句。

    public void execNonQuery(SqlInfo sqlInfo) throws DbException {        SQLiteStatement statement = null;        try {            statement = sqlInfo.buildStatement(database);            statement.execute();        } catch (Throwable e) {            throw new DbException(e);        } finally {            if (statement != null) {                try {                    statement.releaseReference();                } catch (Throwable ex) {                    LogUtil.e(ex.getMessage(), ex);                }            }        }    }    // 绑定SQL语句中"?"对应的值    public SQLiteStatement buildStatement(SQLiteDatabase database) {        SQLiteStatement result = database.compileStatement(sql);        if (bindArgs != null) {            for (int i = 1; i < bindArgs.size() + 1; i++) {                KeyValue kv = bindArgs.get(i - 1);                // 将属性的类型转换为数据库类型,例如String 转换成 TEXT                Object value = ColumnUtils.convert2DbValueIfNeeded(kv.value);                if (value == null) {                    result.bindNull(i);                } else {                    ColumnConverter converter = ColumnConverterFactory.getColumnConverter(value.getClass());                    ColumnDbType type = converter.getColumnDbType();                    switch (type) {                        case INTEGER:                            result.bindLong(i, ((Number) value).longValue());                            break;                        case REAL:                            result.bindDouble(i, ((Number) value).doubleValue());                            break;                        case TEXT:                            result.bindString(i, value.toString());                            break;                        case BLOB:                            result.bindBlob(i, (byte[]) value);                            break;                        default:                            result.bindNull(i);                            break;                    } // end switch                }            }        }        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

save在上述初始化的基础上操作,真正执行save操作的地方在于execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, item))。和创建表的过程类似,使用SqlInfoBuilder.buildInsertSqlInfo()构建一条SQL插入语句,之后执行。跟进看下。

    public static SqlInfo buildInsertSqlInfo(TableEntity<?> table, Object entity) throws DbException {        List<KeyValue> keyValueList = entity2KeyValueList(table, entity);        if (keyValueList.size() == 0) return null;        SqlInfo result = new SqlInfo();        String sql = INSERT_SQL_CACHE.get(table);        if (sql == null) {            StringBuilder builder = new StringBuilder();            builder.append("INSERT INTO ");            builder.append("\"").append(table.getName()).append("\"");            builder.append(" (");            for (KeyValue kv : keyValueList) {                builder.append("\"").append(kv.key).append("\"").append(',');            }            builder.deleteCharAt(builder.length() - 1);            builder.append(") VALUES (");            int length = keyValueList.size();            for (int i = 0; i < length; i++) {                builder.append("?,");            }            builder.deleteCharAt(builder.length() - 1);            builder.append(")");            sql = builder.toString();            result.setSql(sql);            result.addBindArgs(keyValueList);            INSERT_SQL_CACHE.put(table, sql);        } else {            result.setSql(sql);            result.addBindArgs(keyValueList);        }        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

这个方法的作用就是拼接SQL语句:INSERT INTO “tableName”( “key1”,”key2”) VALUES (?,?),之后存入缓存,下次直接从缓存中取出上面拼接的SQL语句。执行的过程和创建表是同一个方法,不再赘述。

示例代码:

DbManager db = x.getDb(daoConfig);db.delete(Parent.class);
  • 1
  • 2
  • 1
  • 2
    @Override    public void delete(Class<?> entityType) throws DbException {        delete(entityType, null);    }    @Override    public int delete(Class<?> entityType, WhereBuilder whereBuilder) throws DbException {        TableEntity<?> table = this.getTable(entityType);        if (!table.tableIsExist()) return 0;        int result = 0;        try {            beginTransaction();            result = executeUpdateDelete(SqlInfoBuilder.buildDeleteSqlInfo(table, whereBuilder));            setTransactionSuccessful();        } finally {            endTransaction();        }        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

因为使用WhereBuilder涉及到查找,而查找的源码还没看,所以这里以删除表中所有数据为例。

创建删除语句

    public static SqlInfo buildDeleteSqlInfo(TableEntity<?> table, WhereBuilder whereBuilder) throws DbException {        StringBuilder builder = new StringBuilder("DELETE FROM ");        builder.append("\"").append(table.getName()).append("\"");        if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {            builder.append(" WHERE ").append(whereBuilder.toString());        }        return new SqlInfo(builder.toString());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

因为这里的WhereBuilder为null,所以返回的是DELETE FROM "tableName",即删除表中所有数据。

示例代码:

DbManager db = x.getDb(daoConfig);Parent parent = new Parent();parent.setName("CSDN 一口仨馍");db.update(parent, "name");
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

update后面照样支持WhereBuilder甚至指定列名,为了方便分析主要流程,这里就简单点来。update方法就不贴了,和前面save过程几乎一样,区别主要在执行的SQL语句不同,下面主要看下更新语句的构建。

    public static SqlInfo buildUpdateSqlInfo(TableEntity<?> table, Object entity, String... updateColumnNames) throws DbException {        List<KeyValue> keyValueList = entity2KeyValueList(table, entity);        if (keyValueList.size() == 0) return null;        HashSet<String> updateColumnNameSet = null;        if (updateColumnNames != null && updateColumnNames.length > 0) {            updateColumnNameSet = new HashSet<String>(updateColumnNames.length);            Collections.addAll(updateColumnNameSet, updateColumnNames);        }        ColumnEntity id = table.getId();        Object idValue = id.getColumnValue(entity);        if (idValue == null) {            throw new DbException("this entity[" + table.getEntityType() + "]'s id value is null");        }        SqlInfo result = new SqlInfo();        StringBuilder builder = new StringBuilder("UPDATE ");        builder.append("\"").append(table.getName()).append("\"");        builder.append(" SET ");        for (KeyValue kv : keyValueList) {            if (updateColumnNameSet == null || updateColumnNameSet.contains(kv.key)) {                builder.append("\"").append(kv.key).append("\"").append("=?,");                result.addBindArg(kv);            }        }        builder.deleteCharAt(builder.length() - 1);        builder.append(" WHERE ").append(WhereBuilder.b(id.getName(), "=", idValue));        result.setSql(builder.toString());        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

倒数第五行表明是依据对象主键的值来查找数据表中对应的行,使用更新语句,数据库实体类(JavaBean)被Column修饰的属性中必须要有isId修饰,而且还必须有值,否则会抛出DbException。拼接出的SQL语句类似于UPDATE "tableName" SET "name"=?,"age"=? WHERE "ID" = '1'。其中的?表示占位符,在执行前被替换成具体的值。

示例代码:

DbManager db = x.getDb(daoConfig);WhereBuilder whereBuilder = WhereBuilder.b("name","=","一口仨馍").and("age","=","18");db.selector(Parent.class).where(whereBuilder).findAll();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

WhereBuilder的作用是构建查找的SQL语句后半段。例如在select * from parent where "name" = '一口仨馍' and "age" = '18'中,WhereBuilder返回的字符串是”name” = ‘一口仨馍’ and “age” = ‘18’。

db.selector()

    @Override    public <T> Selector<T> selector(Class<T> entityType) throws DbException {        return Selector.from(this.getTable(entityType));    }    static <T> Selector<T> from(TableEntity<T> table) {        return new Selector<T>(table);    }    private Selector(TableEntity<T> table) {        this.table = table;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

new了个Selector对象,除了赋值,啥也木干。

Selector.findAll()

    public List<T> findAll() throws DbException {        if (!table.tableIsExist()) return null;        List<T> result = null;        Cursor cursor = table.getDb().execQuery(this.toString());        if (cursor != null) {            try {                result = new ArrayList<T>();                while (cursor.moveToNext()) {                    T entity = CursorUtils.getEntity(table, cursor);                    result.add(entity);                }            } catch (Throwable e) {                throw new DbException(e);            } finally {                IOUtil.closeQuietly(cursor);            }        }        return result;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

乍一看execQuery里的参数吓我一跳,传个this.toString()是什么鬼啊!!

Selector.findAll()

    public String toString() {        StringBuilder result = new StringBuilder();        result.append("SELECT ");        result.append("*");        result.append(" FROM ").append("\"").append(table.getName()).append("\"");        if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {            result.append(" WHERE ").append(whereBuilder.toString());        }        if (orderByList != null && orderByList.size() > 0) {            result.append(" ORDER BY ");            for (OrderBy orderBy : orderByList) {                result.append(orderBy.toString()).append(',');            }            result.deleteCharAt(result.length() - 1);        }        if (limit > 0) {            result.append(" LIMIT ").append(limit);            result.append(" OFFSET ").append(offset);        }        return result.toString();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

在这里拼接的SQL语句(手动冷漠脸)。可以看到查找也支持ORDER BY、LIMIT和OFFSET关键字。

总结

xUtils3的数据库模块,采用Table和Column注解修饰JavaBean,初始化的时候(实际是调用具体操作才会检查是否已经初始化,没有初始化才会执行初始化操作)会依据注解实例化相应的TableEntity和ColumnEntity并添加进缓存,执行增删改查时依据TableEntity和ColumnEntity拼接相应的SQL语句并执行。

原来没有看过ORM框架的源码,外加上自己数据库也渣的一匹,以为ORM框架多难了,以至于最后才分析xUtils3中的数据库模块。愿意看源码,实际稍微花点时间也能看出个大概。没经历会觉得似乎难以逾越,实际上也没有想象的那么难~

xUtils3四大模块到此就全部解析结束了。加上写作,前后大概花了一周工作时间,基本上把类翻了几遍,得益于框架功能比较全面,所以收获还是蛮多的。不敢说自己完全掌握了xUtils3的精髓,至少弄清了xUtils3的许多设计思想,而且从具体的编码中get到不少小技能。总体来说还是比较满意的。如果您看完四篇博客之后,仍有很多疑惑,建议对着博文思路同步阅读源码,实在有不好解决的问题,可以在下面留言,我尽量解答。感谢悉心阅读到最后~