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.render;
17
18 import java.util.concurrent.atomic.AtomicInteger;
19
20 import org.mybatis.dynamic.sql.BindableColumn;
21
22 /**
23 * A rendering strategy is used to generate a platform specific binding.
24 *
25 * <p>Rendering strategies are used during the rendering phase of statement generation.
26 * All generated SQL statements include the generated statement itself, and a map of parameters that
27 * should be bound to the statement at execution time. For example, a generated select statement may
28 * look like this when rendered for MyBatis:
29 *
30 * <p><code>select foo from bar where id = #{parameters.p1,jdbcType=INTEGER}</code>
31 *
32 * <p>In this case, the binding is <code>#{parameters.p1,jdbcType=INTEGER}</code>. MyBatis knows how to interpret this
33 * binding - it will look for a value in the <code>parameters.p1</code> property of the parameter object
34 * passed to the statement and bind it as a prepared statement parameter when executing the statement.
35 */
36 public abstract class RenderingStrategy {
37 public static final String DEFAULT_PARAMETER_PREFIX = "parameters"; //$NON-NLS-1$
38
39 /**
40 * Generate a unique key that can be used to place a parameter value in the parameter map.
41 *
42 * @param sequence a sequence for calculating a unique value
43 * @return a key used to place the parameter value in the parameter map
44 */
45 public String formatParameterMapKey(AtomicInteger sequence) {
46 return "p" + sequence.getAndIncrement(); //$NON-NLS-1$
47 }
48
49 /**
50 * Return a parameter map key intended as a parameter for a fetch first query.
51 *
52 * <p>By default, this parameter is treated the same as any other. This method is a hook to support
53 * MyBatis Spring Batch.
54 *
55 * @param sequence a sequence for calculating a unique value
56 * @return a key used to place the parameter value in the parameter map
57 */
58 public String formatParameterMapKeyForFetchFirstRows(AtomicInteger sequence) {
59 return formatParameterMapKey(sequence);
60 }
61
62 /**
63 * Return a parameter map key intended as a parameter for a limit query.
64 *
65 * <p>By default, this parameter is treated the same as any other. This method is a hook to support
66 * MyBatis Spring Batch.
67 *
68 * @param sequence a sequence for calculating a unique value
69 * @return a key used to place the parameter value in the parameter map
70 */
71 public String formatParameterMapKeyForLimit(AtomicInteger sequence) {
72 return formatParameterMapKey(sequence);
73 }
74
75 /**
76 * Return a parameter map key intended as a parameter for a query offset.
77 *
78 * <p>By default, this parameter is treated the same as any other. This method is a hook to support
79 * MyBatis Spring Batch.
80 *
81 * @param sequence a sequence for calculating a unique value
82 * @return a key used to place the parameter value in the parameter map
83 */
84 public String formatParameterMapKeyForOffset(AtomicInteger sequence) {
85 return formatParameterMapKey(sequence);
86 }
87
88 /**
89 * This method generates a binding for a parameter to a placeholder in a generated SQL statement.
90 *
91 * <p>This binding is appropriate when there can be a mapping between a parameter and a known target column,
92 * In MyBatis, the binding can specify type information based on the column. The bindings are specific
93 * to the target framework.
94 *
95 * <p>For MyBatis, a binding looks like this: "#{prefix.parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
96 *
97 * <p>For Spring, a binding looks like this: ":parameterName"
98 *
99 * @param column column definition used for generating type details in a MyBatis binding. Ignored for Spring.
100 * @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
101 * {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
102 * @param parameterName name of the parameter. Typically generated by calling
103 * {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
104 * @return the generated binding
105 */
106 public abstract String getFormattedJdbcPlaceholder(BindableColumn<?> column, String prefix, String parameterName);
107
108 /**
109 * This method generates a binding for a parameter to a placeholder in a generated SQL statement.
110 *
111 * <p>This binding is appropriate when the parameter is bound to placeholder that is not a known column (such as
112 * a limit or offset parameter). The bindings are specific to the target framework.
113 *
114 * <p>For MyBatis, a binding looks like this: "#{prefix.parameterName}"
115 *
116 * <p>For Spring, a binding looks like this: ":parameterName"
117 *
118 * @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
119 * {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
120 * @param parameterName name of the parameter. Typically generated by calling
121 * {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
122 * @return the generated binding
123 */
124 public abstract String getFormattedJdbcPlaceholder(String prefix, String parameterName);
125
126 /**
127 * This method generates a binding for a parameter to a placeholder in a generated SQL statement.
128 *
129 * <p>This method is used to generate bindings for limit, offset, and fetch first parameters. By default, these
130 * parameters are treated the same as any other. This method supports MyBatis Spring Batch integration where the
131 * parameter keys have predefined values and need special handling.
132 *
133 * @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
134 * {@link RenderingStrategy#DEFAULT_PARAMETER_PREFIX}. This is ignored for Spring.
135 * @param parameterName name of the parameter. Typically generated by calling
136 * {@link RenderingStrategy#formatParameterMapKey(AtomicInteger)}
137 * @return the generated binding
138 */
139 public String getFormattedJdbcPlaceholderForPagingParameters(String prefix, String parameterName) {
140 return getFormattedJdbcPlaceholder(prefix, parameterName);
141 }
142
143 /**
144 * This method generates a binding for a parameter to a placeholder in a row based insert statement.
145 *
146 * <p>This binding is specifically for use with insert, batch insert, and multirow insert statements.
147 * These statements bind parameters to properties of a row class. The Spring implementation changes the binding
148 * to match values expected for a these insert statements. For MyBatis, the binding is the same
149 * as {@link RenderingStrategy#getFormattedJdbcPlaceholder(BindableColumn, String, String)}.
150 *
151 * <p>For MyBatis, a binding looks like this: "#{prefix.parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
152 *
153 * <p>For Spring, a binding looks like this: ":prefix.parameterName"
154 *
155 * @param column column definition used for generating type details in a MyBatis binding. Ignored for Spring.
156 * @param prefix parameter prefix used for locating the parameters in a SQL provider object. Typically, will be
157 * either "row" or "records[x]" to match the properties of the generated statement object class.
158 * @param parameterName name of the parameter. Typically, this is a property in the row class associated with the
159 * insert statement.
160 * @return the generated binding
161 */
162 public String getRecordBasedInsertBinding(BindableColumn<?> column, String prefix, String parameterName) {
163 return getFormattedJdbcPlaceholder(column, prefix, parameterName);
164 }
165
166 /**
167 * This method generates a binding for a parameter to a placeholder in a row based insert statement.
168 *
169 * <p>This binding is specifically for use with insert, batch insert, and multirow insert statements and the
170 * MapToRow mapping. These statements bind parameters to the row class directly.
171 *
172 * <p>For MyBatis, a binding looks like this: "#{parameterName,jdbcType=xxx,typeHandler=xxx,javaType=xxx}"
173 *
174 * <p>For Spring, a binding looks like this: ":parameterName"
175 *
176 * @param column column definition used for generating type details in a MyBatis binding. Ignored for Spring.
177 * @param parameterName name of the parameter. Typically, will be
178 * either "row" or "records[x]" to match the properties of the generated statement object class.
179 * @return the generated binding
180 */
181 public abstract String getRecordBasedInsertBinding(BindableColumn<?> column, String parameterName);
182 }