(dao, tablePrefix);
stringOrderCollation = " COLLATE NOCASE";
}
让继续看看QueryBuilder对象的list()方法:
public List list() {
return build().list();
}
这个方法其实分为两部分:
- 首先调用build方构建SQL以及Query实例;
- 然后调用list方法得到查询救过
下面我们就分为这两个部分来讲解,本文的重点在第一部分,第二部分将在后续的文章中为大家介绍。
1. 如何生成select语句
/**
* Builds a reusable query object
* Query objects can be executed more efficiently
* than creating a QueryBuilder for each execution.
*/
public Query build() {
//生成查询语句
StringBuilder builder = createSelectBuilder();
//获得返回结果的限制数,即最多返回几行数据
int limitPosition = checkAddLimit(builder);
//获得开始查询的位移行数,即从第几行开始查询
int offsetPosition = checkAddOffset(builder);
String sql = builder.toString();
//检查是否输出所生成的sql语句,以及对应的参数
checkLog(sql);
//创建Query对象,通过该对象来正真地查询数据
return Query.create(dao, sql, values.toArray(), limitPosition, offsetPosition);
}
bulid()方法中做了好多事情,简而言之就是在生成了查询语句,并以此返回Query对象:
- 生成包含select语句的StringBuilder;
- 为select语句添加LIMIT部分和OFFSET部分;
- 以String形式产生完整的select sql语句,并根据设置在log中输出sql语句;
- 以线程为单位生成Query类实例。
咱们一一来看。
1.1 createSelectBuilder()
首先createSelectBuilder()方法用来生成select语句,并将生成的select语句保存在StringBuilder中:
private StringBuilder createSelectBuilder() {
//构造select语句主题部分,包括从那个表中查询哪些字段
String select = SqlUtils.createSqlSelect(dao.getTablename(), tablePrefix, dao.getAllColumns(), distinct);
StringBuilder builder = new StringBuilder(select);
//向select语句中添加联合查询以及条件查询部分
appendJoinsAndWheres(builder, tablePrefix);
//向select语句中添加order部分
if (orderBuilder != null && orderBuilder.length() > 0) {
builder.append(" ORDER BY ").append(orderBuilder);
}
return builder;
}
如果读者对于SQL语句很熟悉,应该已经想到* SqlUtils.createSqlSelect*方法在做些什么了,就是根据要查询的字段生成sql语句,以下是源码:
/** Creates an select for given columns with a trailing space */
public static String createSqlSelect(String tablename, String tableAlias, String[] columns, boolean distinct) {
//判断表的别名是否存在,如果表别名无效,则抛出异常
if (tableAlias == null || tableAlias.length() < 0) {
throw new DaoException("Table alias required");
}
// 根据distinct的值,来判断是否要在select语句中加入“ DISTINCT”关键字
StringBuilder builder = new StringBuilder(distinct ? "SELECT DISTINCT " : "SELECT ");
// 添加要查询的列,以及关键字“FROM”
SqlUtils.appendColumns(builder, tableAlias, columns).append(" FROM ");
// 添加表名和表的别名
builder.append('"').append(tablename).append('"').append(' ').append(tableAlias).append(' ');
return builder.toString();
}
createSqlSelect方法返回的sql语句是select语句,相当于是产生“select * from tableName ”,其本质就是通过StringBuilder构造String对象。这里需要解释的是为什么表的别名* tableAlias*是必须的,这是为了在处理多表联合查询时方便处理,多表的联合查询中往往需要给表取别名以方便构建SQL,因此在此处要求必须有别名,所以对于单表查询没有意义,但是在联合查询中却很有帮助,这里也体现了greenDAO设计团队的良苦用心。
下面我们来看下* appendJoinsAndWheres*方法,顾名思义这个方法是为selelct语句添加联合查询和where语句部分:
private void appendJoinsAndWheres(StringBuilder builder, String tablePrefixOrNull) {
//清空sql语句参数
values.clear();
//添加表连接部分
for (Join 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);
}
//根据whereAppended的值,添加where条件
boolean whereAppended = !whereCollector.isEmpty();
if (whereAppended) {
builder.append(" WHERE ");
whereCollector.appendWhereClause(builder, tablePrefixOrNull, values);
}
// 添加连接条件
for (Join join : joins) {
if (!join.whereCollector.isEmpty()) {
if (!whereAppended) {
builder.append(" WHERE ");
whereAppended = true;
} else {
builder.append(" AND ");
}
join.whereCollector.appendWhereClause(builder, join.tablePrefix, values);
}
}
}
这里都是纯粹的SQL语句的生成,相当于是一种编译器,各种条件参数转化为标注的SQL语句,不难理解,就不在赘言了。如果理解有困难,建议去参看关于SQL查询的文章。
1.2 LIMIT & OFFSET
首先要说明下LIMIT & OFFSET的意义,这部分在SQL语句中不是那么常见。假设数据库表student存在13条数据。
语句1:select * from student limit 9,4
语句2:slect * from student limit 4 offset 9
语句1和2均返回表student的第10、11、12、13行,语句2中的4表示返回4行,9表示从表的第十行开始。也就是说 LIMIT表示查询结果返回的行数的限制,OFFSET表示开始从第几行开始查询。注意OFFSET关键字必须和LIMIT联合使用,不能单独使用。
checkAddLimit和* checkAddOffset*方法的代码相似,就放在一起分析:
private int checkAddLimit(StringBuilder builder) {
int limitPosition = -1; //limitPosition表示 LIMIT的值在List