From e1c9a91c77127a1f2a87f6c2effa813a82fc1877 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Sat, 5 Jul 2025 16:09:15 +0800 Subject: [PATCH] =?UTF-8?q?refactor(data/mp):=20=E5=B0=86=20MP=20=E7=9A=84?= =?UTF-8?q?=20CrudRepository=20=E8=BF=81=E7=A7=BB=E8=87=B3=20ServiceImpl?= =?UTF-8?q?=20=E7=B1=BB=E4=B8=AD=EF=BC=8C=E5=87=8F=E5=B0=91=E4=B8=A4?= =?UTF-8?q?=E5=B1=82=E7=BB=A7=E6=89=BF=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=B1=82?= =?UTF-8?q?=E7=BA=A7=E8=BF=87=E5=A4=9A=E5=87=BA=E7=8E=B0=20Sonar=20?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/service/impl/ServiceImpl.java | 252 +++++++++++++++++- 1 file changed, 251 insertions(+), 1 deletion(-) diff --git a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/service/impl/ServiceImpl.java b/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/service/impl/ServiceImpl.java index 8e9a01f7..2523fc04 100644 --- a/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/service/impl/ServiceImpl.java +++ b/continew-starter-data/continew-starter-data-mp/src/main/java/top/continew/starter/data/service/impl/ServiceImpl.java @@ -17,33 +17,258 @@ package top.continew.starter.data.service.impl; import cn.hutool.core.util.ClassUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.MapperProxyMetadata; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.baomidou.mybatisplus.core.toolkit.*; +import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils; +import com.baomidou.mybatisplus.extension.repository.AbstractRepository; import com.baomidou.mybatisplus.extension.repository.CrudRepository; +import com.baomidou.mybatisplus.extension.repository.IRepository; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import org.apache.ibatis.binding.MapperMethod; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; import top.continew.starter.core.util.ReflectUtils; import top.continew.starter.data.service.IService; import top.continew.starter.core.util.validation.CheckUtils; import java.io.Serializable; import java.lang.reflect.Field; +import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; /** * 通用业务实现类 * + *

将 MP 的 {@link CrudRepository} 迁移至本类中,减少两层继承,解决层级过多出现 Sonar 警告的问题

+ * + * @see CrudRepository * @param Mapper 接口 * @param 实体类型 + * @author hubin (MyBatis Plus) * @author Charles7c * @since 1.5.0 */ -public class ServiceImpl, T> extends CrudRepository implements IService { +public abstract class ServiceImpl, T> implements IService { + @Autowired + protected M baseMapper; + private Class entityClass; + private Class mapperClass; private List entityFields; + private volatile SqlSessionFactory sqlSessionFactory; + private final Log innerLog = LogFactory.getLog(getClass()); + + /** + * TableId 注解存在更新记录,否插入一条记录 + * + * @param entity 实体对象 + * @return boolean + * @see AbstractRepository#saveOrUpdate(Object) + */ + @Override + public boolean saveOrUpdate(T entity) { + return getBaseMapper().insertOrUpdate(entity); + } + + /** + * 根据 Wrapper,查询一条记录 + * + * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} + * @param throwEx 有多个 result 是否抛出异常 + * @return 单条数据 + * @see AbstractRepository#getOne(Wrapper, boolean) + */ + @Override + public T getOne(Wrapper queryWrapper, boolean throwEx) { + return getBaseMapper().selectOne(queryWrapper, throwEx); + } + + /** + * 根据 Wrapper,查询一条记录 + * + * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} + * @param throwEx 有多个 result 是否抛出异常 + * @return {@link Optional} 返回一个Optional对象 + * @see AbstractRepository#getOneOpt(Wrapper, boolean) + */ + @Override + public Optional getOneOpt(Wrapper queryWrapper, boolean throwEx) { + return Optional.ofNullable(getBaseMapper().selectOne(queryWrapper, throwEx)); + } + + /** + * 根据 Wrapper,查询一条记录 + * + * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} + * @return 单条数据 + * @see AbstractRepository#getMap(Wrapper) + */ + @Override + public Map getMap(Wrapper queryWrapper) { + return SqlHelper.getObject(innerLog, getBaseMapper().selectMaps(queryWrapper)); + } + + /** + * 根据 Wrapper,查询一条记录 + * + * @param queryWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper} + * @param mapper 转换函数 + * @return 单条数据 + * @see AbstractRepository#getObj(Wrapper, Function) + */ + @Override + public V getObj(Wrapper queryWrapper, Function mapper) { + return SqlHelper.getObject(innerLog, listObjs(queryWrapper, mapper)); + } + + /** + * 执行批量操作 + * + * @param list 数据集合 + * @param batchSize 批量大小 + * @param consumer 执行方法 + * @param 泛型 + * @return 操作结果 + * @see AbstractRepository#executeBatch(Collection, int, BiConsumer) + */ + protected boolean executeBatch(Collection list, int batchSize, BiConsumer consumer) { + return SqlHelper.executeBatch(getSqlSessionFactory(), this.innerLog, list, batchSize, consumer); + } + + /** + * 执行批量操作(默认批次提交数量{@link IRepository#DEFAULT_BATCH_SIZE}) + * + * @param list 数据集合 + * @param consumer 执行方法 + * @param 泛型 + * @return 操作结果 + * @see AbstractRepository#executeBatch(Collection, BiConsumer) + */ + protected boolean executeBatch(Collection list, BiConsumer consumer) { + return executeBatch(list, DEFAULT_BATCH_SIZE, consumer); + } + + /** + * 根据 ID 删除 + * + * @param id 主键(类型必须与实体类型字段保持一致) + * @param useFill 是否启用填充(为true的情况,会将入参转换实体进行delete删除) + * @return 删除结果 + * @see AbstractRepository#removeById(Serializable, boolean) + */ + @Override + public boolean removeById(Serializable id, boolean useFill) { + return SqlHelper.retBool(getBaseMapper().deleteById(id, useFill)); + } + + /** + * 批量插入 + * + * @param entityList 数据集合 + * @param batchSize 批量大小 + * @return boolean + * @see CrudRepository#saveBatch(Collection, int) + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean saveBatch(Collection entityList, int batchSize) { + String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE); + return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity)); + } + + /** + * 批量插入 + * + * @param entityList 数据集合 + * @param batchSize 批量大小 + * @return boolean + * @see CrudRepository#saveOrUpdateBatch(Collection, int) + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean saveOrUpdateBatch(Collection entityList, int batchSize) { + TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass()); + Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!"); + String keyProperty = tableInfo.getKeyProperty(); + Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!"); + return SqlHelper.saveOrUpdateBatch(getSqlSessionFactory(), this + .getMapperClass(), this.innerLog, entityList, batchSize, (sqlSession, entity) -> { + Object idVal = tableInfo.getPropertyValue(entity, keyProperty); + return StringUtils.checkValNull(idVal) || CollectionUtils.isEmpty(sqlSession + .selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity)); + }, (sqlSession, entity) -> { + MapperMethod.ParamMap param = new MapperMethod.ParamMap<>(); + param.put(Constants.ENTITY, entity); + sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param); + }); + } + + /** + * 批量更新 + * + * @param entityList 数据集合 + * @param batchSize 批量大小 + * @return boolean + * @see CrudRepository#updateBatchById(Collection, int) + */ + @Transactional(rollbackFor = Exception.class) + @Override + public boolean updateBatchById(Collection entityList, int batchSize) { + String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID); + return executeBatch(entityList, batchSize, (sqlSession, entity) -> { + MapperMethod.ParamMap param = new MapperMethod.ParamMap<>(); + param.put(Constants.ENTITY, entity); + sqlSession.update(sqlStatement, param); + }); + } @Override public T getById(Serializable id) { return this.getById(id, true); } + @Override + public M getBaseMapper() { + Assert.notNull(this.baseMapper, "baseMapper can not be null"); + return this.baseMapper; + } + + @Override + public Class getEntityClass() { + if (this.entityClass == null) { + this.entityClass = (Class)GenericTypeUtils.resolveTypeArguments(this + .getMapperClass(), BaseMapper.class)[0]; + } + return this.entityClass; + } + + /** + * 获取当前 Mapper 类型 + * + * @return 当前 Mapper 类型 + * @see CrudRepository#getMapperClass() + */ + public Class getMapperClass() { + if (this.mapperClass == null) { + MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this.getBaseMapper()); + this.mapperClass = (Class)mapperProxyMetadata.getMapperInterface(); + } + return this.mapperClass; + } + /** * 获取当前实体类型字段 * @@ -56,6 +281,31 @@ public class ServiceImpl, T> extends CrudRepository