001/*
002 *  Copyright (c) 2022-2025, 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 */
016
017package com.mybatisflex.core.row;
018
019import com.mybatisflex.core.exception.FlexExceptions;
020import com.mybatisflex.core.paginate.Page;
021import com.mybatisflex.core.query.QueryColumn;
022import com.mybatisflex.core.query.QueryTable;
023import com.mybatisflex.core.query.QueryWrapper;
024import com.mybatisflex.core.query.QueryWrapperAdapter;
025import com.mybatisflex.core.table.ColumnInfo;
026import com.mybatisflex.core.table.IdInfo;
027import com.mybatisflex.core.table.TableInfo;
028import com.mybatisflex.core.table.TableInfoFactory;
029import com.mybatisflex.core.update.PropertySetter;
030import com.mybatisflex.core.util.FieldWrapper;
031import com.mybatisflex.core.util.LambdaGetter;
032import com.mybatisflex.core.util.SqlUtil;
033
034import java.util.ArrayList;
035import java.util.List;
036import java.util.Map;
037import java.util.Optional;
038import java.util.stream.Collectors;
039
040/**
041 * 链式构建 {@link QueryWrapper} 并执行 {@link Db} 方法。
042 *
043 * @author 王帅
044 * @since 2023-07-22
045 */
046public class DbChain extends QueryWrapperAdapter<DbChain> implements PropertySetter<DbChain> {
047
048    private final String schema;
049    private final String tableName;
050    private Row rowData;
051
052    private DbChain(String tableName) {
053        this(null, tableName);
054    }
055
056    private DbChain(String schema, String tableName) {
057        this.schema = schema;
058        this.tableName = tableName;
059        this.queryTables = new ArrayList<>();
060        this.queryTables.add(new QueryTable(schema, tableName));
061    }
062
063    public static DbChain create() {
064        throw new UnsupportedOperationException("Please use DbChain#table(...) to replace DbChain.create()");
065    }
066
067    public static DbChain create(Object entity) {
068        throw new UnsupportedOperationException("Please use DbChain#table(...) to replace DbChain.create(entity)");
069    }
070
071    public static DbChain table(String tableName) {
072        return new DbChain(tableName);
073    }
074
075    public static DbChain table(String schema, String tableName) {
076        return new DbChain(schema, tableName);
077    }
078
079    public static DbChain table(Class<?> entityClass) {
080        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entityClass);
081        return table(tableInfo.getSchema(), tableInfo.getTableName());
082    }
083
084    public static DbChain table(QueryTable queryTable) {
085        return table(queryTable.getSchema(), queryTable.getName());
086    }
087
088    private Row getRow() {
089        if (rowData == null) {
090            this.rowData = new Row();
091        }
092        return rowData;
093    }
094
095    public DbChain setId(RowKey rowKey) {
096        getRow().getPrimaryKeys().add(rowKey);
097        return this;
098    }
099
100    public DbChain setId(RowKey rowKey, Object value) {
101        getRow().getPrimaryKeys().add(rowKey);
102        getRow().put(rowKey.keyColumn, value);
103        return this;
104    }
105
106    @Override
107    public DbChain set(String property, Object value, boolean isEffective) {
108        getRow().set(property, value, isEffective);
109        return this;
110    }
111
112    @Override
113    public DbChain set(QueryColumn property, Object value, boolean isEffective) {
114        getRow().set(property, value, isEffective);
115        return this;
116    }
117
118    @Override
119    public <T> DbChain set(LambdaGetter<T> property, Object value, boolean isEffective) {
120        getRow().set(property, value, isEffective);
121        return this;
122    }
123
124    @Override
125    public DbChain setRaw(String property, Object value, boolean isEffective) {
126        getRow().setRaw(property, value, isEffective);
127        return this;
128    }
129
130    @Override
131    public DbChain setRaw(QueryColumn property, Object value, boolean isEffective) {
132        getRow().setRaw(property, value, isEffective);
133        return this;
134    }
135
136    @Override
137    public <T> DbChain setRaw(LambdaGetter<T> property, Object value, boolean isEffective) {
138        getRow().setRaw(property, value, isEffective);
139        return this;
140    }
141
142    public boolean save(Object entity) {
143        return SqlUtil.toBool(Db.insert(schema, tableName, toRow(entity)));
144    }
145
146    public boolean save() {
147        return SqlUtil.toBool(Db.insert(schema, tableName, getRow()));
148    }
149
150    public boolean remove() {
151        return SqlUtil.toBool(Db.deleteByQuery(schema, tableName, this));
152    }
153
154    public boolean removeById() {
155        return SqlUtil.toBool(Db.deleteById(schema, tableName, getRow()));
156    }
157
158    public boolean update() {
159        return SqlUtil.toBool(Db.updateByQuery(schema, tableName, getRow(), this));
160    }
161
162    public boolean updateById() {
163        return SqlUtil.toBool(Db.updateById(schema, tableName, getRow()));
164    }
165
166    private static Row toRow(Object entity) {
167        Class<?> entityClass = entity.getClass();
168        TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass());
169        Row row = new Row();
170
171        // 添加非主键列设置的值
172        for (ColumnInfo columnInfo : tableInfo.getColumnInfoList()) {
173            try {
174                FieldWrapper fieldWrapper = FieldWrapper.of(entityClass, columnInfo.getProperty());
175                Object value = fieldWrapper.get(entity);
176                if (value != null) {
177                    row.put(columnInfo.getColumn(), value);
178                }
179            } catch (Exception e) {
180                throw FlexExceptions.wrap(e);
181            }
182        }
183
184        // 添加主键列设置的值
185        for (IdInfo idInfo : tableInfo.getPrimaryKeyList()) {
186            try {
187                FieldWrapper fieldWrapper = FieldWrapper.of(entityClass, idInfo.getProperty());
188                Object value = fieldWrapper.get(entity);
189                if (value != null) {
190                    RowKey rowKey = RowKey.of(idInfo.getColumn()
191                        , idInfo.getKeyType()
192                        , idInfo.getValue()
193                        , idInfo.getBefore());
194                    row.getPrimaryKeys().add(rowKey);
195                    row.put(rowKey.keyColumn, value);
196                }
197            } catch (Exception e) {
198                throw FlexExceptions.wrap(e);
199            }
200        }
201
202        return row;
203    }
204
205    public boolean update(Object entity) {
206        return update(toRow(entity));
207    }
208
209    public boolean update(Row data) {
210        return SqlUtil.toBool(Db.updateByQuery(schema, tableName, data, this));
211    }
212
213    public boolean update(Map<String, Object> data) {
214        Row row = new Row();
215        row.putAll(data);
216        return update(row);
217    }
218
219    public long count() {
220        return Db.selectCountByQuery(schema, tableName, this);
221    }
222
223    public boolean exists() {
224        return SqlUtil.toBool(count());
225    }
226
227    public Row one() {
228        return Db.selectOneByQuery(schema, tableName, this);
229    }
230
231    public Optional<Row> oneOpt() {
232        return Optional.ofNullable(one());
233    }
234
235    public <R> R oneAs(Class<R> asType) {
236        return one().toEntity(asType);
237    }
238
239    public <R> Optional<R> oneAsOpt(Class<R> asType) {
240        return Optional.ofNullable(oneAs(asType));
241    }
242
243    public Object obj() {
244        return Db.selectObject(schema, tableName, this);
245    }
246
247    public Optional<Object> objOpt() {
248        return Optional.ofNullable(obj());
249    }
250
251    @SuppressWarnings("unchecked")
252    public <R> R objAs() {
253        return (R) obj();
254    }
255
256    public <R> R objAs(Class<R> asType) {
257        return asType.cast(obj());
258    }
259
260    public <R> Optional<R> objAsOpt() {
261        return Optional.ofNullable(objAs());
262    }
263
264    public <R> Optional<R> objAsOpt(Class<R> asType) {
265        return Optional.ofNullable(objAs(asType));
266    }
267
268    public List<Object> objList() {
269        return Db.selectObjectList(schema, tableName, this);
270    }
271
272    @SuppressWarnings("unchecked")
273    public <R> List<R> objListAs() {
274        return objList()
275            .stream()
276            .map(obj -> (R) obj)
277            .collect(Collectors.toList());
278    }
279
280    public <R> List<R> objListAs(Class<R> asType) {
281        return objList()
282            .stream()
283            .map(asType::cast)
284            .collect(Collectors.toList());
285    }
286
287    public List<Row> list() {
288        return Db.selectListByQuery(schema, tableName, this);
289    }
290
291    public <R> List<R> listAs(Class<R> asType) {
292        return list()
293            .stream()
294            .map(row -> row.toEntity(asType))
295            .collect(Collectors.toList());
296    }
297
298    public Page<Row> page(Page<Row> page) {
299        return Db.paginate(schema, tableName, page, this);
300    }
301
302    public <R> Page<R> pageAs(Page<R> page, Class<R> asType) {
303        Page<Row> rowPage = new Page<>();
304        rowPage.setPageNumber(page.getPageNumber());
305        rowPage.setPageSize(page.getPageSize());
306        rowPage.setTotalRow(page.getTotalRow());
307        return page(rowPage).map(row -> row.toEntity(asType));
308    }
309
310}