1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.dynamic.sql.update.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.ExplicitTableAliasCalculator;
26 import org.mybatis.dynamic.sql.render.RenderedParameterInfo;
27 import org.mybatis.dynamic.sql.render.RenderingContext;
28 import org.mybatis.dynamic.sql.render.RenderingStrategy;
29 import org.mybatis.dynamic.sql.render.TableAliasCalculator;
30 import org.mybatis.dynamic.sql.update.UpdateModel;
31 import org.mybatis.dynamic.sql.util.FragmentAndParameters;
32 import org.mybatis.dynamic.sql.util.FragmentCollector;
33 import org.mybatis.dynamic.sql.util.Validator;
34 import org.mybatis.dynamic.sql.where.EmbeddedWhereModel;
35
36 public class UpdateRenderer {
37 private final UpdateModel updateModel;
38 private final RenderingContext renderingContext;
39 private final SetPhraseVisitor visitor;
40
41 private UpdateRenderer(Builder builder) {
42 updateModel = Objects.requireNonNull(builder.updateModel);
43 TableAliasCalculator tableAliasCalculator = builder.updateModel.tableAlias()
44 .map(a -> ExplicitTableAliasCalculator.of(updateModel.table(), a))
45 .orElseGet(TableAliasCalculator::empty);
46 renderingContext = RenderingContext
47 .withRenderingStrategy(Objects.requireNonNull(builder.renderingStrategy))
48 .withTableAliasCalculator(tableAliasCalculator)
49 .withStatementConfiguration(updateModel.statementConfiguration())
50 .build();
51 visitor = new SetPhraseVisitor(renderingContext);
52 }
53
54 public UpdateStatementProvider render() {
55 FragmentCollector fragmentCollector = new FragmentCollector();
56
57 fragmentCollector.add(calculateUpdateStatementStart());
58 fragmentCollector.add(calculateSetPhrase());
59 calculateWhereClause().ifPresent(fragmentCollector::add);
60 calculateOrderByClause().ifPresent(fragmentCollector::add);
61 calculateLimitClause().ifPresent(fragmentCollector::add);
62
63 return toUpdateStatementProvider(fragmentCollector);
64 }
65
66 private UpdateStatementProvider toUpdateStatementProvider(FragmentCollector fragmentCollector) {
67 return DefaultUpdateStatementProvider
68 .withUpdateStatement(fragmentCollector.collectFragments(Collectors.joining(" ")))
69 .withParameters(fragmentCollector.parameters())
70 .build();
71 }
72
73 private FragmentAndParameters calculateUpdateStatementStart() {
74 String aliasedTableName = renderingContext.aliasedTableName(updateModel.table());
75 return FragmentAndParameters.fromFragment("update " + aliasedTableName);
76 }
77
78 private FragmentAndParameters calculateSetPhrase() {
79 FragmentCollector fragmentCollector = updateModel.columnMappings()
80 .map(m -> m.accept(visitor))
81 .flatMap(Optional::stream)
82 .collect(FragmentCollector.collect());
83
84 Validator.assertFalse(fragmentCollector.isEmpty(), "ERROR.18");
85
86 return fragmentCollector.toFragmentAndParameters(
87 Collectors.joining(", ", "set ", ""));
88 }
89
90 private Optional<FragmentAndParameters> calculateWhereClause() {
91 return updateModel.whereModel().flatMap(this::renderWhereClause);
92 }
93
94 private Optional<FragmentAndParameters> renderWhereClause(EmbeddedWhereModel whereModel) {
95 return whereModel.render(renderingContext);
96 }
97
98 private Optional<FragmentAndParameters> calculateLimitClause() {
99 return updateModel.limit().map(this::renderLimitClause);
100 }
101
102 private FragmentAndParameters renderLimitClause(Long limit) {
103 RenderedParameterInfo parameterInfo = renderingContext.calculateLimitParameterInfo();
104
105 return FragmentAndParameters.withFragment("limit " + parameterInfo.renderedPlaceHolder())
106 .withParameter(parameterInfo.parameterMapKey(), limit)
107 .build();
108 }
109
110 private Optional<FragmentAndParameters> calculateOrderByClause() {
111 return updateModel.orderByModel().map(this::renderOrderByClause);
112 }
113
114 private FragmentAndParameters renderOrderByClause(OrderByModel orderByModel) {
115 return new OrderByRenderer(renderingContext).render(orderByModel);
116 }
117
118 public static Builder withUpdateModel(UpdateModel updateModel) {
119 return new Builder().withUpdateModel(updateModel);
120 }
121
122 public static class Builder {
123 private @Nullable UpdateModel updateModel;
124 private @Nullable RenderingStrategy renderingStrategy;
125
126 public Builder withUpdateModel(UpdateModel updateModel) {
127 this.updateModel = updateModel;
128 return this;
129 }
130
131 public Builder withRenderingStrategy(RenderingStrategy renderingStrategy) {
132 this.renderingStrategy = renderingStrategy;
133 return this;
134 }
135
136 public UpdateRenderer build() {
137 return new UpdateRenderer(this);
138 }
139 }
140 }