mirror of
				https://github.com/continew-org/continew-starter.git
				synced 2025-10-30 23:00:11 +08:00 
			
		
		
		
	refactor(data/mp): 将 MP 的 CrudRepository 迁移至 ServiceImpl 类中,减少两层继承,解决层级过多出现 Sonar 警告的问题
This commit is contained in:
		| @@ -17,33 +17,258 @@ | |||||||
| package top.continew.starter.data.service.impl; | package top.continew.starter.data.service.impl; | ||||||
|  |  | ||||||
| import cn.hutool.core.util.ClassUtil; | 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.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.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.core.util.ReflectUtils; | ||||||
| import top.continew.starter.data.service.IService; | import top.continew.starter.data.service.IService; | ||||||
| import top.continew.starter.core.util.validation.CheckUtils; | import top.continew.starter.core.util.validation.CheckUtils; | ||||||
|  |  | ||||||
| import java.io.Serializable; | import java.io.Serializable; | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
|  | import java.util.Collection; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | import java.util.Optional; | ||||||
|  | import java.util.function.BiConsumer; | ||||||
|  | import java.util.function.Function; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * 通用业务实现类 |  * 通用业务实现类 | ||||||
|  * |  * | ||||||
|  |  * <p>将 MP 的 {@link CrudRepository} 迁移至本类中,减少两层继承,解决层级过多出现 Sonar 警告的问题</p> | ||||||
|  |  * | ||||||
|  |  * @see CrudRepository | ||||||
|  * @param <M> Mapper 接口 |  * @param <M> Mapper 接口 | ||||||
|  * @param <T> 实体类型 |  * @param <T> 实体类型 | ||||||
|  |  * @author hubin (<a href="https://gitee.com/baomidou/mybatis-plus">MyBatis Plus</a>) | ||||||
|  * @author Charles7c |  * @author Charles7c | ||||||
|  * @since 1.5.0 |  * @since 1.5.0 | ||||||
|  */ |  */ | ||||||
| public class ServiceImpl<M extends BaseMapper<T>, T> extends CrudRepository<M, T> implements IService<T> { | public abstract class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> { | ||||||
|  |  | ||||||
|  |     @Autowired | ||||||
|  |     protected M baseMapper; | ||||||
|  |     private Class<T> entityClass; | ||||||
|  |     private Class<M> mapperClass; | ||||||
|     private List<Field> entityFields; |     private List<Field> 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<T> 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<T> getOneOpt(Wrapper<T> 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<String, Object> getMap(Wrapper<T> 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> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) { | ||||||
|  |         return SqlHelper.getObject(innerLog, listObjs(queryWrapper, mapper)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 执行批量操作 | ||||||
|  |      * | ||||||
|  |      * @param list      数据集合 | ||||||
|  |      * @param batchSize 批量大小 | ||||||
|  |      * @param consumer  执行方法 | ||||||
|  |      * @param <E>       泛型 | ||||||
|  |      * @return 操作结果 | ||||||
|  |      * @see AbstractRepository#executeBatch(Collection, int, BiConsumer) | ||||||
|  |      */ | ||||||
|  |     protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) { | ||||||
|  |         return SqlHelper.executeBatch(getSqlSessionFactory(), this.innerLog, list, batchSize, consumer); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 执行批量操作(默认批次提交数量{@link IRepository#DEFAULT_BATCH_SIZE}) | ||||||
|  |      * | ||||||
|  |      * @param list     数据集合 | ||||||
|  |      * @param consumer 执行方法 | ||||||
|  |      * @param <E>      泛型 | ||||||
|  |      * @return 操作结果 | ||||||
|  |      * @see AbstractRepository#executeBatch(Collection, BiConsumer) | ||||||
|  |      */ | ||||||
|  |     protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> 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<T> 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<T> 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<T> 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<T> entityList, int batchSize) { | ||||||
|  |         String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID); | ||||||
|  |         return executeBatch(entityList, batchSize, (sqlSession, entity) -> { | ||||||
|  |             MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>(); | ||||||
|  |             param.put(Constants.ENTITY, entity); | ||||||
|  |             sqlSession.update(sqlStatement, param); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public T getById(Serializable id) { |     public T getById(Serializable id) { | ||||||
|         return this.getById(id, true); |         return this.getById(id, true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public M getBaseMapper() { | ||||||
|  |         Assert.notNull(this.baseMapper, "baseMapper can not be null"); | ||||||
|  |         return this.baseMapper; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Class<T> getEntityClass() { | ||||||
|  |         if (this.entityClass == null) { | ||||||
|  |             this.entityClass = (Class<T>)GenericTypeUtils.resolveTypeArguments(this | ||||||
|  |                 .getMapperClass(), BaseMapper.class)[0]; | ||||||
|  |         } | ||||||
|  |         return this.entityClass; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取当前 Mapper 类型 | ||||||
|  |      * | ||||||
|  |      * @return 当前 Mapper 类型 | ||||||
|  |      * @see CrudRepository#getMapperClass() | ||||||
|  |      */ | ||||||
|  |     public Class<M> getMapperClass() { | ||||||
|  |         if (this.mapperClass == null) { | ||||||
|  |             MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this.getBaseMapper()); | ||||||
|  |             this.mapperClass = (Class<M>)mapperProxyMetadata.getMapperInterface(); | ||||||
|  |         } | ||||||
|  |         return this.mapperClass; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 获取当前实体类型字段 |      * 获取当前实体类型字段 | ||||||
|      * |      * | ||||||
| @@ -56,6 +281,31 @@ public class ServiceImpl<M extends BaseMapper<T>, T> extends CrudRepository<M, T | |||||||
|         return this.entityFields; |         return this.entityFields; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取 SqlSessionFactory | ||||||
|  |      * | ||||||
|  |      * @return SqlSessionFactory | ||||||
|  |      * @see AbstractRepository#getSqlSessionFactory() | ||||||
|  |      */ | ||||||
|  |     protected SqlSessionFactory getSqlSessionFactory() { | ||||||
|  |         if (this.sqlSessionFactory == null) { | ||||||
|  |             MapperProxyMetadata mapperProxyMetadata = MybatisUtils.getMapperProxy(this.getBaseMapper()); | ||||||
|  |             this.sqlSessionFactory = MybatisUtils.getSqlSessionFactory(mapperProxyMetadata.getSqlSession()); | ||||||
|  |         } | ||||||
|  |         return this.sqlSessionFactory; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 获取mapperStatementId | ||||||
|  |      * | ||||||
|  |      * @param sqlMethod 方法名 | ||||||
|  |      * @return 命名id | ||||||
|  |      * @see CrudRepository#getSqlStatement(SqlMethod) | ||||||
|  |      */ | ||||||
|  |     protected String getSqlStatement(SqlMethod sqlMethod) { | ||||||
|  |         return SqlHelper.getSqlStatement(this.getMapperClass(), sqlMethod); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 根据 ID 查询 |      * 根据 ID 查询 | ||||||
|      * |      * | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user