View Javadoc
1   /*
2    *    Copyright 2016-2025 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
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(" "))) //$NON-NLS-1$
63                  .withParameters(fragmentCollector.parameters())
64                  .build();
65      }
66  
67      private FragmentAndParameters renderSelect(SelectModel selectModel) {
68          return SubQueryRenderer.withSelectModel(selectModel)
69                  .withRenderingContext(renderingContext)
70                  .withPrefix("(") //$NON-NLS-1$
71                  .withSuffix(")") //$NON-NLS-1$
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() + " (") //$NON-NLS-1$
80                  .withSuffix(")") //$NON-NLS-1$
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 }