001/*
002 *  Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.provider;
017
018import com.mybatisflex.core.FlexConsts;
019import com.mybatisflex.core.dialect.DialectFactory;
020import com.mybatisflex.core.exception.FlexAssert;
021import com.mybatisflex.core.query.CPI;
022import com.mybatisflex.core.query.QueryWrapper;
023import com.mybatisflex.core.row.Row;
024import com.mybatisflex.core.row.RowCPI;
025import com.mybatisflex.core.row.RowMapper;
026import com.mybatisflex.core.table.TableInfo;
027import com.mybatisflex.core.table.TableInfoFactory;
028import com.mybatisflex.core.util.ArrayUtil;
029import com.mybatisflex.core.util.ClassUtil;
030
031import java.util.Collection;
032import java.util.LinkedHashSet;
033import java.util.List;
034import java.util.Map;
035import java.util.Set;
036
037@SuppressWarnings({"rawtypes", "DuplicatedCode"})
038public class RowSqlProvider {
039
040    public static final String METHOD_RAW_SQL = "providerRawSql";
041
042    /**
043     * 不让实例化,使用静态方法的模式,效率更高,非静态方法每次都会实例化当前类
044     * 参考源码: {{@link org.apache.ibatis.builder.annotation.ProviderSqlSource#getBoundSql(Object)}
045     */
046    private RowSqlProvider() {
047    }
048
049    /**
050     * 执行原生 sql 的方法
051     *
052     * @param params 方法参数
053     * @return SQL 语句
054     * @see RowMapper#insertBySql(String, Object...)
055     * @see RowMapper#deleteBySql(String, Object...)
056     * @see RowMapper#updateBySql(String, Object...)
057     */
058    public static String providerRawSql(Map params) {
059        ProviderUtil.flatten(params);
060        return ProviderUtil.getSqlString(params);
061    }
062
063    /**
064     * insert 的 SQL 构建。
065     *
066     * @param params 方法参数
067     * @return SQL 语句
068     * @see RowMapper#insert(String, String, Row)
069     */
070    public static String insert(Map params) {
071        String tableName = ProviderUtil.getTableName(params);
072        String schema = ProviderUtil.getSchemaName(params);
073        Row row = ProviderUtil.getRow(params);
074
075        // 先生成 SQL,再设置参数
076        String sql = DialectFactory.getDialect().forInsertRow(schema, tableName, row);
077        ProviderUtil.setSqlArgs(params, row.obtainInsertValues());
078        return sql;
079    }
080
081    /**
082     * insertBatch 的 SQL 构建。
083     *
084     * @param params 方法参数
085     * @return SQL 语句
086     * @see RowMapper#insertBatchWithFirstRowColumns(String, String, List)
087     */
088    public static String insertBatchWithFirstRowColumns(Map params) {
089        List<Row> rows = ProviderUtil.getRows(params);
090
091        FlexAssert.notEmpty(rows, "rows");
092
093        String tableName = ProviderUtil.getTableName(params);
094        String schema = ProviderUtil.getSchemaName(params);
095
096        // 让所有 row 的列顺序和值的数量与第条数据保持一致
097        // 这个必须 new 一个 LinkedHashSet,因为 keepModifyAttrs 会清除 row 所有的 modifyAttrs
098        Set<String> modifyAttrs = new LinkedHashSet<>(RowCPI.getInsertAttrs(rows.get(0)));
099
100        // sql: INSERT INTO `tb_table`(`name`, `sex`) VALUES (?, ?),(?, ?),(?, ?)
101        String sql = DialectFactory.getDialect().forInsertBatchWithFirstRowColumns(schema, tableName, rows);
102
103        Object[] values = new Object[]{};
104        for (Row row : rows) {
105            values = ArrayUtil.concat(values, row.obtainInsertValues(modifyAttrs));
106        }
107        ProviderUtil.setSqlArgs(params, values);
108
109        return sql;
110    }
111
112    /**
113     * deleteById 的 SQL 构建。
114     *
115     * @param params 方法参数
116     * @return SQL 语句
117     * @see RowMapper#deleteById(String, String, String, Object)
118     */
119    public static String deleteById(Map params) {
120        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
121
122        FlexAssert.notEmpty(primaryValues, "primaryValues");
123
124        String schema = ProviderUtil.getSchemaName(params);
125        String tableName = ProviderUtil.getTableName(params);
126        String[] primaryKeys = ProviderUtil.getPrimaryKeys(params);
127
128        String sql = DialectFactory.getDialect().forDeleteById(schema, tableName, primaryKeys);
129        ProviderUtil.setSqlArgs(params, primaryValues);
130        return sql;
131    }
132
133    /**
134     * deleteBatchByIds 的 SQL 构建。
135     *
136     * @param params 方法参数
137     * @return SQL 语句
138     * @see RowMapper#deleteBatchByIds(String, String, String, Collection)
139     */
140    public static String deleteBatchByIds(Map params) {
141        String schema = ProviderUtil.getSchemaName(params);
142        String tableName = ProviderUtil.getTableName(params);
143        String[] primaryKeys = ProviderUtil.getPrimaryKeys(params);
144        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
145
146        String sql = DialectFactory.getDialect().forDeleteBatchByIds(schema, tableName, primaryKeys, primaryValues);
147        ProviderUtil.setSqlArgs(params, primaryValues);
148        return sql;
149    }
150
151    /**
152     * deleteByQuery 的 SQL 构建。
153     *
154     * @param params 方法参数
155     * @return SQL 语句
156     * @see RowMapper#deleteByQuery(String, String, QueryWrapper)
157     */
158    public static String deleteByQuery(Map params) {
159        String schema = ProviderUtil.getSchemaName(params);
160        String tableName = ProviderUtil.getTableName(params);
161        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
162        CPI.setFromIfNecessary(queryWrapper, schema, tableName);
163
164        // 优先构建 sql,再构建参数
165        String sql = DialectFactory.getDialect().forDeleteByQuery(queryWrapper);
166        Object[] valueArray = CPI.getValueArray(queryWrapper);
167        ProviderUtil.setSqlArgs(params, valueArray);
168
169        return sql;
170    }
171
172    /**
173     * updateById 的 SQL 构建。
174     *
175     * @param params 方法参数
176     * @return SQL 语句
177     * @see RowMapper#updateById(String, String, Row)
178     */
179    public static String updateById(Map params) {
180        String schema = ProviderUtil.getSchemaName(params);
181        String tableName = ProviderUtil.getTableName(params);
182        Row row = ProviderUtil.getRow(params);
183        String sql = DialectFactory.getDialect().forUpdateById(schema, tableName, row);
184        ProviderUtil.setSqlArgs(params, RowCPI.obtainUpdateValues(row));
185        return sql;
186    }
187
188    /**
189     * updateByQuery 的 SQL 构建。
190     *
191     * @param params 方法参数
192     * @return SQL 语句
193     * @see RowMapper#updateByQuery(String, String, Row, QueryWrapper)
194     */
195    public static String updateByQuery(Map params) {
196        String schema = ProviderUtil.getSchemaName(params);
197        String tableName = ProviderUtil.getTableName(params);
198        Row data = ProviderUtil.getRow(params);
199
200        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
201        CPI.setFromIfNecessary(queryWrapper, schema, tableName);
202
203        // 优先构建 sql,再构建参数
204        String sql = DialectFactory.getDialect().forUpdateByQuery(queryWrapper, data);
205
206        Object[] modifyValues = RowCPI.obtainModifyValues(data);
207        Object[] valueArray = CPI.getValueArray(queryWrapper);
208
209        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(modifyValues, valueArray));
210
211        return sql;
212    }
213
214    /**
215     * updateBatchById 的 SQL 构建。
216     * mysql 等链接配置需要开启 allowMultiQueries=true
217     *
218     * @param params 方法参数
219     * @return SQL 语句
220     * @see RowMapper#updateBatchById(String, String, List)
221     */
222    public static String updateBatchById(Map params) {
223        List<Row> rows = ProviderUtil.getRows(params);
224
225        FlexAssert.notEmpty(rows, "rows");
226
227        String schema = ProviderUtil.getSchemaName(params);
228        String tableName = ProviderUtil.getTableName(params);
229
230        String sql = DialectFactory.getDialect().forUpdateBatchById(schema, tableName, rows);
231
232        Object[] values = FlexConsts.EMPTY_ARRAY;
233        for (Row row : rows) {
234            values = ArrayUtil.concat(values, RowCPI.obtainUpdateValues(row));
235        }
236        ProviderUtil.setSqlArgs(params, values);
237        return sql;
238    }
239
240    /**
241     * updateEntity 的 SQL 构建。
242     *
243     * @param params 方法参数
244     * @return SQL 语句
245     * @see RowMapper#updateEntity(Object entities)
246     */
247    public static String updateEntity(Map params) {
248        Object entity = ProviderUtil.getEntity(params);
249
250        FlexAssert.notNull(entity, "entity can not be null for execute update");
251
252        // 该 Mapper 是通用 Mapper  无法通过 ProviderContext 获取,直接使用 TableInfoFactory
253
254        TableInfo tableInfo = TableInfoFactory.ofEntityClass(ClassUtil.getUsefulClass(entity.getClass()));
255        // 执行 onUpdate 监听器
256        tableInfo.invokeOnUpdateListener(entity);
257
258        String sql = DialectFactory.getDialect().forUpdateEntity(tableInfo, entity, false);
259
260        Object[] updateValues = tableInfo.buildUpdateSqlArgs(entity, false, false);
261        Object[] primaryValues = tableInfo.buildPkSqlArgs(entity);
262        Object[] tenantIdArgs = tableInfo.buildTenantIdArgs();
263
264        FlexAssert.assertAreNotNull(primaryValues, "The value of primary key must not be null for execute update an entity, entity[%s]", entity);
265
266        ProviderUtil.setSqlArgs(params, ArrayUtil.concat(updateValues, primaryValues, tenantIdArgs));
267        return sql;
268    }
269
270
271    /**
272     * selectOneById 的 SQL 构建。
273     *
274     * @param params 方法参数
275     * @return SQL 语句
276     * @see RowMapper#selectOneById(String, String, String, Object)
277     */
278    public static String selectOneById(Map params) {
279        String schema = ProviderUtil.getSchemaName(params);
280        String tableName = ProviderUtil.getTableName(params);
281        String[] primaryKeys = ProviderUtil.getPrimaryKeys(params);
282        Object[] primaryValues = ProviderUtil.getPrimaryValues(params);
283
284        String sql = DialectFactory.getDialect().forSelectOneById(schema, tableName, primaryKeys, primaryValues);
285        ProviderUtil.setSqlArgs(params, primaryValues);
286
287        return sql;
288    }
289
290    /**
291     * selectListByQuery 的 SQL 构建。
292     *
293     * @param params 方法参数
294     * @return SQL 语句
295     * @see RowMapper#selectListByQuery(String, String, QueryWrapper)
296     */
297    public static String selectListByQuery(Map params) {
298        String schema = ProviderUtil.getSchemaName(params);
299        String tableName = ProviderUtil.getTableName(params);
300
301        QueryWrapper queryWrapper = ProviderUtil.getQueryWrapper(params);
302        CPI.setFromIfNecessary(queryWrapper, schema, tableName);
303
304        // 优先构建 sql,再构建参数
305        String sql = DialectFactory.getDialect().forSelectByQuery(queryWrapper);
306
307        Object[] valueArray = CPI.getValueArray(queryWrapper);
308        ProviderUtil.setSqlArgs(params, valueArray);
309
310        return sql;
311    }
312
313}