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