一. 概述
之前一篇了解 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 这两个对象;
我们可以通过 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;该对象就存放了两个属性值:
2.2.4 Constant
常量表达式,比较好理解,可以这么理解:SQL 中“?”占位符要填充的值是我们要注入的值,该值一般用这个对象来包装;
2.2.5 SubQueryExpression
子查询表达式,其实现类是 SubQueryExpressionImpl,其内部的属性是 QueryMetadata 接口(其实现类 DefaultQueryMetadata).
2.2.6 TemplateExpression
模板表达式,这里目前没有发现在 query-sql 工程中没有使用到这个,姑且不再介绍;我们主要看其主要的实现类 TemplateExpressionImpl 中包含的属性:
2.2.7 ParamExpression
参数表达式,目前在 query-sql 工程中,并没有使用到这个。这个不再介绍。只介绍其主要的实现类 ParamExpressionImpl 中包含的属性:
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 其他
目前在 SQL 中并没有使用到这个,不介绍
2.3 Visitor
根据观察者模式,在 QueryDSL 提供了这么一个统一接口:
该接口有很多实现类,这里我只列出关键的实现类;
我们重点介绍 SQLSerializer 对象的关键代码。
2.3.1 Constant
@Override
public final Void visit(Constant<?> expr, Void context) {
//获取常量值
visitConstant(expr.getConstant());
return null;
}
@Override
public 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......。代码如下:
@Override
public 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
其追加路径,其有上下级关系,所以基本是要追加:
代码如下:
@Override
public 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 中;代码如下:
@Override
public 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.fetch
if (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
@Override
public Tuple newInstance(Object... a) {
return new TupleImpl(a);
}
复制代码
3.2 基于 All
也就是 select * from 表名;
//AbstractSQLQuery.fetch
if (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 中,对"?"占位符替换目标值以及结果映射中的属性值获取,都涉及到类型转换,然而并没有介绍,特此在这里进行完全介绍。
//Configuration
public <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
@Override
public 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
//JSR310LocalDateType
public 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 生成的;
感兴趣的可以去查阅相关的代码;
评论