写点什么

深入了解 QueryDSL

用户头像
邱学喆
关注
发布于: 9 小时前
深入了解QueryDSL

一. 概述

之前一篇了解 JDBC 层之 QueryDSL简单介绍了有关 QueryDSL 的大体的功能,拼装 SQL 和结果映射;这篇将介绍其拼装 SQL 原理以及结果映射;同时也会介绍高级配置;

有关 SQL 生成的采用观察者模式,如果没有这模式不是太清楚的,可以去查阅刘伟大神的四篇访问者模式介绍,其主要很好的利用方法重载这么一个特性来实现该模式。这里不在这儿过多讲解其模式。

二. SQL 生成

2.1 数据结构

我们重点看一些 DefaultQueryMetadata 对象数据结构,这个对象在生成 SQL(SQLSerializer 来生成 SQL)过程,会根据其来相关的属性值来做追加相应的 SQL 内容;

2.2 Expression

上面的两小节,属性值的类型以及方法中都涉及一个关键接口 Expression。如果将其实现类进行完全介绍出来,难度会很大;我这里只把关键的内容介绍,会对其进行有了大概的认识;

2.2.1 Path

该接口是路径表达式,在 SQL 中可以理解为元数据表的表明以及字段名;

该接口的主要的实现类图如下:

其 Path 会根据不同的类型来对应的不同类型的 Path 实现类;这里只列了一些类;

其中 RelationPathBase 是元数据表的数据结构,其会包含表的一些元数据信息,如字段名、索引等信息;

我们通过查看 TimePath、StringPath 等类,都包含了 PathImpl 属性值,所以可以这么认为这些类型的 Path 都是一个适配器,真正存放的内容的是 PathImpl;假设产品表下有商品名称这个字段,那么查看其 PathMetadata 结构如图所示:

2.2.2 FactoryExpression

工厂表达式,用于封装 Select 中 Fields_names 也就是 Path 集合,同时会携带映射结果功能;

这里我们常用的是 QBean 以及 QTuple 这两个对象;

  • QBean 会将查询的结果集映射到指定的目标对象;

  • QTuple 会将查询的结果集映射到 QTuple

我们可以通过 Projections 这个工具类来创建 FactoryExpression 对应的实现类;

2.2.3 Operation

操作符表达式,例如运算符等都会封装这个接口对应的实现类;我们一般都不会直接创建对应的实现类,而是由 Path 实现类(字段对象)的提供的方法去创建对应的 Operation 对象;

例如订单表中有这么一个价格(数值类型),我们汇总满足条件的价格,那么我们可以调用价格对象 sum 方法,那么(NumberPath),代码如下:

