1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.dynamic.sql.select;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Objects;
25 import java.util.Optional;
26 import java.util.function.Supplier;
27
28 import org.jspecify.annotations.Nullable;
29 import org.mybatis.dynamic.sql.AndOrCriteriaGroup;
30 import org.mybatis.dynamic.sql.SqlCriterion;
31 import org.mybatis.dynamic.sql.SqlTable;
32 import org.mybatis.dynamic.sql.TableExpression;
33 import org.mybatis.dynamic.sql.exception.DuplicateTableAliasException;
34 import org.mybatis.dynamic.sql.select.join.JoinModel;
35 import org.mybatis.dynamic.sql.select.join.JoinSpecification;
36 import org.mybatis.dynamic.sql.select.join.JoinType;
37 import org.mybatis.dynamic.sql.util.Buildable;
38 import org.mybatis.dynamic.sql.where.AbstractWhereFinisher;
39 import org.mybatis.dynamic.sql.where.AbstractWhereStarter;
40
41 public abstract class AbstractQueryExpressionDSL<W extends AbstractWhereFinisher<?>,
42 T extends AbstractQueryExpressionDSL<W, T>>
43 implements AbstractWhereStarter<W, T> {
44
45 private final List<Supplier<JoinSpecification>> joinSpecificationSuppliers = new ArrayList<>();
46 private final Map<SqlTable, String> tableAliases = new HashMap<>();
47 private final TableExpression table;
48
49 protected AbstractQueryExpressionDSL(TableExpression table) {
50 this.table = Objects.requireNonNull(table);
51 }
52
53 public TableExpression table() {
54 return table;
55 }
56
57 public T join(SqlTable joinTable, SqlCriterion onJoinCriterion,
58 AndOrCriteriaGroup... andJoinCriteria) {
59 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.INNER, Arrays.asList(andJoinCriteria));
60 return getThis();
61 }
62
63 public T join(SqlTable joinTable, String tableAlias, SqlCriterion onJoinCriterion,
64 AndOrCriteriaGroup... andJoinCriteria) {
65 addTableAlias(joinTable, tableAlias);
66 return join(joinTable, onJoinCriterion, andJoinCriteria);
67 }
68
69 public T join(SqlTable joinTable, @Nullable SqlCriterion onJoinCriterion,
70 List<AndOrCriteriaGroup> andJoinCriteria) {
71 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.INNER, andJoinCriteria);
72 return getThis();
73 }
74
75 public T join(SqlTable joinTable, String tableAlias, @Nullable SqlCriterion onJoinCriterion,
76 List<AndOrCriteriaGroup> andJoinCriteria) {
77 addTableAlias(joinTable, tableAlias);
78 return join(joinTable, onJoinCriterion, andJoinCriteria);
79 }
80
81 public T join(Buildable<SelectModel> subQuery, @Nullable String tableAlias, @Nullable SqlCriterion onJoinCriterion,
82 List<AndOrCriteriaGroup> andJoinCriteria) {
83 addJoinSpecificationSupplier(buildSubQuery(subQuery, tableAlias), onJoinCriterion, JoinType.INNER,
84 andJoinCriteria);
85 return getThis();
86 }
87
88 public T leftJoin(SqlTable joinTable, SqlCriterion onJoinCriterion,
89 AndOrCriteriaGroup... andJoinCriteria) {
90 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.LEFT, Arrays.asList(andJoinCriteria));
91 return getThis();
92 }
93
94 public T leftJoin(SqlTable joinTable, String tableAlias, SqlCriterion onJoinCriterion,
95 AndOrCriteriaGroup... andJoinCriteria) {
96 addTableAlias(joinTable, tableAlias);
97 return leftJoin(joinTable, onJoinCriterion, andJoinCriteria);
98 }
99
100 public T leftJoin(SqlTable joinTable, @Nullable SqlCriterion onJoinCriterion,
101 List<AndOrCriteriaGroup> andJoinCriteria) {
102 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.LEFT, andJoinCriteria);
103 return getThis();
104 }
105
106 public T leftJoin(SqlTable joinTable, String tableAlias, @Nullable SqlCriterion onJoinCriterion,
107 List<AndOrCriteriaGroup> andJoinCriteria) {
108 addTableAlias(joinTable, tableAlias);
109 return leftJoin(joinTable, onJoinCriterion, andJoinCriteria);
110 }
111
112 public T leftJoin(Buildable<SelectModel> subQuery, @Nullable String tableAlias,
113 @Nullable SqlCriterion onJoinCriterion, List<AndOrCriteriaGroup> andJoinCriteria) {
114 addJoinSpecificationSupplier(buildSubQuery(subQuery, tableAlias), onJoinCriterion, JoinType.LEFT,
115 andJoinCriteria);
116 return getThis();
117 }
118
119 public T rightJoin(SqlTable joinTable, SqlCriterion onJoinCriterion,
120 AndOrCriteriaGroup... andJoinCriteria) {
121 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.RIGHT, Arrays.asList(andJoinCriteria));
122 return getThis();
123 }
124
125 public T rightJoin(SqlTable joinTable, String tableAlias, SqlCriterion onJoinCriterion,
126 AndOrCriteriaGroup... andJoinCriteria) {
127 addTableAlias(joinTable, tableAlias);
128 return rightJoin(joinTable, onJoinCriterion, andJoinCriteria);
129 }
130
131 public T rightJoin(SqlTable joinTable, @Nullable SqlCriterion onJoinCriterion,
132 List<AndOrCriteriaGroup> andJoinCriteria) {
133 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.RIGHT, andJoinCriteria);
134 return getThis();
135 }
136
137 public T rightJoin(SqlTable joinTable, String tableAlias, @Nullable SqlCriterion onJoinCriterion,
138 List<AndOrCriteriaGroup> andJoinCriteria) {
139 addTableAlias(joinTable, tableAlias);
140 return rightJoin(joinTable, onJoinCriterion, andJoinCriteria);
141 }
142
143 public T rightJoin(Buildable<SelectModel> subQuery, @Nullable String tableAlias,
144 @Nullable SqlCriterion onJoinCriterion, List<AndOrCriteriaGroup> andJoinCriteria) {
145 addJoinSpecificationSupplier(buildSubQuery(subQuery, tableAlias), onJoinCriterion, JoinType.RIGHT,
146 andJoinCriteria);
147 return getThis();
148 }
149
150 public T fullJoin(SqlTable joinTable, SqlCriterion onJoinCriterion,
151 AndOrCriteriaGroup... andJoinCriteria) {
152 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.FULL, Arrays.asList(andJoinCriteria));
153 return getThis();
154 }
155
156 public T fullJoin(SqlTable joinTable, String tableAlias, SqlCriterion onJoinCriterion,
157 AndOrCriteriaGroup... andJoinCriteria) {
158 addTableAlias(joinTable, tableAlias);
159 return fullJoin(joinTable, onJoinCriterion, andJoinCriteria);
160 }
161
162 public T fullJoin(SqlTable joinTable, @Nullable SqlCriterion onJoinCriterion,
163 List<AndOrCriteriaGroup> andJoinCriteria) {
164 addJoinSpecificationSupplier(joinTable, onJoinCriterion, JoinType.FULL, andJoinCriteria);
165 return getThis();
166 }
167
168 public T fullJoin(SqlTable joinTable, String tableAlias, @Nullable SqlCriterion onJoinCriterion,
169 List<AndOrCriteriaGroup> andJoinCriteria) {
170 addTableAlias(joinTable, tableAlias);
171 return fullJoin(joinTable, onJoinCriterion, andJoinCriteria);
172 }
173
174 public T fullJoin(Buildable<SelectModel> subQuery, @Nullable String tableAlias,
175 @Nullable SqlCriterion onJoinCriterion, List<AndOrCriteriaGroup> andJoinCriteria) {
176 addJoinSpecificationSupplier(buildSubQuery(subQuery, tableAlias), onJoinCriterion, JoinType.FULL,
177 andJoinCriteria);
178 return getThis();
179 }
180
181 private void addJoinSpecificationSupplier(TableExpression joinTable, @Nullable SqlCriterion onJoinCriterion,
182 JoinType joinType, List<AndOrCriteriaGroup> andJoinCriteria) {
183 joinSpecificationSuppliers.add(() -> new JoinSpecification.Builder()
184 .withJoinTable(joinTable)
185 .withJoinType(joinType)
186 .withInitialCriterion(onJoinCriterion)
187 .withSubCriteria(andJoinCriteria).build());
188 }
189
190 protected void addJoinSpecificationSupplier(Supplier<JoinSpecification> joinSpecificationSupplier) {
191 joinSpecificationSuppliers.add(joinSpecificationSupplier);
192 }
193
194 protected Optional<JoinModel> buildJoinModel() {
195 if (joinSpecificationSuppliers.isEmpty()) {
196 return Optional.empty();
197 }
198
199 return Optional.of(JoinModel.of(joinSpecificationSuppliers.stream()
200 .map(Supplier::get)
201 .toList()));
202 }
203
204 protected void addTableAlias(SqlTable table, String tableAlias) {
205 if (tableAliases.containsKey(table)) {
206 throw new DuplicateTableAliasException(table, tableAlias, tableAliases.get(table));
207 }
208
209 tableAliases.put(table, tableAlias);
210 }
211
212 protected Map<SqlTable, String> tableAliases() {
213 return Collections.unmodifiableMap(tableAliases);
214 }
215
216 protected static SubQuery buildSubQuery(Buildable<SelectModel> selectModel) {
217 return new SubQuery.Builder()
218 .withSelectModel(selectModel.build())
219 .build();
220 }
221
222 protected static SubQuery buildSubQuery(Buildable<SelectModel> selectModel, @Nullable String alias) {
223 return new SubQuery.Builder()
224 .withSelectModel(selectModel.build())
225 .withAlias(alias)
226 .build();
227 }
228
229 protected abstract T getThis();
230 }