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.common.OrderByModel;
24 import org.mybatis.dynamic.sql.common.OrderByRenderer;
25 import org.mybatis.dynamic.sql.render.RenderingContext;
26 import org.mybatis.dynamic.sql.render.RenderingStrategy;
27 import org.mybatis.dynamic.sql.select.MultiSelectModel;
28 import org.mybatis.dynamic.sql.select.PagingModel;
29 import org.mybatis.dynamic.sql.select.SelectModel;
30 import org.mybatis.dynamic.sql.select.UnionQuery;
31 import org.mybatis.dynamic.sql.util.FragmentAndParameters;
32 import org.mybatis.dynamic.sql.util.FragmentCollector;
33
34 public class MultiSelectRenderer {
35 private final MultiSelectModel multiSelectModel;
36 private final RenderingContext renderingContext;
37
38 private MultiSelectRenderer(Builder builder) {
39 multiSelectModel = Objects.requireNonNull(builder.multiSelectModel);
40 renderingContext = RenderingContext
41 .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy))
42 .withStatementConfiguration(multiSelectModel.statementConfiguration())
43 .build();
44 }
45
46 public SelectStatementProvider render() {
47 FragmentAndParameters initialSelect = renderSelect(multiSelectModel.initialSelect());
48
49 FragmentCollector fragmentCollector = multiSelectModel
50 .unionQueries()
51 .map(this::renderSelect)
52 .collect(FragmentCollector.collect(initialSelect));
53
54 renderOrderBy().ifPresent(fragmentCollector::add);
55 renderPagingModel().ifPresent(fragmentCollector::add);
56
57 return toSelectStatementProvider(fragmentCollector);
58 }
59
60 private SelectStatementProvider toSelectStatementProvider(FragmentCollector fragmentCollector) {
61 return DefaultSelectStatementProvider
62 .withSelectStatement(fragmentCollector.collectFragments(Collectors.joining(" ")))
63 .withParameters(fragmentCollector.parameters())
64 .build();
65 }
66
67 private FragmentAndParameters renderSelect(SelectModel selectModel) {
68 return SubQueryRenderer.withSelectModel(selectModel)
69 .withRenderingContext(renderingContext)
70 .withPrefix("(")
71 .withSuffix(")")
72 .build()
73 .render();
74 }
75
76 private FragmentAndParameters renderSelect(UnionQuery unionQuery) {
77 return SubQueryRenderer.withSelectModel(unionQuery.selectModel())
78 .withRenderingContext(renderingContext)
79 .withPrefix(unionQuery.connector() + " (")
80 .withSuffix(")")
81 .build()
82 .render();
83 }
84
85 private Optional<FragmentAndParameters> renderOrderBy() {
86 return multiSelectModel.orderByModel().map(this::renderOrderBy);
87 }
88
89 private FragmentAndParameters renderOrderBy(OrderByModel orderByModel) {
90 return new OrderByRenderer(renderingContext).render(orderByModel);
91 }
92
93 private Optional<FragmentAndParameters> renderPagingModel() {
94 return multiSelectModel.pagingModel().map(this::renderPagingModel);
95 }
96
97 private FragmentAndParameters renderPagingModel(PagingModel pagingModel) {
98 return new PagingModelRenderer.Builder()
99 .withPagingModel(pagingModel)
100 .withRenderingContext(renderingContext)
101 .build()
102 .render();
103 }
104
105 public static Builder withMultiSelectModel(MultiSelectModel multiSelectModel) {
106 return new Builder().withMultiSelectModel(multiSelectModel);
107 }
108
109 public static class Builder {
110 private @Nullable RenderingStrategy renderingStrategy;
111 private @Nullable MultiSelectModel multiSelectModel;
112
113 public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) {
114 this.renderingStrategy = renderingStrategy;
115 return this;
116 }
117
118 public Builder withMultiSelectModel(MultiSelectModel multiSelectModel) {
119 this.multiSelectModel = multiSelectModel;
120 return this;
121 }
122
123 public MultiSelectRenderer build() {
124 return new MultiSelectRenderer(this);
125 }
126 }
127 }