public NumberExpression<T> sum() {  if (sum == null) {    //其是创建NumberOperation这么一个对象;    sum = Expressions.numberOperation(getType(), Ops.AggOps.SUM_AGG, mixin);  }  return sum;}
复制代码

该接口的跟 Path 有点类似,很多实现类都是一个空壳,关键的存放内容的是在 OperationImpl;该对象就存放了两个属性值:

  • args 参数

  • operator 操作符

2.2.4 Constant

常量表达式,比较好理解,可以这么理解:SQL 中“?”占位符要填充的值是我们要注入的值,该值一般用这个对象来包装;

2.2.5 SubQueryExpression

子查询表达式,其实现类是 SubQueryExpressionImpl,其内部的属性是 QueryMetadata 接口(其实现类 DefaultQueryMetadata).

2.2.6 TemplateExpression

模板表达式,这里目前没有发现在 query-sql 工程中没有使用到这个,姑且不再介绍;我们主要看其主要的实现类 TemplateExpressionImpl 中包含的属性:

  • template 模板

  • args 参数值

2.2.7 ParamExpression

参数表达式,目前在 query-sql 工程中,并没有使用到这个。这个不再介绍。只介绍其主要的实现类 ParamExpressionImpl 中包含的属性:

  • name 参数名

  • anon

2.2.8 DslExpression

DSL 表达式,很多上面介绍的实现类很多都继承了该接口下的子类,用于操作生产对应的目标对象;就如上面介绍的例子 NumberPath,调用 sum 方法,就是继承 DslExpression 接口下的 NumberExpression 子类。

2.2.9 Predicate

条件表达式。我们并不会直接去创建该对象,而是通过字段对象中的方法去创建,例如 StringPath 对象"等于"方法:

public BooleanExpression eq(Expression<? super T> right) {  return Expressions.booleanOperation(Ops.EQ, mixin, right);}
复制代码

其创建的对象是一个适配类对象 BooleanOperation,其内部是使用 OperationImpl 来保存相关的数据;

2.2.10 其他

  • GroupExpression 聚合表达式;

  • ArrayExpression 数组表达式

  • ParameterizedExpression 泛型表达式

目前在 SQL 中并没有使用到这个,不介绍

2.3 Visitor

根据观察者模式,在 QueryDSL 提供了这么一个统一接口:

该接口有很多实现类,这里我只列出关键的实现类;

  • SQLSerializer 非常关键的类,基本生成 SQL 都是在这个类来实现的;

  • ValidatingVisitor 用来校验 Expression 中合法性;

我们重点介绍 SQLSerializer 对象的关键代码。

2.3.1 Constant

 @Overridepublic final Void visit(Constant<?> expr, Void context) {  //获取常量值  visitConstant(expr.getConstant());  return null;}@Overridepublic void visitConstant(Object constant) {  if (useLiterals) {//默认该值是为false    //使用字面量去访问,也就是往SQL中追加对应的内容,而不是采用?占位符去设置;    //为了安全着想,基本不会设置为true    if (constant instanceof Collection) {      append("(");      boolean first = true;      for (Object o : ((Collection) constant)) {        if (!first) {          append(COMMA);        }        append(configuration.asLiteral(o));        first = false;      }      append(")");    } else {      append(configuration.asLiteral(constant));    }  } else if (constant instanceof Collection) {    //其追加内容格式:(?,?...?)    append("(");    boolean first = true;    for (Object o : ((Collection) constant)) {      if (!first) {        append(COMMA);      }      //插入?占位符,接着会将参数值保存到常量集合中,      //setParameters中设置该值;      append("?");            constants.add(o);      if (first && (constantPaths.size() < constants.size())) {        constantPaths.add(null);      }      first = false;    }    append(")");        int size = ((Collection) constant).size() - 1;    Path<?> lastPath = constantPaths.peekLast();    for (int i = 0; i < size; i++) {      constantPaths.add(lastPath);    }  } else {    if (stage == Stage.SELECT        && !Null.class.isInstance(constant)        && configuration.getTemplates().isWrapSelectParameters()) {      //在select阶段,且wrapSelectParameters为true时,      //会使用模板cast({0} as {1s})去追加;      //CAST (expression AS data_type) => CAST函数用于将某种数据类型的表达式显式转换为另一种数据类型      String typeName = configuration.getTypeNameForCast(constant.getClass());      Expression type = Expressions.constant(typeName);      super.visitOperation(constant.getClass(), SQLOps.CAST, ImmutableList.<Expression<?>>of(Q, type));    } else {            append("?");    }    //插入?占位符,接着会将参数值保存到常量集合中,    //setParameters中设置该值;    constants.add(constant);    if (constantPaths.size() < constants.size()) {      constantPaths.add(null);    }  }}
复制代码

2.3.2 FactoryExpression

其操作比较简单,就是获取 FactoryExpression 中的参数,也就是 Path 集合(select 后面要查询的字段集合),最终追加的内容:field_name, field_name......。代码如下:

@Overridepublic Void visit(FactoryExpression<?> expr, Void context) {  handle(", ", expr.getArgs());  return null;}public final S handle(final String sep, final List<? extends Expression<?>> expressions) {  for (int i = 0; i < expressions.size(); i++) {        if (i != 0) {      append(sep);    }    //基本会调用Path的访问方法    handle(expressions.get(i));  }  return self;}
复制代码

2.3.3 Path

其追加路径,其有上下级关系,所以基本是要追加:

  • 如果是 dml 操作,则直接追加 path.element

  • 如果是 select 操作,则追加 path.parent.element + "." + path.element;

代码如下:

@Overridepublic Void visit(Path<?> path, Void context) {  if (dml) {    //只要做update、insert、delete操作时,dml才为true.    if (path.equals(entity) && path instanceof RelationalPath<?>) {      SchemaAndTable schemaAndTable = getSchemaAndTable((RelationalPath<?>) path);      boolean precededByDot;      if (dmlWithSchema && templates.isPrintSchema()) {        //追加schema.        appendSchemaName(schemaAndTable.getSchema());        append(".");        precededByDot = true;      } else {        precededByDot = false;      }      //追加表名      appendTableName(schemaAndTable.getTable(), precededByDot);      return null;    } else if (entity.equals(path.getMetadata().getParent()) && skipParent) {      //追加字段名      appendAsColumnName(path, false);      return null;    }  }  //是select语句的访问;  final PathMetadata metadata = path.getMetadata();  boolean precededByDot;  if (metadata.getParent() != null && (!skipParent || dml)) {    //访问上级,也就是父级的path路径,在常规中会加上表名这个字符串。    visit(metadata.getParent(), context);    append(".");    precededByDot = true;  } else {    precededByDot = false;  }  //  appendAsColumnName(path, precededByDot);  return null;}protected void appendAsColumnName(Path<?> path, boolean precededByDot) {  //获取该路径下的element,  String column = ColumnMetadata.getName(path);  if (path.getMetadata().getParent() instanceof RelationalPath) {    RelationalPath<?> parent = (RelationalPath<?>) path.getMetadata().getParent();    column = configuration.getColumnOverride(parent.getSchemaAndTable(), column);  }  append(templates.quoteIdentifier(column, precededByDot));}
复制代码

2.3.4 Operation

其是根据操作符(Operator)从 Template 找到对应的模板,然后采用替换的形式操作,接着追加到 SQL 中;代码如下:

@Overridepublic Void visit(Operation<?> expr, Void context) {  visitOperation(expr.getType(), expr.getOperator(), expr.getArgs());  return null;}protected void visitOperation(Class<?> type, Operator operator, final List<? extends Expression<?>> args) {  //从template模板对象中找到对应的模板  final Template template = templates.getTemplate(operator);  if (template != null) {    final int precedence = templates.getPrecedence(operator);    boolean first = true;    for (final Template.Element element : template.getElements()) {      //从args中获取对应的对象信息;      final Object rv = element.convert(args);      if (rv instanceof Expression) {        //如果是表达式        final Expression<?> expr = (Expression<?>) rv;        if (precedence > -1 && expr instanceof Operation) {          Operator op = ((Operation<?>) expr).getOperator();          int opPrecedence = templates.getPrecedence(op);          if (precedence < opPrecedence) {            append("(").handle(expr).append(")");          } else if (!first && precedence == opPrecedence && !SAME_PRECEDENCE.contains(op)) {            append("(").handle(expr).append(")");          } else {            handle(expr);          }        } else {          handle(expr);        }        first = false;      } else if (element.isString()) {        //如果是字符串类型的,则追加字符串值        append(rv.toString());      } else {        //其他的,则认为是常量        visitConstant(rv);      }    }  } else if (strict) {    throw new IllegalArgumentException("No pattern found for " + operator);  } else {    append(operator.toString());    append("(");    handle(", ", args);    append(")");  }}
复制代码

2.3.5 SubQueryExpression

则重复上面的操作而已,这里不在阐述;

2.3.6 其他

这里不在阐述了,具体可以查看其代码即可;而且不是我们使用该框架会很少被调用,所以不再简述;

2.4 生成 SQL

所有的入口在 SqlSerializer 对象中;针对不同的操作,会有对应的方法提供;

2.4.1 生成 Query

其入口是 serializeForQuery,这里只在代码阐述关键的代码处进行解释,代码如下:

protected void serializeForQuery(QueryMetadata metadata, boolean forCountRow) {  boolean oldInSubquery = inSubquery;  inSubquery = inSubquery || getLength() > 0;  boolean oldSkipParent = skipParent;  skipParent = false;  //从DefaultQueryMetadata对象sql对应的部分值,有关DefaultQueryMetadata的介绍,在数据结构有介绍。  final Expression<?> select = metadata.getProjection();  final List<JoinExpression> joins = metadata.getJoins();  final Predicate where = metadata.getWhere();  final List<? extends Expression<?>> groupBy = metadata.getGroupBy();  final Predicate having = metadata.getHaving();  final List<OrderSpecifier<?>> orderBy = metadata.getOrderBy();  final Set<QueryFlag> flags = metadata.getFlags();  final boolean hasFlags = !flags.isEmpty();  String suffix = null;    List<? extends Expression<?>> sqlSelect;  if (select instanceof FactoryExpression) {    sqlSelect = ((FactoryExpression<?>) select).getArgs();  } else if (select != null) {    sqlSelect = ImmutableList.of(select);  } else {    sqlSelect = ImmutableList.of();  }    //在with阶段,如果有with操作,则追加with 表达式  if (hasFlags) {    List<Expression<?>> withFlags = Lists.newArrayList();    boolean recursive = false;    for (QueryFlag flag : flags) {      if (flag.getPosition() == Position.WITH) {        if (flag.getFlag() == SQLTemplates.RECURSIVE) {          recursive = true;          continue;        }        withFlags.add(flag.getFlag());      }    }    if (!withFlags.isEmpty()) {      if (recursive) {        append(templates.getWithRecursive());      } else {        append(templates.getWith());      }      handle(", ", withFlags);      append("\n");    }  }    // 在start阶段  if (hasFlags) {    serialize(Position.START, flags);  }  // 在select阶段  Stage oldStage = stage;  stage = Stage.SELECT;  if (forCountRow) {    //如果是查询总数的,即count的,select    append(templates.getSelect());    if (hasFlags) {      serialize(Position.AFTER_SELECT, flags);    }        if (!metadata.isDistinct()) {      //追加count(*) from (select 1 as one ..... ) internal      append(templates.getCountStar());      if (!groupBy.isEmpty()) {        append(templates.getFrom());        append("(");        append(templates.getSelect());        append("1 as one ");        suffix = ") internal";      }          } else {      List<? extends Expression<?>> columns;      if (sqlSelect.isEmpty()) {        columns = getIdentifierColumns(joins, !templates.isCountDistinctMultipleColumns());      } else {        columns = sqlSelect;      }      if (!groupBy.isEmpty()) {        // select count(*) from (select distinct ...)        append(templates.getCountStar());        append(templates.getFrom());        append("(");        append(templates.getSelectDistinct());        handleSelect(COMMA, columns);        suffix = ") internal";      } else if (columns.size() == 1) {        append(templates.getDistinctCountStart());        handle(columns.get(0));        append(templates.getDistinctCountEnd());      } else if (templates.isCountDistinctMultipleColumns()) {        append(templates.getDistinctCountStart());        append("(").handleSelect(COMMA, columns).append(")");        append(templates.getDistinctCountEnd());      } else {        // select count(*) from (select distinct ...)        append(templates.getCountStar());        append(templates.getFrom());        append("(");        append(templates.getSelectDistinct());        handleSelect(COMMA, columns);        suffix = ") internal";      }    }  } else if (!sqlSelect.isEmpty()) {    //在select阶段,追加字段名信息    if (!metadata.isDistinct()) {      append(templates.getSelect());    } else {      append(templates.getSelectDistinct());    }    if (hasFlags) {      serialize(Position.AFTER_SELECT, flags);    }        handleSelect(COMMA, sqlSelect);  }  if (hasFlags) {    serialize(Position.AFTER_PROJECTION, flags);  }    // 在from阶段,追加表以及表连接表达式  stage = Stage.FROM;  serializeSources(joins);    // 在where阶段,追加条件表达式  if (hasFlags) {    serialize(Position.BEFORE_FILTERS, flags);  }  if (where != null) {    stage = Stage.WHERE;    append(templates.getWhere()).handle(where);  }  if (hasFlags) {    serialize(Position.AFTER_FILTERS, flags);  }    // 在group by阶段,追加groupby表达式  if (hasFlags) {    serialize(Position.BEFORE_GROUP_BY, flags);  }  if (!groupBy.isEmpty()) {    stage = Stage.GROUP_BY;    append(templates.getGroupBy()).handle(COMMA, groupBy);  }  if (hasFlags) {    serialize(Position.AFTER_GROUP_BY, flags);  }    // 在having阶段,追加having表达式  if (hasFlags) {    serialize(Position.BEFORE_HAVING, flags);  }  if (having != null) {    stage = Stage.HAVING;    append(templates.getHaving()).handle(having);  }  if (hasFlags) {    serialize(Position.AFTER_HAVING, flags);  }    // 在order by阶段,追加orderby表达式  if (hasFlags) {    serialize(Position.BEFORE_ORDER, flags);  }  if (!orderBy.isEmpty() && !forCountRow) {    stage = Stage.ORDER_BY;    append(templates.getOrderBy());    handleOrderBy(orderBy);  }  if (hasFlags) {    serialize(Position.AFTER_ORDER, flags);  }  // 在modifiers阶段,追加分页表达式  if (!forCountRow && metadata.getModifiers().isRestricting() && !joins.isEmpty()) {    stage = Stage.MODIFIERS;    templates.serializeModifiers(metadata, this);  }    if (suffix != null) {    append(suffix);  }    // reset stage  stage = oldStage;  skipParent = oldSkipParent;  inSubquery = oldInSubquery;}
复制代码

2.4.2 生成 Update

其入口是 serializeForUpdate,代码如下:

/** * entity是目标表, update是更新目标表的指定的字段以及对应的值集合 */protected void serializeForUpdate(QueryMetadata metadata, RelationalPath<?> entity,                                  Map<Path<?>, Expression<?>> updates) {  this.entity = entity;  //在start阶段,追加update 表名  serialize(Position.START, metadata.getFlags());    if (!serialize(Position.START_OVERRIDE, metadata.getFlags())) {    append(templates.getUpdate());  }  serialize(Position.AFTER_SELECT, metadata.getFlags());    boolean originalDmlWithSchema = dmlWithSchema;  dmlWithSchema = true;  handle(entity);  dmlWithSchema = originalDmlWithSchema;  append("\n");  append(templates.getSet());  boolean first = true;  skipParent = true;  //追加 set field = field_value  for (final Map.Entry<Path<?>,Expression<?>> update : updates.entrySet()) {    if (!first) {      append(COMMA);    }    handle(update.getKey());    append(" = ");    if (!useLiterals && update.getValue() instanceof Constant<?>) {      constantPaths.add(update.getKey());    }    handle(update.getValue());    first = false;  }  skipParent = false;  //追加where条件  if (metadata.getWhere() != null) {    serializeForWhere(metadata);  }}
复制代码

2.4.3 生成 Insert

其入口是 serializeForInsert,这里有分单个也有批量插入的,这里只介绍单个插入的代码:

/** * columns是要插入指定字段的集合 * values 指定字段对应的值得集合 * subQuery子查询 */protected void serializeForInsert(QueryMetadata metadata, RelationalPath<?> entity, List<Path<?>> columns,                                  List<Expression<?>> values, @Nullable SubQueryExpression<?> subQuery) {  serialize(Position.START, metadata.getFlags());    if (!serialize(Position.START_OVERRIDE, metadata.getFlags())) {    //追加insert into    append(templates.getInsertInto());  }  serialize(Position.AFTER_SELECT, metadata.getFlags());    boolean originalDmlWithSchema = dmlWithSchema;  dmlWithSchema = true;  handle(entity);  dmlWithSchema = originalDmlWithSchema;  // 追加要插入的字段(column1,column2...)  if (!columns.isEmpty()) {    append(" (");    skipParent = true;    handle(COMMA, columns);    skipParent = false;    append(")");  }    if (subQuery != null) {    //追加select column.... from table where ....语句    append("\n");    serialize(subQuery.getMetadata(), false);      } else {        if (!useLiterals) {      //将值保存到constantPaths      for (int i = 0; i < columns.size(); i++) {        if (values.get(i) instanceof Constant<?>) {          constantPaths.add(columns.get(i));        }      }    }        if (!values.isEmpty()) {      // 追加values (?,?....?)      append(templates.getValues());      append("(");      handle(COMMA, values);      append(")");    } else {      append(templates.getDefaultValues());    }  }  }
复制代码

2.4.4 生成 Delete

其入口是 serializeForDelete,代码如下:

protected void serializeForDelete(QueryMetadata metadata, RelationalPath<?> entity) {  serialize(Position.START, metadata.getFlags());  //追加delete  if (!serialize(Position.START_OVERRIDE, metadata.getFlags())) {    append(templates.getDelete());  }  serialize(Position.AFTER_SELECT, metadata.getFlags());  //追加from   append("from ");    boolean originalDmlWithSchema = dmlWithSchema;  dmlWithSchema = true;  //追加表名  handle(entity);  dmlWithSchema = originalDmlWithSchema;  //追加where后的条件  if (metadata.getWhere() != null) {    serializeForWhere(metadata);  }}
复制代码

三. 结果映射

将 sql 执行响应的结果很多情况下,需要将响应报文进行映射,映射有三种。

3.1 基于 FactoryExpression

//AbstractSQLQuery.fetchif (expr instanceof FactoryExpression) {  FactoryExpression<T> fe = (FactoryExpression<T>) expr;  while (rs.next()) {    if (getLastCell) {      //如果getLastCell为true时,同时会添加count(*) over() 这样语句,      //来做满足条件下的有多少条记录,会放在最后的一列。而且只取一次即可。      lastCell = rs.getObject(fe.getArgs().size() + 1);      getLastCell = false;    }    //这个才是真正的结果映射    rv.add(newInstance(fe, rs, 0));  }}private <RT> RT newInstance(FactoryExpression<RT> c, ResultSet rs, int offset)  throws InstantiationException, IllegalAccessException, InvocationTargetException, SQLException {  Object[] args = new Object[c.getArgs().size()];  for (int i = 0; i < args.length; i++) {    //其会结合Configuration去做对应的类型转换。    args[i] = get(rs, c.getArgs().get(i), offset + i + 1, c.getArgs().get(i).getType());  }  //会调用FactoryExpression子类中对应的创建对象;  return c.newInstance(args);}
复制代码

在我们常规下,我们用的 QBean 以及 QTuple 来做映射结果;我们分别来查看代码:

//QBean,通过反射来做属性的注入public T newInstance(Object... a) {  try {    T rv = create(getType());    if (fieldAccess) {      for (int i = 0; i < a.length; i++) {        Object value = a[i];        if (value != null) {          Field field = fields.get(i);          if (field != null) {            field.set(rv, value);          }        }      }    } else {      for (int i = 0; i < a.length; i++) {        Object value = a[i];        if (value != null) {          Method setter = setters.get(i);          if (setter != null) {            setter.invoke(rv, value);          }                            }      }    }    return rv;  } catch (InstantiationException e) {    //....  }}//QTuple@Overridepublic Tuple newInstance(Object... a) {  return new TupleImpl(a);}
复制代码


3.2 基于 All

也就是 select * from 表名;

//AbstractSQLQuery.fetchif (expr.equals(Wildcard.all)) {  while (rs.next()) {    Object[] row = new Object[rs.getMetaData().getColumnCount()];    if (getLastCell) {      lastCell = rs.getObject(row.length);      getLastCell = false;    }    for (int i = 0; i < row.length; i++) {      row[i] = rs.getObject(i + 1);    }    rv.add((T) row);  }}
复制代码

3.3 基于单列

while (rs.next()) {  if (getLastCell) {    lastCell = rs.getObject(2);    getLastCell = false;  }  rv.add(get(rs, expr, 1, expr.getType()));}
复制代码

四. 类型转换

在生产 SQL 中,对"?"占位符替换目标值以及结果映射中的属性值获取,都涉及到类型转换,然而并没有介绍,特此在这里进行完全介绍。

  • 占位符"?"的替换

//Configurationpublic <T> void set(PreparedStatement stmt, Path<?> path, int i, T value) throws SQLException {  if (value == null || value instanceof Null) {    //如果是空对象,从表元数据中找到对应的列对象信息中的jdbcType类型,    //调用setNull来设置    Integer sqlType = null;    if (path != null) {      ColumnMetadata columnMetadata = ColumnMetadata.getColumnMetadata(path);      if (columnMetadata.hasJdbcType()) {        sqlType = columnMetadata.getJdbcType();      }    }    if (sqlType != null) {      stmt.setNull(i, sqlType);    } else {      stmt.setNull(i, Types.NULL);    }  } else {    //如果不是空对象,则从javaType映射集合中找到对应的类型处理器.    //其接口是com.querydsl.sql.types.Type,调用其setValue方法    getType(path, (Class) value.getClass()).setValue(stmt, i, value);  }}//假设找到的类型LocalDate,那么对应的实现类JSR310LocalDateType//JSR310LocalDateType@Overridepublic void setValue(PreparedStatement st, int startIndex, LocalDate value) throws SQLException {  Instant i = value.atStartOfDay(ZoneOffset.UTC).toInstant();  st.setDate(startIndex, new Date(i.toEpochMilli()), utc());}
复制代码
  • 结果映射中的属性值获取

public <T> T get(ResultSet rs, @Nullable Path<?> path, int i, Class<T> clazz) throws SQLException {  //其跟上面的介绍差不多,也是从javaType映射集合中找到对应的类型处理器,然后调用getValue方法	return getType(path, clazz).getValue(rs, i);}//假设找到的类型LocalDate,那么对应的实现类JSR310LocalDateType//JSR310LocalDateTypepublic LocalDate getValue(ResultSet rs, int startIndex) throws SQLException {  Date date = rs.getDate(startIndex, utc());  return date != null ? LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()),                                                ZoneOffset.UTC).toLocalDate() : null;}
复制代码

五. Configuration

上面介绍了类型转换讲到了 Configuration,可以这么理解,这个配置对象影响了 QueryDSL 生成 SQL 以及结果映射的过程。该里面存放了哪些内容,该类有哪些属性对象,如图所示:

  • jdbcTypeMapping&javaTypeMapping 存放了 SQL 数据类型对应的 java 类型的映射以及类型转换器

  • nameMapping& schemaMapping 存放修正的表以及列的映射;

  • typeToName 类型名对应的 class 类型;目前没有找到其用途在哪。先忽略

  • template 目标数据库的模板语法。

  • exceptionTranslator 异常转换;

  • listeners 执行过程的监听器;

  • hasTableColumnTypes 是否有拓展表列对应的类型转换器

  • useLiterals 是否使用字面量去填充参数值;

六. 总结

这里介绍了 QueryDSL 的 sql 生成以及结果映射的关键逻辑,相信对其的 SQL 生成以及结果映射有了更进一步了解;同时也介绍了 Configuration 中的类型转换。

这里没有讲解 QueryFactory 接口以及其实现类,其是创建对应的 SQL 表达式的入口;

另外也没有对模板 Template 的介绍,直接去看代码即可大体看明白了;同时在生成 SQL 过程有关 QueryFlag 以及 JoinFlag 的是如何应该 SQL 生成的;

感兴趣的可以去查阅相关的代码;


发布于: 9 小时前阅读数: 6
用户头像

邱学喆

关注

计算机原理的深度解读,源码分析。 2018.08.26 加入

在IT领域keep Learning。要知其然,也要知其所以然。原理的爱好,源码的阅读。输出我对原理以及源码解读的理解。个人的仓库:https://gitee.com/Michael_Chan

评论

发布
暂无评论
深入了解QueryDSL