diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 569da4b2..eaf71c45 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -78,6 +78,7 @@ export default defineConfig({ {text: '乐观锁', link: '/zh/core/version'}, {text: '数据填充', link: '/zh/core/fill'}, {text: '数据脱敏', link: '/zh/core/mask'}, + {text: '数据缓存', link: '/zh/core/data-cache'}, {text: 'SQL 审计', link: '/zh/core/audit'}, {text: 'SQL 打印', link: '/zh/core/sql-print'}, {text: '多数据源', link: '/zh/core/multi-datasource'}, diff --git a/docs/zh/core/data-cache.md b/docs/zh/core/data-cache.md new file mode 100644 index 00000000..cb9edbea --- /dev/null +++ b/docs/zh/core/data-cache.md @@ -0,0 +1,170 @@ +# 数据缓存 + +MyBatis-Flex 是一个 MyBatis 增强框架,所以您可以使用 MyBatis 提供的二级缓存来作为数据缓存。但是它仍然有很多的缺点,比如不适用于分布式环境,在这里推荐使用 [Spring Cache](https://docs.spring.io/spring-framework/docs/5.2.24.RELEASE/spring-framework-reference/integration.html#cache) 模块来处理数据缓存。 + +## 使用方法 + +因为要用到 Spring Cache 模块,所以您的项目必须要使用 Spring Framework 框架,这里以 Spring Boot 项目作为例子,实现 MyBatis-Flex 项目将缓存数据存入 Redis 组件中。 + +1、引入 `spring-boot-starter-cache` 和 `spring-boot-starter-data-redis`模块 + +```xml + + + org.springframework.boot + spring-boot-starter-cache + + + org.springframework.boot + spring-boot-starter-data-redis + + +``` + +2、设置 Redis 连接信息(全是默认的话可以跳过这步) + +```yaml +spring: + redis: + port: 6379 + host: localhost +``` + +3、在 Spring Boot 配置类上启用 Spring Cache 缓存 + +```java +@EnableCaching +@Configuration +public class CacheConfig { +} +``` + +4、将 ServiceImpl 默认实现类换为 CacheableServiceImpl 实现类 + +```java +public interface AccountService extends IService { +} + +@Service +public class AccountServiceImpl extends CacheableServiceImpl implements AccountService { +} +``` + +5、最后即可使用 Spring Cache 的相关注解实现数据缓存到 Redis 中了 + +```java +// 设置统一的缓存名称 +@Service +@CacheConfig(cacheNames = "account") +public class AccountServiceImpl extends CacheableServiceImpl implements AccountService { + + // 根据主键缓存数据 + @Override + @Cacheable(key = "#id") + public Account getById(Serializable id) { + return super.getById(id); + } + + // 根据方法名加查询 SQL 语句缓存结果数据 + // 加上方法名是为了避免不同的方法使用一样的 QueryWrapper + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public List list(QueryWrapper query) { + return super.list(query); + } + +} +``` + +## 使用说明 + +MyBatis-Flex 在 IService 接口中做了方法调用链优化,所以您只需将缓存注解加到一些特定的方法上,即可实现所有相关的方法也可以进行数据缓存。相关方法见如下示例: + +```java +@Service +@CacheConfig(cacheNames = "account") +public class AccountServiceImpl extends CacheableServiceImpl { + + @Override + @CacheEvict(allEntries = true) + public boolean remove(QueryWrapper query) { + return super.remove(query); + } + + @Override + @CacheEvict(key = "#id") + public boolean removeById(Serializable id) { + return super.removeById(id); + } + + @Override + @CacheEvict(allEntries = true) + public boolean removeByIds(Collection ids) { + return super.removeByIds(ids); + } + + @Override + @CachePut(key = "#entity.id") + public boolean update(Account entity, QueryWrapper query) { + return super.update(entity, query); + } + + @Override + @CachePut(key = "#entity.id") + public boolean updateById(Account entity) { + return super.updateById(entity); + } + + @Override + @Cacheable(key = "#id") + public Account getById(Serializable id) { + return super.getById(id); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public Account getOne(QueryWrapper query) { + return super.getOne(query); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public R getOneAs(QueryWrapper query, Class asType) { + return super.getOneAs(query, asType); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public List list(QueryWrapper query) { + return super.list(query); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public List listAs(QueryWrapper query, Class asType) { + return super.listAs(query, asType); + } + + // 无法通过注解进行缓存操作 + @Override + @Deprecated + public List listByIds(Collection ids) { + return super.listByIds(ids); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public long count(QueryWrapper query) { + return super.count(query); + } + + @Override + @Cacheable(key = "#root.methodName + ':' + #query.toDebugSQL()") + public Page page(Page page, QueryWrapper query) { + return super.page(page, query); + } + +} +``` + + diff --git a/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java b/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java index 3f82205c..96124ce3 100644 --- a/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java +++ b/mybatis-flex-core/src/main/java/com/mybatisflex/core/service/IService.java @@ -1,26 +1,26 @@ -/** - * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.mybatisflex.core.service; import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.exception.FlexExceptions; import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.query.QueryCondition; import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.row.Db; -import com.mybatisflex.core.row.RowMapper; import com.mybatisflex.core.util.ClassUtil; import com.mybatisflex.core.util.CollectionUtil; import com.mybatisflex.core.util.SqlUtil; @@ -36,20 +36,22 @@ import java.util.*; * @author 王帅 * @since 2023-05-01 */ -@SuppressWarnings("unused") +@SuppressWarnings({"unused", "unchecked"}) public interface IService { + int DEFAULT_BATCH_SIZE = 1000; + // ===== 保存(增)操作 ===== /** - * 获取对应实体类(Entity)的基础映射类(BaseMapper)。 + *

获取对应实体类(Entity)的基础映射类(BaseMapper)。 * * @return 基础映射类(BaseMapper) */ BaseMapper getMapper(); /** - * 保存实体类对象数据。 + *

保存实体类对象数据。 * * @param entity 实体类对象 * @return {@code true} 保存成功,{@code false} 保存失败。 @@ -60,9 +62,8 @@ public interface IService { return SqlUtil.toBool(getMapper().insertSelective(entity)); } - /** - * 保存或者更新实体类对象数据。 + *

保存或者更新实体类对象数据。 * * @param entity 实体类对象 * @return {@code true} 保存或更新成功,{@code false} 保存或更新失败。 @@ -73,30 +74,30 @@ public interface IService { } /** - * 批量保存实体类对象数据。 + *

批量保存实体类对象数据。 * * @param entities 实体类对象 * @return {@code true} 保存成功,{@code false} 保存失败。 */ default boolean saveBatch(Collection entities) { - return SqlUtil.toBool(getMapper().insertBatch(new ArrayList<>(entities))); + return saveBatch(entities, DEFAULT_BATCH_SIZE); } // ===== 删除(删)操作 ===== /** - * 批量保存实体类对象数据。 + *

批量保存实体类对象数据。 * - * @param entities 实体类对象 - * @param size 每次保存切分的数量 + * @param entities 实体类对象 + * @param batchSize 每次保存切分的数量 * @return {@code true} 保存成功,{@code false} 保存失败。 */ - default boolean saveBatch(Collection entities, int size) { - return SqlUtil.toBool(getMapper().insertBatch(CollectionUtil.toList(entities), size)); + default boolean saveBatch(Collection entities, int batchSize) { + return SqlUtil.toBool(getMapper().insertBatch(CollectionUtil.toList(entities), batchSize)); } /** - * 根据查询条件删除数据。 + *

根据查询条件删除数据。 * * @param query 查询条件 * @return {@code true} 删除成功,{@code false} 删除失败。 @@ -106,17 +107,17 @@ public interface IService { } /** - * 根据查询条件删除数据。 + *

根据查询条件删除数据。 * * @param condition 查询条件 * @return {@code true} 删除成功,{@code false} 删除失败。 */ default boolean remove(QueryCondition condition) { - return SqlUtil.toBool(getMapper().deleteByCondition(condition)); + return remove(query().where(condition)); } /** - * 根据数据主键删除数据。 + *

根据数据主键删除数据。 * * @param id 数据主键 * @return {@code true} 删除成功,{@code false} 删除失败。 @@ -126,7 +127,7 @@ public interface IService { } /** - * 根据数据主键批量删除数据。 + *

根据数据主键批量删除数据。 * * @param ids 数据主键 * @return {@code true} 删除成功,{@code false} 删除失败。 @@ -141,40 +142,21 @@ public interface IService { // ===== 更新(改)操作 ===== /** - * 根据 {@link Map} 构建查询条件删除数据。 + *

根据 {@link Map} 构建查询条件删除数据。 * * @param query 查询条件 * @return {@code true} 删除成功,{@code false} 删除失败。 */ default boolean removeByMap(Map query) { - return SqlUtil.toBool(getMapper().deleteByMap(query)); - } - - - /** - * 更加主键更新 entity,默认忽略 null 值 - * - * @param entity 实体类对象 - * @return {@code true} 更新成功,{@code false} 更新失败。 - */ - default boolean updateById(T entity) { - return updateById(entity, true); - } - - - /** - * 更加主键更新 entity - * - * @param entity entity - * @param ignoreNulls 是否忽略 null 值 - * @return {@code true} 更新成功,{@code false} 更新失败。 - */ - default boolean updateById(T entity, boolean ignoreNulls) { - return SqlUtil.toBool(getMapper().update(entity, ignoreNulls)); + // 防止全表删除 + if (query == null || query.isEmpty()) { + throw FlexExceptions.wrap("deleteByMap is not allow empty map."); + } + return remove(query().where(query)); } /** - * 根据查询条件更新数据。 + *

根据查询条件更新数据。 * * @param entity 实体类对象 * @param query 查询条件 @@ -184,62 +166,69 @@ public interface IService { return SqlUtil.toBool(getMapper().updateByQuery(entity, query)); } - /** - * 根据 {@link Map} 构建查询条件更新数据。 - * - * @param entity 实体类对象 - * @param query 查询条件 - * @return {@code true} 更新成功,{@code false} 更新失败。 - */ - default boolean update(T entity, Map query) { - return SqlUtil.toBool(getMapper().updateByMap(entity, query)); - } - - /** - * 根据查询条件更新数据。 + *

根据查询条件更新数据。 * * @param entity 实体类对象 * @param condition 查询条件 * @return {@code true} 更新成功,{@code false} 更新失败。 */ default boolean update(T entity, QueryCondition condition) { - return SqlUtil.toBool(getMapper().updateByCondition(entity, condition)); + return update(entity, query().where(condition)); } /** - * 根据 id 批量更新数据 + *

根据数据主键批量更新数据 * * @param entities 实体类对象集合 * @return boolean {@code true} 更新成功,{@code false} 更新失败。 */ default boolean updateBatch(Collection entities) { - return updateBatch(entities, RowMapper.DEFAULT_BATCH_SIZE); + return updateBatch(entities, DEFAULT_BATCH_SIZE); } /** - * 根据 id 批量更新数据 + *

根据数据主键批量更新数据 * * @param entities 实体类对象集合 * @param batchSize 每批次更新数量 - * @return boolean {@code true} 更新成功,{@code false} 更新失败。 + * @return {@code true} 更新成功,{@code false} 更新失败。 */ @SuppressWarnings("unchecked") default boolean updateBatch(Collection entities, int batchSize) { return Db.tx(() -> { final List entityList = CollectionUtil.toList(entities); - // BaseMapper 是经过 Mybatis 动态代理处理过的对象,需要获取原始 BaseMapper 类型 final Class> usefulClass = (Class>) ClassUtil.getUsefulClass(getMapper().getClass()); return SqlUtil.toBool(Arrays.stream(Db.executeBatch(entityList.size(), batchSize, usefulClass, (mapper, index) -> mapper.update(entityList.get(index)))).sum()); }); } + /** + *

根据数据主键更新数据。 + * + * @param entity 实体类对象 + * @return {@code true} 更新成功,{@code false} 更新失败。 + */ + default boolean updateById(T entity) { + return SqlUtil.toBool(getMapper().update(entity)); + } + + /** + *

根据 {@link Map} 构建查询条件更新数据。 + * + * @param entity 实体类对象 + * @param query 查询条件 + * @return {@code true} 更新成功,{@code false} 更新失败。 + */ + default boolean updateByMap(T entity, Map query) { + return update(entity, query().where(query)); + } // ===== 查询(查)操作 ===== /** - * 根据数据主键查询一条数据。 + *

根据数据主键查询一条数据。 * * @param id 数据主键 * @return 查询结果数据 @@ -249,7 +238,7 @@ public interface IService { } /** - * 根据数据主键查询一条数据。 + *

根据数据主键查询一条数据。 * * @param id 数据主键 * @return 查询结果数据 @@ -260,7 +249,7 @@ public interface IService { } /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param query 查询条件 * @return 查询结果数据 @@ -269,9 +258,19 @@ public interface IService { return getMapper().selectOneByQuery(query); } + /** + *

根据查询条件查询一条数据。 + * + * @param query 查询条件 + * @return 查询结果数据 + * @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。 + */ + default Optional getOneOpt(QueryWrapper query) { + return Optional.ofNullable(getOne(query)); + } /** - * 根据查询条件查询一条数据,并通过 asType 进行接收 + *

根据查询条件查询一条数据,并通过 asType 进行接收 * * @param query 查询条件 * @param asType 接收的数据类型 @@ -282,41 +281,29 @@ public interface IService { } /** - * 根据查询条件查询一条数据。 - * - * @param query 查询条件 - * @return 查询结果数据 - * @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。 - */ - default Optional getOneOpt(QueryWrapper query) { - return Optional.ofNullable(getOne(query)); - } - - - /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param query 查询条件 * @param asType 接收的数据类型 * @return 查询结果数据 * @apiNote 该方法会将查询结果封装为 {@link Optional} 类进行返回,方便链式操作。 */ - default Optional getOneOptAs(QueryWrapper query, Class asType) { + default Optional getOneAsOpt(QueryWrapper query, Class asType) { return Optional.ofNullable(getOneAs(query, asType)); } /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param condition 查询条件 * @return 查询结果数据 */ default T getOne(QueryCondition condition) { - return getMapper().selectOneByCondition(condition); + return getOne(query().where(condition)); } /** - * 根据查询条件查询一条数据。 + *

根据查询条件查询一条数据。 * * @param condition 查询条件 * @return 查询结果数据 @@ -327,37 +314,16 @@ public interface IService { } /** - * 查询所有数据。 + *

查询所有数据。 * * @return 所有数据 */ default List list() { - return getMapper().selectAll(); + return list(query()); } /** - * 根据查询条件查询数据集合。 - * - * @param condition 查询条件 - * @return 数据集合 - */ - default List list(QueryCondition condition) { - return getMapper().selectListByCondition(condition); - } - - /** - * 根据查询条件查询数据集合。 - * - * @param condition 查询条件 - * @return 数据集合 - */ - default List list(QueryCondition condition, int count) { - return getMapper().selectListByCondition(condition, count); - } - - - /** - * 根据查询条件查询数据集合。 + *

根据查询条件查询数据集合。 * * @param query 查询条件 * @return 数据集合 @@ -367,7 +333,17 @@ public interface IService { } /** - * 根据查询条件查询数据集合,并通过 asType 进行接收 + *

根据查询条件查询数据集合。 + * + * @param condition 查询条件 + * @return 数据集合 + */ + default List list(QueryCondition condition) { + return list(query().where(condition)); + } + + /** + *

根据查询条件查询数据集合,并通过 asType 进行接收 * * @param query 查询条件 * @param asType 接收的数据类型 @@ -377,9 +353,8 @@ public interface IService { return getMapper().selectListByQueryAs(query, asType); } - /** - * 根据数据主键查询数据集合。 + *

根据数据主键查询数据集合。 * * @param ids 数据主键 * @return 数据集合 @@ -389,19 +364,19 @@ public interface IService { } /** - * 根据 {@link Map} 构建查询条件查询数据集合。 + *

根据 {@link Map} 构建查询条件查询数据集合。 * * @param query 查询条件 * @return 数据集合 */ default List listByMap(Map query) { - return getMapper().selectListByMap(query); + return list(query().where(query)); } // ===== 数量查询操作 ===== /** - * 根据查询条件判断数据是否存在。 + *

根据查询条件判断数据是否存在。 * * @param query 查询条件 * @return {@code true} 数据存在,{@code false} 数据不存在。 @@ -411,7 +386,7 @@ public interface IService { } /** - * 根据查询条件判断数据是否存在。 + *

根据查询条件判断数据是否存在。 * * @param condition 查询条件 * @return {@code true} 数据存在,{@code false} 数据不存在。 @@ -421,16 +396,16 @@ public interface IService { } /** - * 查询所有数据数量。 + *

查询所有数据数量。 * * @return 所有数据数量 */ default long count() { - return getMapper().selectCountByQuery(QueryWrapper.create()); + return count(query()); } /** - * 根据查询条件查询数据数量。 + *

根据查询条件查询数据数量。 * * @param query 查询条件 * @return 数据数量 @@ -440,29 +415,29 @@ public interface IService { } /** - * 根据查询条件查询数据数量。 + *

根据查询条件查询数据数量。 * * @param condition 查询条件 * @return 数据数量 */ default long count(QueryCondition condition) { - return getMapper().selectCountByCondition(condition); + return count(query().where(condition)); } // ===== 分页查询操作 ===== /** - * 分页查询所有数据。 + *

分页查询所有数据。 * * @param page 分页对象 * @return 分页对象 */ default Page page(Page page) { - return getMapper().paginate(page, QueryWrapper.create()); + return page(page, query()); } /** - * 根据查询条件分页查询数据。 + *

根据查询条件分页查询数据。 * * @param page 分页对象 * @param query 查询条件 @@ -473,14 +448,18 @@ public interface IService { } /** - * 根据查询条件分页查询数据。 + *

根据查询条件分页查询数据。 * * @param page 分页对象 * @param condition 查询条件 * @return 分页对象 */ default Page page(Page page, QueryCondition condition) { - return getMapper().paginate(page, QueryWrapper.create().where(condition)); + return page(page, query().where(condition)); + } + + default QueryWrapper query() { + return QueryWrapper.create(); } } \ No newline at end of file diff --git a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/service/impl/CacheableServiceImpl.java b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/service/impl/CacheableServiceImpl.java new file mode 100644 index 00000000..b58b94de --- /dev/null +++ b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/service/impl/CacheableServiceImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mybatisflex.spring.service.impl; + +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.exception.FlexExceptions; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.service.IService; +import com.mybatisflex.core.table.TableInfo; +import com.mybatisflex.core.table.TableInfoFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Collection; + +/** + *

可缓存数据的 Service 实现类。 + * + *

该实现类对缓存做了以下处理: + * + *

    + *
  • 重写 {@link #saveOrUpdate(Object)} 方法,分别调用 {@link #save(Object)} 和 {@link #updateById(Object)} + * 方法,避免缓存无法更新造成数据不一致。 + *
  • 重写{@link #updateBatch(Collection, int)} 方法,默认抛出异常,不支持批量更新操作, + * 防止批量更新数据,缓存不一致。 + *
  • 重写 {@link #query()} 方法,解决使用 {@link QueryWrapper#toDebugSQL()} 作为缓存 + * 的主键时,"SELECT * FROM" 后面没有表名的问题。 + *
+ * + * @author 王帅 + * @since 2023-05-30 + */ +public class CacheableServiceImpl, T> implements IService { + + @Autowired + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + private M mapper; + + /** + * {@inheritDoc} + */ + @Override + public BaseMapper getMapper() { + return mapper; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean saveOrUpdate(T entity) { + TableInfo tableInfo = TableInfoFactory.ofEntityClass(entity.getClass()); + Object[] pkArgs = tableInfo.buildPkSqlArgs(entity); + if (pkArgs.length == 0 || pkArgs[0] == null) { + return save(entity); + } else { + return updateById(entity); + } + } + + /** + *

不支持批量更新操作。 + */ + @Override + public boolean updateBatch(Collection entities, int batchSize) { + throw FlexExceptions.wrap("Batch update do not support caching operation."); + } + + /** + *

获取默认的 {@link QueryWrapper}。 + * + *

使用 {@link QueryWrapper#create()} 构建默认查询条件的时候, + * 要使用 {@link QueryWrapper#from(String...)} 方法指定从哪个表 + * 查询数据,不然使用 {@link QueryWrapper#toDebugSQL()} 生成的 + * SQL 语句就是 {@code "SELECT * FROM"},没有表名信息。 + * + *

默认通过反射获取表名,建议重写,根据情况设置默认表名,以提升效率。 + * + *

例如: + * + *

{@code
+     * @Override
+     * public QueryWrapper query() {
+     *     return QueryWrapper.create().from(ACCOUNT);
+     * }
+     * }
+ * + * @return 默认的 {@link QueryWrapper} + */ + @Override + public QueryWrapper query() { + String tableName = TableInfoFactory.ofMapperClass(getMapper().getClass()).getTableName(); + return QueryWrapper.create().from(tableName); + } + +} \ No newline at end of file