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