1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.dynamic.sql.select.render;
17
18 import java.util.Objects;
19 import java.util.Optional;
20 import java.util.stream.Collectors;
21
22 import org.jspecify.annotations.Nullable;
23 import org.mybatis.dynamic.sql.BasicColumn;
24 import org.mybatis.dynamic.sql.TableExpression;
25 import org.mybatis.dynamic.sql.render.ExplicitTableAliasCalculator;
26 import org.mybatis.dynamic.sql.render.GuaranteedTableAliasCalculator;
27 import org.mybatis.dynamic.sql.render.RenderingContext;
28 import org.mybatis.dynamic.sql.render.TableAliasCalculator;
29 import org.mybatis.dynamic.sql.select.GroupByModel;
30 import org.mybatis.dynamic.sql.select.HavingModel;
31 import org.mybatis.dynamic.sql.select.QueryExpressionModel;
32 import org.mybatis.dynamic.sql.select.join.JoinModel;
33 import org.mybatis.dynamic.sql.util.FragmentAndParameters;
34 import org.mybatis.dynamic.sql.util.FragmentCollector;
35 import org.mybatis.dynamic.sql.util.StringUtilities;
36 import org.mybatis.dynamic.sql.where.EmbeddedWhereModel;
37
38 public class QueryExpressionRenderer {
39 private final QueryExpressionModel queryExpression;
40 private final TableExpressionRenderer tableExpressionRenderer;
41 private final RenderingContext renderingContext;
42
43 private QueryExpressionRenderer(Builder builder) {
44 queryExpression = Objects.requireNonNull(builder.queryExpression);
45 TableAliasCalculator childTableAliasCalculator = calculateChildTableAliasCalculator(queryExpression);
46
47 renderingContext = Objects.requireNonNull(builder.renderingContext)
48 .withChildTableAliasCalculator(childTableAliasCalculator);
49
50 tableExpressionRenderer = new TableExpressionRenderer.Builder()
51 .withRenderingContext(renderingContext)
52 .build();
53 }
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78 private TableAliasCalculator calculateChildTableAliasCalculator(QueryExpressionModel queryExpression) {
79 return queryExpression.joinModel()
80 .map(JoinModel::containsSubQueries)
81 .map(this::calculateTableAliasCalculatorWithJoins)
82 .orElseGet(this::explicitTableAliasCalculator);
83 }
84
85 private TableAliasCalculator calculateTableAliasCalculatorWithJoins(boolean hasSubQueries) {
86 if (hasSubQueries) {
87
88
89 return explicitTableAliasCalculator();
90 } else {
91
92 return guaranteedTableAliasCalculator();
93 }
94 }
95
96 private TableAliasCalculator explicitTableAliasCalculator() {
97 return ExplicitTableAliasCalculator.of(queryExpression.tableAliases());
98 }
99
100 private TableAliasCalculator guaranteedTableAliasCalculator() {
101 return GuaranteedTableAliasCalculator.of(queryExpression.tableAliases());
102 }
103
104 public FragmentAndParameters render() {
105 FragmentCollector fragmentCollector = new FragmentCollector();
106
107 fragmentCollector.add(calculateQueryExpressionStart());
108 calculateJoinClause().ifPresent(fragmentCollector::add);
109 calculateWhereClause().ifPresent(fragmentCollector::add);
110 calculateGroupByClause().ifPresent(fragmentCollector::add);
111 calculateHavingClause().ifPresent(fragmentCollector::add);
112
113 return fragmentCollector.toFragmentAndParameters(Collectors.joining(" "));
114 }
115
116 private FragmentAndParameters calculateQueryExpressionStart() {
117 FragmentAndParameters columnList = calculateColumnList();
118
119 String start = queryExpression.connector().map(StringUtilities::spaceAfter).orElse("")
120 + "select "
121 + (queryExpression.isDistinct() ? "distinct " : "")
122 + columnList.fragment()
123 + " from ";
124
125 FragmentAndParameters renderedTable = renderTableExpression(queryExpression.table());
126 start += renderedTable.fragment();
127
128 return FragmentAndParameters.withFragment(start)
129 .withParameters(renderedTable.parameters())
130 .withParameters(columnList.parameters())
131 .build();
132 }
133
134 private FragmentAndParameters calculateColumnList() {
135 return queryExpression.columns()
136 .map(this::renderColumnAndAlias)
137 .collect(FragmentCollector.collect())
138 .toFragmentAndParameters(Collectors.joining(", "));
139 }
140
141 private FragmentAndParameters renderColumnAndAlias(BasicColumn selectListItem) {
142 FragmentAndParameters renderedColumn = selectListItem.render(renderingContext);
143
144 return selectListItem.alias().map(a -> renderedColumn.mapFragment(f -> f + " as " + a))
145 .orElse(renderedColumn);
146 }
147
148 private FragmentAndParameters renderTableExpression(TableExpression table) {
149 return table.accept(tableExpressionRenderer);
150 }
151
152 private Optional<FragmentAndParameters> calculateJoinClause() {
153 return queryExpression.joinModel().map(this::renderJoin);
154 }
155
156 private FragmentAndParameters renderJoin(JoinModel joinModel) {
157 return JoinRenderer.withJoinModel(joinModel)
158 .withTableExpressionRenderer(tableExpressionRenderer)
159 .withRenderingContext(renderingContext)
160 .build()
161 .render();
162 }
163
164 private Optional<FragmentAndParameters> calculateWhereClause() {
165 return queryExpression.whereModel().flatMap(this::renderWhereClause);
166 }
167
168 private Optional<FragmentAndParameters> renderWhereClause(EmbeddedWhereModel whereModel) {
169 return whereModel.render(renderingContext);
170 }
171
172 private Optional<FragmentAndParameters> calculateGroupByClause() {
173 return queryExpression.groupByModel().map(this::renderGroupBy);
174 }
175
176 private FragmentAndParameters renderGroupBy(GroupByModel groupByModel) {
177 return groupByModel.columns()
178 .map(this::renderColumn)
179 .collect(FragmentCollector.collect())
180 .toFragmentAndParameters(
181 Collectors.joining(", ", "group by ", ""));
182 }
183
184 private FragmentAndParameters renderColumn(BasicColumn column) {
185 return column.render(renderingContext);
186 }
187
188 private Optional<FragmentAndParameters> calculateHavingClause() {
189 return queryExpression.havingModel().flatMap(this::renderHavingClause);
190 }
191
192 private Optional<FragmentAndParameters> renderHavingClause(HavingModel havingModel) {
193 return HavingRenderer.withHavingModel(havingModel)
194 .withRenderingContext(renderingContext)
195 .build()
196 .render();
197 }
198
199 public static Builder withQueryExpression(QueryExpressionModel model) {
200 return new Builder().withQueryExpression(model);
201 }
202
203 public static class Builder {
204 private @Nullable QueryExpressionModel queryExpression;
205 private @Nullable RenderingContext renderingContext;
206
207 public Builder withRenderingContext(RenderingContext renderingContext) {
208 this.renderingContext = renderingContext;
209 return this;
210 }
211
212 public Builder withQueryExpression(QueryExpressionModel queryExpression) {
213 this.queryExpression = queryExpression;
214 return this;
215 }
216
217 public QueryExpressionRenderer build() {
218 return new QueryExpressionRenderer(this);
219 }
220 }
221 }