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