同步到1.8.0

This commit is contained in:
Jerry
2021-08-14 08:47:19 +08:00
parent 49ffcab84d
commit 8658e0ea5a
243 changed files with 2962 additions and 2159 deletions

View File

@@ -96,9 +96,9 @@
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mybatis-mapper.version}</version>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>

View File

@@ -1,16 +0,0 @@
package com.orange.demo.common.core.annotation;
import java.lang.annotation.*;
/**
* 主要用于标记逻辑删除字段。
*
* @author Jerry
* @date 2020-09-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DeletedFlagColumn {
}

View File

@@ -36,13 +36,13 @@ public class DataSourceAspect {
Class<?> clazz = point.getTarget().getClass();
MyDataSource ds = clazz.getAnnotation(MyDataSource.class);
// 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
DataSourceContextHolder.setDataSourceType(ds.value());
Integer originalType = DataSourceContextHolder.setDataSourceType(ds.value());
log.debug("set datasource is " + ds.value());
try {
return point.proceed();
} finally {
DataSourceContextHolder.clear();
log.debug("clean datasource");
DataSourceContextHolder.unset(originalType);
log.debug("unset datasource is " + originalType);
}
}
}

View File

@@ -50,13 +50,13 @@ public class DataSourceResolveAspect {
}
int type = resolver.resolve(dsr.arg(), point.getArgs());
// 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
DataSourceContextHolder.setDataSourceType(type);
Integer originalType = DataSourceContextHolder.setDataSourceType(type);
log.debug("set datasource is " + type);
try {
return point.proceed();
} finally {
DataSourceContextHolder.clear();
log.debug("clean datasource");
DataSourceContextHolder.unset(originalType);
log.debug("unset datasource is " + originalType);
}
}
}

View File

@@ -10,6 +10,8 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* 字典缓存同步的AOP。该AOP的优先级必须比事务切面的优先级高因此会在事务外执行该切面的代码。
*
@@ -39,23 +41,23 @@ public class DictCacheSyncAspect {
Object arg = joinPoint.getArgs()[0];
if ("saveNew".equals(methodName)) {
Object data = joinPoint.proceed();
BaseDictService<Object, Object> service =
(BaseDictService<Object, Object>) joinPoint.getTarget();
BaseDictService<Object, Serializable> service =
(BaseDictService<Object, Serializable>) joinPoint.getTarget();
// 这里参数必须使用saveNew方法的返回对象因为里面包含实际主键值。
service.putDictionaryCache(data);
return data;
} else if ("update".equals(methodName)) {
Object data = joinPoint.proceed();
BaseDictService<Object, Object> service =
(BaseDictService<Object, Object>) joinPoint.getTarget();
BaseDictService<Object, Serializable> service =
(BaseDictService<Object, Serializable>) joinPoint.getTarget();
// update的方法返回的是boolean因此这里的参数需要使用第一个参数即可。
service.putDictionaryCache(arg);
return data;
} else {
// remove
BaseDictService<Object, Object> service =
(BaseDictService<Object, Object>) joinPoint.getTarget();
service.removeDictionaryCache(arg);
BaseDictService<Object, Serializable> service =
(BaseDictService<Object, Serializable>) joinPoint.getTarget();
service.removeDictionaryCache((Serializable) arg);
return joinPoint.proceed();
}
}

View File

@@ -1,10 +1,8 @@
package com.orange.demo.common.core.base.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import tk.mybatis.mapper.additional.insert.InsertListMapper;
import tk.mybatis.mapper.annotation.RegisterMapper;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
import java.util.Map;
@@ -16,8 +14,7 @@ import java.util.Map;
* @author Jerry
* @date 2020-09-24
*/
@RegisterMapper
public interface BaseDaoMapper<M> extends Mapper<M>, InsertListMapper<M> {
public interface BaseDaoMapper<M> extends BaseMapper<M> {
/**
* 根据指定的表名、显示字段列表、过滤条件字符串和分组字段,返回聚合计算后的查询结果。

View File

@@ -1,6 +1,7 @@
package com.orange.demo.common.core.base.service;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.orange.demo.common.core.constant.GlobalDeletedFlag;
import com.orange.demo.common.core.exception.MyRuntimeException;
import com.orange.demo.common.core.cache.DictionaryCache;
@@ -8,8 +9,8 @@ import com.orange.demo.common.core.object.TokenData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import java.io.Serializable;
import java.util.*;
/**
@@ -22,7 +23,7 @@ import java.util.*;
* @date 2020-09-24
*/
@Slf4j
public abstract class BaseDictService<M, K> extends BaseService<M, K> implements IBaseDictService<M, K> {
public abstract class BaseDictService<M, K extends Serializable> extends BaseService<M, K> implements IBaseDictService<M, K> {
/**
* 缓存池对象。
@@ -89,15 +90,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
if (tenantIdField != null) {
ReflectUtil.setFieldValue(data, tenantIdField, TokenData.takeFromRequest().getTenantId());
}
if (deletedFlagFieldName != null) {
try {
setDeletedFlagMethod.invoke(data, GlobalDeletedFlag.NORMAL);
} catch (Exception e) {
log.error("Failed to call reflection [setDeletedFlagMethod] in BaseDictService.update.", e);
throw new MyRuntimeException(e);
}
}
return mapper().updateByPrimaryKey(data) == 1;
return mapper().updateById(data) == 1;
}
/**
@@ -109,7 +102,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
@Transactional(rollbackFor = Exception.class)
@Override
public boolean remove(K id) {
return this.removeById(id);
return mapper().deleteById(id) == 1;
}
/**
@@ -118,15 +111,16 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
* @param id 主键Id。
* @return 主键关联的数据不存在返回null。
*/
@SuppressWarnings("unchecked")
@Override
public M getById(K id) {
M data = dictionaryCache.get(id);
public M getById(Serializable id) {
M data = dictionaryCache.get((K) id);
if (data != null) {
return data;
}
data = super.getById(id);
if (data != null) {
this.dictionaryCache.put(id, data);
this.dictionaryCache.put((K) id, data);
}
return data;
}
@@ -188,7 +182,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
}
/**
* 返回符合 inFilterField in (inFilterValues) 条件的所有数据。蜀国property是主键则从缓存中读取。
* 返回符合 inFilterField in (inFilterValues) 条件的所有数据。属性property是主键则从缓存中读取。
*
* @param inFilterField 参与(In-list)过滤的Java字段。
* @param inFilterValues 参与(In-list)过滤的Java字段值集合。
@@ -200,7 +194,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
if (inFilterField.equals(this.idFieldName)) {
return this.getInList((Set<K>) inFilterValues);
}
return this.getInList(inFilterField, inFilterValues);
return super.getInList(inFilterField, inFilterValues);
}
/**
@@ -220,8 +214,10 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
List<M> dataList = this.getInList((Set<K>) inFilterValues);
return dataList.size() == inFilterValues.size();
}
Example e = this.makeDefaultInListExample(inFilterField, inFilterValues, null);
return mapper().selectCountByExample(e) == inFilterValues.size();
String columnName = this.safeMapToColumnName(inFilterField);
QueryWrapper<M> queryWrapper = new QueryWrapper<>();
queryWrapper.in(columnName, inFilterValues);
return mapper().selectCount(queryWrapper) == inFilterValues.size();
}
/**

View File

@@ -1,9 +1,15 @@
package com.orange.demo.common.core.base.service;
import com.baomidou.mybatisplus.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.orange.demo.common.core.annotation.*;
import com.orange.demo.common.core.base.dao.BaseDaoMapper;
import com.orange.demo.common.core.constant.AggregationType;
import com.orange.demo.common.core.constant.GlobalDeletedFlag;
import com.orange.demo.common.core.exception.InvalidDataFieldException;
import com.orange.demo.common.core.exception.MyRuntimeException;
import com.orange.demo.common.core.object.*;
import com.orange.demo.common.core.util.AopTargetUtil;
@@ -14,13 +20,9 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
import cn.hutool.core.util.ReflectUtil;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -39,7 +41,7 @@ import static java.util.stream.Collectors.*;
* @date 2020-09-24
*/
@Slf4j
public abstract class BaseService<M, K> implements IBaseService<M, K> {
public abstract class BaseService<M, K extends Serializable> extends ServiceImpl<BaseDaoMapper<M>, M> implements IBaseService<M, K> {
/**
* 当前Service关联的主Model实体对象的Class。
*/
@@ -133,6 +135,11 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
private static final String AGGREGATED_VALUE = "aggregatedValue";
private static final String AND_OP = " AND ";
@Override
public BaseDaoMapper<M> getBaseMapper() {
return mapper();
}
/**
* 构造函数在实例化的时候一次性完成所有有关主Model对象信息的加载。
*/
@@ -140,7 +147,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
public BaseService() {
modelClass = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
idFieldClass = (Class<K>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
this.tableName = modelClass.getAnnotation(Table.class).name();
this.tableName = modelClass.getAnnotation(TableName.class).value();
Field[] fields = ReflectUtil.getFields(modelClass);
for (Field field : fields) {
initializeField(field);
@@ -148,10 +155,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
}
private void initializeField(Field field) {
if (idFieldName == null && null != field.getAnnotation(Id.class)) {
if (idFieldName == null && null != field.getAnnotation(TableId.class)) {
idFieldName = field.getName();
Column c = field.getAnnotation(Column.class);
idColumnName = c == null ? idFieldName : c.name();
TableId c = field.getAnnotation(TableId.class);
idColumnName = c == null ? idFieldName : c.value();
setIdFieldMethod = ReflectUtil.getMethod(
modelClass, "set" + StringUtils.capitalize(idFieldName), idFieldClass);
getIdFieldMethod = ReflectUtil.getMethod(
@@ -159,21 +166,18 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
}
if (updateTimeFieldName == null && null != field.getAnnotation(JobUpdateTimeColumn.class)) {
updateTimeFieldName = field.getName();
Column c = field.getAnnotation(Column.class);
updateTimeColumnName = c == null ? updateTimeFieldName : c.name();
updateTimeColumnName = this.safeMapToColumnName(updateTimeFieldName);
}
if (deletedFlagFieldName == null && null != field.getAnnotation(DeletedFlagColumn.class)) {
if (deletedFlagFieldName == null && null != field.getAnnotation(TableLogic.class)) {
deletedFlagFieldName = field.getName();
Column c = field.getAnnotation(Column.class);
deletedFlagColumnName = c == null ? deletedFlagFieldName : c.name();
deletedFlagColumnName = this.safeMapToColumnName(deletedFlagFieldName);
setDeletedFlagMethod = ReflectUtil.getMethod(
modelClass, "set" + StringUtils.capitalize(deletedFlagFieldName), Integer.class);
}
if (tenantIdFieldName == null && null != field.getAnnotation(TenantFilterColumn.class)) {
tenantIdField = field;
tenantIdFieldName = field.getName();
Column c = field.getAnnotation(Column.class);
tenantIdColumnName = c == null ? tenantIdFieldName : c.name();
tenantIdColumnName = this.safeMapToColumnName(tenantIdFieldName);
}
}
@@ -185,28 +189,15 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
protected abstract BaseDaoMapper<M> mapper();
/**
* 基于主键Id删除数据。如果包含逻辑删除字段则进行逻辑删除
* 根据过滤条件删除数据
*
* @param id 主键Id值
* @return true删除成功false数据不存在
* @param filter 过滤对象
* @return 删除数量
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean removeById(K id) {
if (this.deletedFlagFieldName == null) {
return mapper().deleteByPrimaryKey(id) == 1;
}
try {
Example e = new Example(modelClass);
Example.Criteria c = e.createCriteria().andEqualTo(idFieldName, id);
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
M data = modelClass.newInstance();
setDeletedFlagMethod.invoke(data, GlobalDeletedFlag.DELETED);
return mapper().updateByExampleSelective(data, e) == 1;
} catch (Exception ex) {
log.error("Failed to call reflection method in BaseService.removeById.", ex);
throw new MyRuntimeException(ex);
}
public Integer removeBy(M filter) {
return mapper().delete(new QueryWrapper<>(filter));
}
/**
@@ -223,9 +214,8 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
if (fieldName.equals(this.idFieldName)) {
return this.existId((K) fieldValue);
}
Example e = new Example(modelClass);
e.createCriteria().andEqualTo(fieldName, fieldValue);
return mapper().selectCountByExample(e) == 1;
String columnName = MyModelUtil.mapToColumnName(fieldName, modelClass);
return mapper().selectCount(new QueryWrapper<M>().eq(columnName, fieldValue)) == 1;
}
/**
@@ -239,24 +229,6 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
return getById(id) != null;
}
/**
* 获取主键Id关联的数据。
*
* @param id 主键Id。
* @return 主键关联的数据不存在返回null。
*/
@Override
public M getById(K id) {
if (deletedFlagFieldName == null) {
return mapper().selectByPrimaryKey(id);
}
Example e = new Example(modelClass);
e.createCriteria()
.andEqualTo(this.idFieldName, id)
.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
return mapper().selectOneByExample(e);
}
/**
* 返回符合 filterField = filterValue 条件的一条数据。
*
@@ -270,12 +242,9 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
if (filterField.equals(idFieldName)) {
return this.getById((K) filterValue);
}
Example e = new Example(modelClass);
Example.Criteria c = e.createCriteria().andEqualTo(filterField, filterValue);
if (deletedFlagFieldName != null) {
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
}
return mapper().selectOneByExample(e);
String columnName = this.safeMapToColumnName(filterField);
QueryWrapper<M> queryWrapper = new QueryWrapper<M>().eq(columnName, filterValue);
return mapper().selectOne(queryWrapper);
}
/**
@@ -299,12 +268,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public List<M> getAllList() {
if (deletedFlagFieldName == null) {
return mapper().selectAll();
}
Example e = new Example(modelClass);
e.createCriteria().andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
return mapper().selectByExample(e);
return mapper().selectList(Wrappers.emptyWrapper());
}
/**
@@ -315,14 +279,11 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public List<M> getAllListByOrder(String... orderByProperties) {
Example e = new Example(modelClass);
for (String orderByProperty : orderByProperties) {
e.orderBy(orderByProperty);
String[] columns = new String[orderByProperties.length];
for (int i = 0; i < orderByProperties.length; i++) {
columns[i] = this.safeMapToColumnName(orderByProperties[i]);
}
if (deletedFlagFieldName != null) {
e.and().andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
}
return mapper().selectByExample(e);
return mapper().selectList(new QueryWrapper<M>().orderByAsc(columns));
}
/**
@@ -351,8 +312,8 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
if (CollectionUtils.isEmpty(inFilterValues)) {
return true;
}
Example e = this.makeDefaultInListExample(inFilterField, inFilterValues, null);
return mapper().selectCountByExample(e) == inFilterValues.size();
String column = this.safeMapToColumnName(inFilterField);
return mapper().selectCount(new QueryWrapper<M>().in(column, inFilterValues)) == inFilterValues.size();
}
/**
@@ -391,8 +352,12 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
if (CollectionUtils.isEmpty(inFilterValues)) {
return new LinkedList<>();
}
Example e = this.makeDefaultInListExample(inFilterField, inFilterValues, orderBy);
return mapper().selectByExample(e);
String column = this.safeMapToColumnName(inFilterField);
QueryWrapper<M> queryWrapper = new QueryWrapper<M>().in(column, inFilterValues);
if (StringUtils.isNotBlank(orderBy)) {
queryWrapper.last(orderBy);
}
return mapper().selectList(queryWrapper);
}
/**
@@ -403,16 +368,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public int getCountByFilter(M filter) {
if (deletedFlagFieldName == null) {
return mapper().selectCount(filter);
}
try {
setDeletedFlagMethod.invoke(filter, GlobalDeletedFlag.NORMAL);
return mapper().selectCount(filter);
} catch (Exception e) {
log.error("Failed to call reflection [setDeletedFlagMethod] in BaseService.getCountByFilter.", e);
throw new MyRuntimeException(e);
}
return mapper().selectCount(new QueryWrapper<>(filter));
}
/**
@@ -434,42 +390,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public List<M> getListByFilter(M filter) {
if (filter == null) {
return this.getAllList();
}
if (deletedFlagFieldName == null) {
return mapper().select(filter);
}
try {
setDeletedFlagMethod.invoke(filter, GlobalDeletedFlag.NORMAL);
return mapper().select(filter);
} catch (Exception ex) {
log.error("Failed to call reflection code of BaseService.getListByFilter.", ex);
throw new MyRuntimeException(ex);
}
}
private void assembleCriteriaByFilter(M filter, Field field, Example.Criteria c) {
int modifiers = field.getModifiers();
// transient类型的字段不能作为查询条件
int transientMask = 128;
if ((modifiers & transientMask) != 0 || Modifier.isStatic(modifiers)) {
return;
}
if (field.getName().equals(deletedFlagFieldName)) {
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
} else {
ReflectUtil.setAccessible(field);
try {
Object o = field.get(filter);
if (o != null) {
c.andEqualTo(field.getName(), field.get(filter));
}
} catch (IllegalAccessException ex) {
log.error("Failed to call reflection code of BaseService.getListByFilter.", ex);
throw new MyRuntimeException(ex);
}
}
return mapper().selectList(new QueryWrapper<>(filter));
}
/**
@@ -481,17 +402,14 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public List<M> getListByParentId(String parentIdFieldName, K parentId) {
Example e = new Example(modelClass);
Example.Criteria c = e.createCriteria();
QueryWrapper<M> queryWrapper = new QueryWrapper<>();
String parentIdColumn = this.safeMapToColumnName(parentIdFieldName);
if (parentId != null) {
c.andEqualTo(parentIdFieldName, parentId);
queryWrapper.eq(parentIdColumn, parentId);
} else {
c.andIsNull(parentIdFieldName);
queryWrapper.isNull(parentIdColumn);
}
if (deletedFlagFieldName != null) {
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
}
return mapper().selectByExample(e);
return mapper().selectList(queryWrapper);
}
/**
@@ -521,32 +439,21 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public List<M> getListByCondition(List<String> selectList, M filter, String whereClause, String orderBy) {
Example e = new Example(modelClass);
Example.Criteria c = null;
QueryWrapper<M> queryWrapper = new QueryWrapper<>(filter);
if (CollectionUtils.isNotEmpty(selectList)) {
String[] selectFields = new String[selectList.size()];
selectList.toArray(selectFields);
e.selectProperties(selectFields);
}
if (StringUtils.isNotBlank(orderBy)) {
e.setOrderByClause(orderBy);
}
if (filter != null) {
c = e.createCriteria();
Field[] fields = ReflectUtil.getFields(modelClass);
for (Field field : fields) {
if (field.getAnnotation(Transient.class) == null) {
this.assembleCriteriaByFilter(filter, field, c);
}
String[] columns = new String[selectList.size()];
for (int i = 0; i < selectList.size(); i++) {
columns[i] = this.safeMapToColumnName(selectList.get(i));
}
queryWrapper.select(columns);
}
if (StringUtils.isNotBlank(whereClause)) {
if (c == null) {
c = e.createCriteria();
}
c.andCondition(whereClause);
queryWrapper.apply(whereClause);
}
return mapper().selectByExample(e);
if (StringUtils.isNotBlank(orderBy)) {
queryWrapper.last(" ORDER BY " + orderBy);
}
return mapper().selectList(queryWrapper);
}
/**
@@ -753,9 +660,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
continue;
}
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
Example e = new Example(relationStruct.relationManyToMany.relationModelClass());
e.createCriteria().andEqualTo(relationStruct.masterIdField.getName(), masterIdValue);
List<?> manyToManyList = relationStruct.manyToManyMapper.selectByExample(e);
String masterIdColumn = this.safeMapToColumnName(relationStruct.masterIdField.getName());
Map<String, Object> filterMap = new HashMap<>(1);
filterMap.put(masterIdColumn, masterIdValue);
List<?> manyToManyList = relationStruct.manyToManyMapper.selectByMap(filterMap);
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, manyToManyList);
}
}
@@ -904,7 +812,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
.collect(toSet());
// 从主表集合中抽取主表关联字段的集合再以in list形式去从表中查询。
if (CollectionUtils.isNotEmpty(masterIdSet)) {
BaseService<Object, Object> relationService = relationStruct.service;
BaseService<Object, Serializable> relationService = relationStruct.service;
List<Object> relationList =
relationService.getInList(relationStruct.relationOneToOne.slaveIdField(), masterIdSet);
MyModelUtil.makeOneToOneRelation(
@@ -913,8 +821,8 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
if (withDict && relationStruct.relationOneToOne.loadSlaveDict()
&& CollectionUtils.isNotEmpty(relationList)) {
@SuppressWarnings("unchecked")
BaseService<Object, Object> proxyTarget =
(BaseService<Object, Object>) AopTargetUtil.getTarget(relationService);
BaseService<Object, Serializable> proxyTarget =
(BaseService<Object, Serializable>) AopTargetUtil.getTarget(relationService);
// 关联本地字典。
proxyTarget.buildDictForDataList(relationList, false, ignoreFields);
// 关联常量字典
@@ -941,14 +849,14 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
}
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (id != null) {
BaseService<Object, Object> relationService = relationStruct.service;
BaseService<Object, Serializable> relationService = relationStruct.service;
Object relationObject = relationService.getOne(relationStruct.relationOneToOne.slaveIdField(), id);
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, relationObject);
// 仅仅当需要加载从表字典关联时,才去加载。
if (withDict && relationStruct.relationOneToOne.loadSlaveDict() && relationObject != null) {
@SuppressWarnings("unchecked")
BaseService<Object, Object> proxyTarget =
(BaseService<Object, Object>) AopTargetUtil.getTarget(relationService);
BaseService<Object, Serializable> proxyTarget =
(BaseService<Object, Serializable>) AopTargetUtil.getTarget(relationService);
// 关联本地字典
proxyTarget.buildDictForData(relationObject, false, ignoreFields);
// 关联常量字典
@@ -978,7 +886,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
.collect(toSet());
// 从主表集合中抽取主表关联字段的集合再以in list形式去从表中查询。
if (CollectionUtils.isNotEmpty(masterIdSet)) {
BaseService<Object, Object> relationService = relationStruct.service;
BaseService<Object, Serializable> relationService = relationStruct.service;
List<Object> relationList =
relationService.getInList(relationStruct.relationOneToMany.slaveIdField(), masterIdSet);
MyModelUtil.makeOneToManyRelation(
@@ -1003,7 +911,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
}
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (id != null) {
BaseService<Object, Object> relationService = relationStruct.service;
BaseService<Object, Serializable> relationService = relationStruct.service;
Set<Object> masterIdSet = new HashSet<>(1);
masterIdSet.add(id);
List<Object> relationObject = relationService.getInList(
@@ -1289,32 +1197,97 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
}
/**
* 通过(In-list)条件和orderBy条件构建Example对象以供后续的查询操作使用。
* 因为Mybatis Plus中QueryWrapper的条件方法都要求传入数据表字段名因此提供该函数将
* Java实体对象的字段名转换为数据表字段名如果不存在会抛出异常。
* 另外在MyModelUtil.mapToColumnName有一级缓存对于查询过的对象字段都会放到缓存中
* 下次映射转换的时候,会直接从缓存获取。
*
* @param inFilterField 参与(In-list)过滤的Java字段。
* @param inFilterValues 参与(In-list)过滤的Java字段值集合
* @param orderBy 排序字段。
* @param <T> in 属性字段的类型。
* @return 构建后的Example对象。
* @param fieldName Java实体对象的字段
* @return 对应的数据表字段名
*/
protected <T> Example makeDefaultInListExample(String inFilterField, Collection<T> inFilterValues, String orderBy) {
Set<T> inFilterValueSet;
Example e = new Example(modelClass);
if (StringUtils.isNotBlank(orderBy)) {
e.setOrderByClause(orderBy);
protected String safeMapToColumnName(String fieldName) {
String columnName = MyModelUtil.mapToColumnName(fieldName, modelClass);
if (columnName == null) {
throw new InvalidDataFieldException(modelClass.getSimpleName(), fieldName);
}
if (inFilterValues instanceof Set) {
inFilterValueSet = (Set<T>) inFilterValues;
} else {
inFilterValueSet = new HashSet<>(inFilterValues.size());
inFilterValueSet.addAll(inFilterValues);
return columnName;
}
/**
* 因为Mybatis Plus在update的时候不能将实体对象中值为null的字段更新为null
* 而且忽略更新,在全部更新场景下,这个是非常重要的,所以我们写了这个函数绕开这一问题。
* 该函数会遍历实体对象中,所有不包含@Transient注解没有transient修饰符的字段如果
* 当前对象的该字段值为null则会调用UpdateWrapper的set方法将该字段赋值为null。
* 相比于其他重载方法该方法会将参数中的主键id设置到UpdateWrapper的过滤条件中。
*
* @param o 实体对象。
* @param id 实体对象的主键值。
* @return 创建后的UpdateWrapper。
*/
protected UpdateWrapper<M> createUpdateQueryForNullValue(M o, K id) {
UpdateWrapper<M> uw = createUpdateQueryForNullValue(o, modelClass);
try {
M filter = modelClass.newInstance();
this.setIdFieldMethod.invoke(filter, id);
uw.setEntity(filter);
} catch (Exception e) {
log.error("Failed to call reflection code of BaseService.createUpdateQueryForNullValue.", e);
throw new MyRuntimeException(e);
}
Example.Criteria c = e.createCriteria();
c.andIn(inFilterField, inFilterValueSet);
if (deletedFlagFieldName != null) {
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
return uw;
}
/**
* 因为Mybatis Plus在update的时候不能将实体对象中值为null的字段更新为null
* 而且忽略更新,在全部更新场景下,这个是非常重要的,所以我们写了这个函数绕开这一问题。
* 该函数会遍历实体对象中,所有不包含@Transient注解没有transient修饰符的字段如果
* 当前对象的该字段值为null则会调用UpdateWrapper的set方法将该字段赋值为null。
*
* @param o 实体对象。
* @return 创建后的UpdateWrapper。
*/
protected UpdateWrapper<M> createUpdateQueryForNullValue(M o) {
return createUpdateQueryForNullValue(o, modelClass);
}
/**
* 因为Mybatis Plus在update的时候不能将实体对象中值为null的字段更新为null
* 而且忽略更新,在全部更新场景下,这个是非常重要的,所以我们写了这个函数绕开这一问题。
* 该函数会遍历实体对象中,所有不包含@Transient注解没有transient修饰符的字段如果
* 当前对象的该字段值为null则会调用UpdateWrapper的set方法将该字段赋值为null。
*
* @param o 实体对象。
* @param clazz 实体对象的class。
* @return 创建后的UpdateWrapper。
*/
public static <T> UpdateWrapper<T> createUpdateQueryForNullValue(T o, Class<T> clazz) {
UpdateWrapper<T> uw = new UpdateWrapper<>();
Field[] fields = ReflectUtil.getFields(clazz);
List<String> nullColumnList = new LinkedList<>();
for (Field field : fields) {
TableField tableField = field.getAnnotation(TableField.class);
if (tableField == null || tableField.exist()) {
int modifiers = field.getModifiers();
// transient类型的字段不能作为查询条件静态字段和逻辑删除都不考虑。
int transientMask = 128;
if ((modifiers & transientMask) == 1
|| Modifier.isStatic(modifiers)
|| field.getAnnotation(TableLogic.class) != null) {
continue;
}
// 仅当实体对象参数中当前字段值为null的时候才会赋值给UpdateWrapper。
// 以便在后续的更新中可以将这些null字段的值设置到数据库表对应的字段中。
if (ReflectUtil.getFieldValue(o, field) == null) {
nullColumnList.add(MyModelUtil.safeMapToColumnName(field.getName(), clazz));
}
}
}
return e;
if (CollectionUtils.isNotEmpty(nullColumnList)) {
for (String nullColumn : nullColumnList) {
uw.set(nullColumn, null);
}
}
return uw;
}
@SuppressWarnings("unchecked")
@@ -1329,7 +1302,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
relationStruct.service = ApplicationContextHolder.getBean(
StringUtils.uncapitalize(relationOneToOne.slaveServiceName()));
} else {
relationStruct.service = (BaseService<Object, Object>)
relationStruct.service = (BaseService<Object, Serializable>)
ApplicationContextHolder.getBean(relationOneToOne.slaveServiceClass());
}
relationOneToOneStructList.add(relationStruct);
@@ -1345,7 +1318,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
relationStruct.service = ApplicationContextHolder.getBean(
StringUtils.uncapitalize(relationOneToMany.slaveServiceName()));
} else {
relationStruct.service = (BaseService<Object, Object>)
relationStruct.service = (BaseService<Object, Serializable>)
ApplicationContextHolder.getBean(relationOneToMany.slaveServiceClass());
}
relationOneToManyStructList.add(relationStruct);
@@ -1375,7 +1348,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
relationStruct.service = ApplicationContextHolder.getBean(
StringUtils.uncapitalize(relationOneToManyAggregation.slaveServiceName()));
} else {
relationStruct.service = (BaseService<Object, Object>)
relationStruct.service = (BaseService<Object, Serializable>)
ApplicationContextHolder.getBean(relationOneToManyAggregation.slaveServiceClass());
}
relationOneToManyAggrStructList.add(relationStruct);
@@ -1391,7 +1364,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
relationStruct.service = ApplicationContextHolder.getBean(
StringUtils.uncapitalize(relationManyToManyAggregation.slaveServiceName()));
} else {
relationStruct.service = (BaseService<Object, Object>)
relationStruct.service = (BaseService<Object, Serializable>)
ApplicationContextHolder.getBean(relationManyToManyAggregation.slaveServiceClass());
}
relationManyToManyAggrStructList.add(relationStruct);
@@ -1424,7 +1397,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
relationStruct.service = ApplicationContextHolder.getBean(
StringUtils.uncapitalize(relationDict.slaveServiceName()));
} else {
relationStruct.service = (BaseService<Object, Object>)
relationStruct.service = (BaseService<Object, Serializable>)
ApplicationContextHolder.getBean(relationDict.slaveServiceClass());
}
relationDictStructList.add(relationStruct);
@@ -1621,7 +1594,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
private Field relationField;
private Field masterIdField;
private Field equalOneToOneRelationField;
private BaseService<Object, Object> service;
private BaseService<Object, Serializable> service;
private BaseDaoMapper<Object> manyToManyMapper;
private Map<Object, String> dictMap;
private RelationDict relationDict;

View File

@@ -1,5 +1,6 @@
package com.orange.demo.common.core.base.service;
import java.io.Serializable;
import java.util.List;
/**
@@ -10,7 +11,7 @@ import java.util.List;
* @author Jerry
* @date 2020-09-24
*/
public interface IBaseDictService<M, K> extends IBaseService<M, K> {
public interface IBaseDictService<M, K extends Serializable> extends IBaseService<M, K> {
/**
* 重新加载数据库中所有当前表数据到系统内存。

View File

@@ -1,7 +1,9 @@
package com.orange.demo.common.core.base.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.orange.demo.common.core.object.MyRelationParam;
import java.io.Serializable;
import java.util.*;
/**
@@ -12,15 +14,15 @@ import java.util.*;
* @author Jerry
* @date 2020-09-24
*/
public interface IBaseService<M, K> {
public interface IBaseService<M, K extends Serializable> extends IService<M>{
/**
* 基于主键Id删除数据。如果包含逻辑删除字段则进行逻辑删除
* 根据过滤条件删除数据
*
* @param id 主键Id值
* @return true删除成功false数据不存在
* @param filter 过滤对象
* @return 删除数量
*/
boolean removeById(K id);
Integer removeBy(M filter);
/**
* 判断指定字段的数据是否存在,且仅仅存在一条记录。
@@ -40,14 +42,6 @@ public interface IBaseService<M, K> {
*/
boolean existId(K id);
/**
* 获取主键Id关联的数据。
*
* @param id 主键Id。
* @return 主键关联的数据不存在返回null。
*/
M getById(K id);
/**
* 返回符合 filterField = filterValue 条件的一条数据。
*

View File

@@ -11,15 +11,20 @@ public class DataSourceContextHolder {
private static final ThreadLocal<Integer> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源类型
* 设置数据源类型
*
* @param type 数据源类型
* @return 原有数据源类型如果第一次设置则返回null。
*/
public static void setDataSourceType(Integer type) {
public static Integer setDataSourceType(Integer type) {
Integer datasourceType = CONTEXT_HOLDER.get();
CONTEXT_HOLDER.set(type);
return datasourceType;
}
/**
* 获取当前数据库操作执行线程的数据源类型,同时由动态数据源的路由函数调用。
*
* @return 数据源类型。
*/
public static Integer getDataSourceType() {
@@ -27,10 +32,16 @@ public class DataSourceContextHolder {
}
/**
* 清除线程本地变量,以免内存泄漏
* 清除线程本地变量,以免内存泄漏
* @param originalType 原有的数据源类型如果该值为null则情况本地化变量。
*/
public static void clear() {
CONTEXT_HOLDER.remove();
public static void unset(Integer originalType) {
if (originalType == null) {
CONTEXT_HOLDER.remove();
} else {
CONTEXT_HOLDER.set(originalType);
}
}
/**

View File

@@ -34,11 +34,11 @@ public final class AggregationType {
private static final Map<Object, String> DICT_MAP = new HashMap<>(5);
static {
DICT_MAP.put(0, "累计总和");
DICT_MAP.put(1, "数量总和");
DICT_MAP.put(2, "平均值");
DICT_MAP.put(3, "最小值");
DICT_MAP.put(4, "最大值");
DICT_MAP.put(SUM, "累计总和");
DICT_MAP.put(COUNT, "数量总和");
DICT_MAP.put(AVG, "平均值");
DICT_MAP.put(MIN, "最小值");
DICT_MAP.put(MAX, "最大值");
}
/**

View File

@@ -6,8 +6,10 @@ import com.orange.demo.common.core.exception.InvalidClassFieldException;
import com.orange.demo.common.core.exception.InvalidDataFieldException;
import com.orange.demo.common.core.exception.InvalidDataModelException;
import com.orange.demo.common.core.util.MyModelUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -227,6 +229,8 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
/**
* 排序信息对象。
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class OrderInfo {
/**

View File

@@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import java.util.Collection;
import java.util.Date;
import java.util.List;
/**
@@ -21,7 +22,6 @@ import java.util.List;
@Slf4j
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyWhereCriteria {
/**
@@ -80,11 +80,26 @@ public class MyWhereCriteria {
@JSONField(serialize = false)
private Class<?> modelClazz;
/**
* 数据库表名。
*/
private String tableName;
/**
* Java属性名称。
*/
private String fieldName;
/**
* 数据表字段名。
*/
private String columnName;
/**
* 数据表字段类型。
*/
private Integer columnType;
/**
* 操作符类型,取值范围见上面的常量值。
*/
@@ -95,6 +110,13 @@ public class MyWhereCriteria {
*/
private Object value;
public MyWhereCriteria(Class<?> modelClazz, String fieldName, Integer operatorType, Object value) {
this.modelClazz = modelClazz;
this.fieldName = fieldName;
this.operatorType = operatorType;
this.value = value;
}
/**
* 设置条件值。
*
@@ -127,6 +149,29 @@ public class MyWhereCriteria {
return doVerify();
}
/**
* 设置条件值通过该构造方法设置时通常是直接将表名、字段名、字段类型等赋值无需在通过modelClazz进行推演。
*
* @param tableName 数据表名。
* @param columnName 数据字段名。
* @param columnType 数据字段类型。
* @param operatorType 操作类型。具体值可参考当前对象的静态变量。
* @param value 条件过滤值。
*/
public void setCriteria(
String tableName, String columnName, String columnType, Integer operatorType, Object value) {
this.tableName = tableName;
this.columnName = columnName;
this.columnType = MyModelUtil.NUMERIC_FIELD_TYPE;
if (String.class.getSimpleName().equals(columnType)) {
this.columnType = MyModelUtil.STRING_FIELD_TYPE;
} else if (Date.class.getSimpleName().equals(columnType)) {
this.columnType = MyModelUtil.DATE_FIELD_TYPE;
}
this.operatorType = operatorType;
this.value = value;
}
/**
* 在执行该函数之前,该对象的所有数据均已经赋值完毕。
* 该函数主要验证操作符字段和条件值字段对应关系的合法性。
@@ -182,13 +227,13 @@ public class MyWhereCriteria {
case OPERATOR_NOT_EQUAL:
return " != ";
case OPERATOR_GE:
return " &gt;= ";
return " >= ";
case OPERATOR_GT:
return " &gt; ";
return " > ";
case OPERATOR_LE:
return " &lt;= ";
return " <= ";
case OPERATOR_LT:
return " &lt; ";
return " < ";
case OPERATOR_LIKE:
return " LIKE ";
case OPERATOR_NOT_NULL:
@@ -222,18 +267,26 @@ public class MyWhereCriteria {
* @return 组装后的SQL条件从句。
*/
public String makeCriteriaString(Class<?> modelClazz) {
if (modelClazz == null) {
throw new IllegalArgumentException("ModelClazz argument can't be NULL.");
String tableName;
String columnName;
Integer columnType;
if (modelClazz != null) {
Tuple2<String, Integer> fieldInfo = MyModelUtil.mapToColumnInfo(fieldName, modelClazz);
if (fieldInfo == null) {
throw new InvalidDataFieldException(modelClazz.getSimpleName(), fieldName);
}
columnName = fieldInfo.getFirst();
columnType = fieldInfo.getSecond();
tableName = MyModelUtil.mapToTableName(modelClazz);
if (tableName == null) {
throw new InvalidDataModelException(modelClazz.getSimpleName());
}
} else {
tableName = this.tableName;
columnName = this.columnName;
columnType = this.columnType;
}
Tuple2<String, Integer> fieldInfo = MyModelUtil.mapToColumnInfo(fieldName, modelClazz);
if (fieldInfo == null) {
throw new InvalidDataFieldException(modelClazz.getSimpleName(), fieldName);
}
String tableName = MyModelUtil.mapToTableName(modelClazz);
if (tableName == null) {
throw new InvalidDataModelException(modelClazz.getSimpleName());
}
return this.buildClauseString(tableName, fieldInfo.getFirst(), fieldInfo.getSecond());
return this.buildClauseString(tableName, columnName, columnType);
}
/**

View File

@@ -135,7 +135,7 @@ public class ResponseResult<T> {
}
/**
* 根据参数的errorCode和errorMessage创建新的错误应答对象。
* 根据参数中出错的ResponseResult创建新的错误应答对象。
*
* @param errorCause 导致错误原因的应答对象。
* @return 返回创建的ResponseResult实例对象。
@@ -144,6 +144,16 @@ public class ResponseResult<T> {
return error(errorCause.errorCode, errorCause.getErrorMessage());
}
/**
* 根据参数中出错的CallResult创建新的错误应答对象。
*
* @param errorCause 导致错误原因的应答对象。
* @return 返回创建的ResponseResult实例对象。
*/
public static <T> ResponseResult<T> errorFrom(CallResult errorCause) {
return error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorCause.getErrorMessage());
}
/**
* 是否成功。
*

View File

@@ -56,6 +56,10 @@ public class TokenData {
* 仅当系统支持uaa时可用否则可以直接忽略该字段。保留该字段是为了保持单体和微服务通用代码部分的兼容性。
*/
private String uaaAccessToken;
/**
* 数据库路由键(仅当水平分库时使用)。
*/
private Integer datasourceRouteKey;
/**
* 登录IP。
*/

View File

@@ -2,22 +2,19 @@ package com.orange.demo.common.core.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.*;
import com.orange.demo.common.core.exception.InvalidDataFieldException;
import com.orange.demo.common.core.annotation.*;
import com.orange.demo.common.core.exception.MyRuntimeException;
import com.orange.demo.common.core.object.TokenData;
import com.orange.demo.common.core.object.Tuple2;
import com.orange.demo.common.core.upload.UploadStoreInfo;
import com.google.common.base.CaseFormat;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import tk.mybatis.mapper.entity.Example;
import javax.persistence.Column;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.lang.reflect.Modifier;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -66,6 +63,24 @@ public class MyModelUtil {
*/
private static final Map<String, Tuple2<String, Integer>> CACHED_COLUMNINFO_MAP = new ConcurrentHashMap<>();
/**
* 将bean的数据列表转换为Map列表。
*
* @param dataList bean数据列表。
* @param <T> bean对象类型。
* @return 转换后的Map列表。
*/
public static <T> List<Map<String, Object>> beanToMapList(List<T> dataList) {
if (CollectionUtils.isEmpty(dataList)) {
return null;
}
List<Map<String, Object>> resultList = new LinkedList<>();
for (T data : dataList) {
resultList.add(BeanUtil.beanToMap(data));
}
return resultList;
}
/**
* 拷贝源类型的集合数据到目标类型的集合中,其中源类型和目标类型中的对象字段类型完全相同。
* NOTE: 该函数主要应用于框架中Dto和Model之间的copy特别针对一对一关联的深度copy。
@@ -189,8 +204,20 @@ public class MyModelUtil {
if (field == null) {
return null;
}
Column c = field.getAnnotation(Column.class);
String columnName = c == null ? fieldName : c.name();
TableField c = field.getAnnotation(TableField.class);
String columnName = null;
if (c == null) {
TableId id = field.getAnnotation(TableId.class);
if (id != null) {
columnName = id.value();
}
}
if (columnName == null) {
columnName = c == null ? CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName) : c.value();
if (StringUtils.isBlank(columnName)) {
columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName);
}
}
// 这里缺省情况下都是按照整型去处理,因为他覆盖太多的类型了。
// 如Integer/Long/Double/BigDecimal可根据实际情况完善和扩充。
String typeName = field.getType().getSimpleName();
@@ -213,8 +240,8 @@ public class MyModelUtil {
* @return Model对象对应的数据表名称。
*/
public static String mapToTableName(Class<?> modelClazz) {
Table t = modelClazz.getAnnotation(Table.class);
return t == null ? null : t.name();
TableName t = modelClazz.getAnnotation(TableName.class);
return t == null ? null : t.value();
}
/**
@@ -574,43 +601,6 @@ public class MyModelUtil {
return isMap ? BeanUtil.beanToMap(model) : model;
}
/**
* 转换过滤对象到与其等效的Example对象。
*
* @param filterModel 过滤对象。
* @param modelClass 过滤对象的Class对象。
* @param <T> 过滤对象类型。
* @return 转换后的Example对象。
*/
public static <T> Example convertFilterModelToExample(T filterModel, Class<T> modelClass) {
if (filterModel == null) {
return null;
}
Example e = new Example(modelClass);
Example.Criteria c = e.createCriteria();
Field[] fields = ReflectUtil.getFields(modelClass);
for (Field field : fields) {
if (field.getAnnotation(Transient.class) == null) {
int modifiers = field.getModifiers();
// transient类型的字段不能作为查询条件
if ((modifiers & 128) != 0 || Modifier.isStatic(modifiers)) {
continue;
}
ReflectUtil.setAccessible(field);
try {
Object o = field.get(filterModel);
if (o != null) {
c.andEqualTo(field.getName(), field.get(filterModel));
}
} catch (IllegalAccessException ex) {
log.error("Failed to call reflection code.", ex);
throw new MyRuntimeException(ex);
}
}
}
return e;
}
/**
* 获取上传字段的存储信息。
*

View File

@@ -39,6 +39,9 @@ public class RedissonConfig {
@Value("${redis.redisson.timeout}")
private Integer timeout;
@Value("${redis.redisson.password:}")
private String password;
@Value("${redis.redisson.pool.poolSize}")
private Integer poolSize;
@@ -47,10 +50,14 @@ public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
if (StrUtil.isBlank(password)) {
password = null;
}
Config config = new Config();
if ("single".equals(mode)) {
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useSingleServer()
.setPassword(password)
.setAddress(address)
.setConnectionPoolSize(poolSize)
.setConnectionMinimumIdleSize(minIdle)
@@ -59,6 +66,7 @@ public class RedissonConfig {
String[] clusterAddresses = StrUtil.splitToArray(address, ',');
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useClusterServers()
.setPassword(password)
.addNodeAddress(clusterAddresses)
.setConnectTimeout(timeout)
.setMasterConnectionPoolSize(poolSize);
@@ -66,6 +74,7 @@ public class RedissonConfig {
String[] sentinelAddresses = StrUtil.splitToArray(address, ',');
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useSentinelServers()
.setPassword(password)
.setMasterName(masterName)
.addSentinelAddress(sentinelAddresses)
.setConnectTimeout(timeout)
@@ -80,6 +89,7 @@ public class RedissonConfig {
ArrayUtil.copy(masterSlaveAddresses, 1, slaveAddresses, 0, slaveAddresses.length);
config.setLockWatchdogTimeout(lockWatchdogTimeout)
.useMasterSlaveServers()
.setPassword(password)
.setMasterAddress(masterSlaveAddresses[0])
.addSlaveAddress(slaveAddresses)
.setConnectTimeout(timeout)

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common</artifactId>
<groupId>com.orange.demo</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>common-swagger</artifactId>
<version>1.0.0</version>
<name>common-swagger</name>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-metadata</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.orange.demo</groupId>
<artifactId>common-core</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,56 @@
package com.orange.demo.common.swagger.config;
import com.orange.demo.common.core.annotation.MyRequestBody;
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
/**
* 自动加载bean的配置对象。
*
* @author Jerry
* @date 2020-09-24
*/
@EnableSwagger2WebMvc
@EnableKnife4j
@EnableConfigurationProperties(SwaggerProperties.class)
@ConditionalOnProperty(prefix = "swagger", name = "enabled")
public class SwaggerAutoConfiguration {
@Bean
public Docket upmsDocket(SwaggerProperties properties) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("1. 用户权限分组接口")
.ignoredParameterTypes(MyRequestBody.class)
.apiInfo(apiInfo(properties))
.select()
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage() + ".upms.controller"))
.paths(PathSelectors.any()).build();
}
@Bean
public Docket bizDocket(SwaggerProperties properties) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("2. 业务应用分组接口")
.ignoredParameterTypes(MyRequestBody.class)
.apiInfo(apiInfo(properties))
.select()
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage() + ".app.controller"))
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(SwaggerProperties properties) {
return new ApiInfoBuilder()
.title(properties.getTitle())
.description(properties.getDescription())
.version(properties.getVersion()).build();
}
}

View File

@@ -0,0 +1,40 @@
package com.orange.demo.common.swagger.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 配置参数对象。
*
* @author Jerry
* @date 2020-09-24
*/
@Data
@ConfigurationProperties("swagger")
public class SwaggerProperties {
/**
* 是否开启Swagger。
*/
private Boolean enabled;
/**
* Swagger解析的基础包路径。
**/
private String basePackage = "";
/**
* ApiInfo中的标题。
**/
private String title = "";
/**
* ApiInfo中的描述信息。
**/
private String description = "";
/**
* ApiInfo中的版本信息。
**/
private String version = "";
}

View File

@@ -0,0 +1,85 @@
package com.orange.demo.common.swagger.plugin;
import cn.hutool.core.lang.Assert;
import com.orange.demo.common.core.annotation.MyRequestBody;
import com.github.xiaoymin.knife4j.core.conf.Consts;
import javassist.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import springfox.documentation.service.ResolvedMethodParameter;
import java.util.List;
/**
* 通过字节码方式动态创建接口参数封装对象。
*
* @author Jerry
* @date 2020-09-24
*/
@Slf4j
public class ByteBuddyUtil {
private static final ClassPool CLASS_POOL = ClassPool.getDefault();
public static Class<?> createDynamicModelClass(String name, List<ResolvedMethodParameter> parameters) {
String clazzName = Consts.BASE_PACKAGE_PREFIX + name;
try {
CtClass tmp = CLASS_POOL.getCtClass(clazzName);
if (tmp != null) {
tmp.detach();
}
} catch (NotFoundException e) {
// 需要吃掉这个异常。
}
CtClass ctClass = CLASS_POOL.makeClass(clazzName);
try {
int fieldCount = 0;
for (ResolvedMethodParameter dynamicParameter : parameters) {
// 因为在调用这个方法之前这些参数都包含MyRequestBody注解。
MyRequestBody myRequestBody =
dynamicParameter.findAnnotation(MyRequestBody.class).orElse(null);
Assert.notNull(myRequestBody);
String fieldName = dynamicParameter.defaultName().isPresent()
? dynamicParameter.defaultName().get() : "parameter";
if (StringUtils.isNotBlank(myRequestBody.value())) {
fieldName = myRequestBody.value();
}
ctClass.addField(createField(dynamicParameter, fieldName, ctClass));
fieldCount++;
}
if (fieldCount > 0) {
return ctClass.toClass();
}
} catch (Throwable e) {
log.error(e.getMessage());
}
return null;
}
private static CtField createField(ResolvedMethodParameter parameter, String parameterName, CtClass ctClass)
throws NotFoundException, CannotCompileException {
CtField field = new CtField(getFieldType(parameter.getParameterType().getErasedType()), parameterName, ctClass);
field.setModifiers(Modifier.PUBLIC);
return field;
}
private static CtClass getFieldType(Class<?> propetyType) {
CtClass fieldType = null;
try {
if (!propetyType.isAssignableFrom(Void.class)) {
fieldType = CLASS_POOL.get(propetyType.getName());
} else {
fieldType = CLASS_POOL.get(String.class.getName());
}
} catch (NotFoundException e) {
// 抛异常
ClassClassPath path = new ClassClassPath(propetyType);
CLASS_POOL.insertClassPath(path);
try {
fieldType = CLASS_POOL.get(propetyType.getName());
} catch (NotFoundException e1) {
log.error(e1.getMessage(), e1);
}
}
return fieldType;
}
}

View File

@@ -0,0 +1,61 @@
package com.orange.demo.common.swagger.plugin;
import com.orange.demo.common.core.annotation.MyRequestBody;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.CaseFormat;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationModelsProviderPlugin;
import springfox.documentation.spi.service.contexts.RequestMappingContext;
import java.util.List;
import java.util.stream.Collectors;
/**
* 生成参数包装类的插件。
*
* @author Jerry
* @date 2020-09-24
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 200)
@ConditionalOnProperty(prefix = "swagger", name = "enabled")
public class DynamicBodyModelPlugin implements OperationModelsProviderPlugin {
private final TypeResolver typeResolver;
public DynamicBodyModelPlugin(TypeResolver typeResolver) {
this.typeResolver = typeResolver;
}
@Override
public void apply(RequestMappingContext context) {
List<ResolvedMethodParameter> parameterTypes = context.getParameters();
if (CollectionUtils.isEmpty(parameterTypes)) {
return;
}
List<ResolvedMethodParameter> bodyParameter = parameterTypes.stream()
.filter(p -> p.hasParameterAnnotation(MyRequestBody.class)).collect(Collectors.toList());
if (CollectionUtils.isEmpty(bodyParameter)) {
return;
}
String groupName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, context.getGroupName());
String clazzName = groupName + StringUtils.capitalize(context.getName());
Class<?> clazz = ByteBuddyUtil.createDynamicModelClass(clazzName, bodyParameter);
if (clazz != null) {
context.operationModelsBuilder().addInputParam(typeResolver.resolve(clazz));
}
}
@Override
public boolean supports(DocumentationType delimiter) {
// 支持2.0版本
return delimiter == DocumentationType.SWAGGER_2;
}
}

View File

@@ -0,0 +1,64 @@
package com.orange.demo.common.swagger.plugin;
import com.orange.demo.common.core.annotation.MyRequestBody;
import com.google.common.base.CaseFormat;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.ParameterContext;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 构建操作接口参数对象的插件。
*
* @author Jerry
* @date 2020-09-24
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 102)
@ConditionalOnProperty(prefix = "swagger", name = "enabled")
public class DynamicBodyParameterBuilder implements OperationBuilderPlugin {
@Override
public void apply(OperationContext context) {
List<ResolvedMethodParameter> methodParameters = context.getParameters();
List<Parameter> parameters = new ArrayList<>();
if (CollectionUtils.isNotEmpty(methodParameters)) {
List<ResolvedMethodParameter> bodyParameter = methodParameters.stream()
.filter(p -> p.hasParameterAnnotation(MyRequestBody.class)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(bodyParameter)) {
// 构造model
String groupName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, context.getGroupName());
String clazzName = groupName + StringUtils.capitalize(context.getName());
ResolvedMethodParameter methodParameter = bodyParameter.get(0);
ParameterContext parameterContext = new ParameterContext(methodParameter,
new ParameterBuilder(),
context.getDocumentationContext(),
context.getGenericsNamingStrategy(),
context);
Parameter parameter = parameterContext.parameterBuilder()
.parameterType("body").modelRef(new ModelRef(clazzName)).name(clazzName).build();
parameters.add(parameter);
}
}
context.operationBuilder().parameters(parameters);
}
@Override
public boolean supports(DocumentationType delimiter) {
return delimiter == DocumentationType.SWAGGER_2;
}
}

View File

@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.orange.demo.common.swagger.config.SwaggerAutoConfiguration

View File

@@ -15,5 +15,6 @@
<module>common-core</module>
<module>common-redis</module>
<module>common-sequence</module>
<module>common-swagger</module>
</modules>
</project>