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.update.render;
17  
18  import java.util.Objects;
19  import java.util.Optional;
20  
21  import org.jspecify.annotations.Nullable;
22  import org.mybatis.dynamic.sql.render.RenderedParameterInfo;
23  import org.mybatis.dynamic.sql.render.RenderingContext;
24  import org.mybatis.dynamic.sql.select.render.SubQueryRenderer;
25  import org.mybatis.dynamic.sql.util.AbstractColumnMapping;
26  import org.mybatis.dynamic.sql.util.ColumnToColumnMapping;
27  import org.mybatis.dynamic.sql.util.ConstantMapping;
28  import org.mybatis.dynamic.sql.util.FragmentAndParameters;
29  import org.mybatis.dynamic.sql.util.NullMapping;
30  import org.mybatis.dynamic.sql.util.SelectMapping;
31  import org.mybatis.dynamic.sql.util.StringConstantMapping;
32  import org.mybatis.dynamic.sql.util.StringUtilities;
33  import org.mybatis.dynamic.sql.util.UpdateMappingVisitor;
34  import org.mybatis.dynamic.sql.util.ValueMapping;
35  import org.mybatis.dynamic.sql.util.ValueOrNullMapping;
36  import org.mybatis.dynamic.sql.util.ValueWhenPresentMapping;
37  
38  public class SetPhraseVisitor extends UpdateMappingVisitor<Optional<FragmentAndParameters>> {
39  
40      private final RenderingContext renderingContext;
41  
42      public SetPhraseVisitor(RenderingContext renderingContext) {
43          this.renderingContext = Objects.requireNonNull(renderingContext);
44      }
45  
46      @Override
47      public Optional<FragmentAndParameters> visit(NullMapping mapping) {
48          return buildNullFragment(mapping);
49      }
50  
51      @Override
52      public Optional<FragmentAndParameters> visit(ConstantMapping mapping) {
53          String fragment = renderingContext.aliasedColumnName(mapping.column())
54                  + " = " + mapping.constant(); //$NON-NLS-1$
55          return FragmentAndParameters.withFragment(fragment)
56                  .buildOptional();
57      }
58  
59      @Override
60      public Optional<FragmentAndParameters> visit(StringConstantMapping mapping) {
61          String fragment = renderingContext.aliasedColumnName(mapping.column())
62                  + " = " //$NON-NLS-1$
63                  + StringUtilities.formatConstantForSQL(mapping.constant());
64  
65          return FragmentAndParameters.withFragment(fragment)
66                  .buildOptional();
67      }
68  
69      @Override
70      public <T> Optional<FragmentAndParameters> visit(ValueMapping<T> mapping) {
71          return buildValueFragment(mapping, mapping.value());
72      }
73  
74      @Override
75      public <T> Optional<FragmentAndParameters> visit(ValueOrNullMapping<T> mapping) {
76          return mapping.value()
77                  .map(v -> buildValueFragment(mapping, v))
78                  .orElseGet(() -> buildNullFragment(mapping));
79      }
80  
81      @Override
82      public <T> Optional<FragmentAndParameters> visit(ValueWhenPresentMapping<T> mapping) {
83          return mapping.value().flatMap(v -> buildValueFragment(mapping, v));
84      }
85  
86      @Override
87      public Optional<FragmentAndParameters> visit(SelectMapping mapping) {
88          String prefix = renderingContext.aliasedColumnName(mapping.column()) + " = ("; //$NON-NLS-1$
89  
90          FragmentAndParameters fragmentAndParameters = SubQueryRenderer.withSelectModel(mapping.selectModel())
91                  .withRenderingContext(renderingContext)
92                  .withPrefix(prefix)
93                  .withSuffix(")") //$NON-NLS-1$
94                  .build()
95                  .render();
96  
97          return Optional.of(fragmentAndParameters);
98      }
99  
100     @Override
101     public Optional<FragmentAndParameters> visit(ColumnToColumnMapping mapping) {
102         FragmentAndParameters fragmentAndParameters = mapping.rightColumn().render(renderingContext)
103                 .mapFragment(f -> renderingContext.aliasedColumnName(mapping.column()) + " = " + f); //$NON-NLS-1$
104         return Optional.of(fragmentAndParameters);
105     }
106 
107     private <T> Optional<FragmentAndParameters> buildValueFragment(AbstractColumnMapping mapping, @Nullable T value) {
108         RenderedParameterInfo parameterInfo = renderingContext.calculateParameterInfo(mapping.column());
109         String setPhrase = renderingContext.aliasedColumnName(mapping.column())
110                 + " = "  //$NON-NLS-1$
111                 + parameterInfo.renderedPlaceHolder();
112 
113         return FragmentAndParameters.withFragment(setPhrase)
114                 .withParameter(parameterInfo.parameterMapKey(), value)
115                 .buildOptional();
116     }
117 
118     private Optional<FragmentAndParameters> buildNullFragment(AbstractColumnMapping mapping) {
119         return FragmentAndParameters
120                 .withFragment(renderingContext.aliasedColumnName(mapping.column()) + " = null") //$NON-NLS-1$
121                 .buildOptional();
122     }
123 }