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.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 }